Geskriptete Objekte die Attribute speichern
Einleitung
Skriptgenerierte Objekte werden jedes Mal neu aufgebaut, wenn ein FCStd-Dokument geöffnet wird. Zu diesem Zweck behält das Dokument einen Bezug zu dem Modul und der Python-Klasse, die zur Erstellung des Objekts verwendet wurden, zusammen mit seinen Eigenschaften.
Attribute der Klasse, die zur Erstellung des Objekts verwendet wurden, können ebenfalls gespeichert werden. Dies kann darüber hinaus durch die Methoden dumps und loads der Klasse gesteuert werden.
Alle Attribute speichern
Standardmäßig werden die Attribute in einer Objektklasse gesichert, die sich in dem Wörterbuch __dict__ der Klasse befinden.
# various_states.py
class VariousStates:
def __init__(self, obj):
obj.addProperty("App::PropertyLength", "Length")
obj.addProperty("App::PropertyArea", "Area")
obj.Length = 15
obj.Area = 300
obj.Proxy = self
Type = dict()
Type["Version"] = "Custom"
Type["Release"] = "production"
self.Type = Type
self.ver = "0.18"
self.color = (0, 0, 1)
self.width = 2.5
def execute(self, obj):
pass
Mit dieser Klasse kann ein Objekt erstellt und unter my_document.FCstd gespeichert werden. Wenn dem neuen Objekt kein bestimmter Viewprovider zugewiesen ist, wird seine Proxy-Klasse einfach auf einen anderen Wert als None gesetzt, in diesem Fall auf 1.
import FreeCAD as App
import various_states
doc = App.newDocument()
doc.FileName = "my_document.FCStd"
obj = doc.addObject("Part::FeaturePython", "Custom")
various_states.VariousStates(obj)
if App.GuiUp:
obj.ViewObject.Proxy = 1
doc.recompute()
doc.save()
Wenn wir die Datei erneut öffnen, können wir das Wörterbuch der Klasse des Objekts einsehen.
>>> obj = App.ActiveDocument.Custom
>>> print(obj.Proxy)
<various_states.VariousStates object at 0x7f0a899bde10>
>>> print(obj.Proxy.__dict__)
{'Type': {'Version': 'Custom', 'Release': 'production'}, 'ver': '0.18', 'color': [0, 0, 1], 'width': 2.5}
Wir sehen, dass alle Attribute der Klasse, die mit self beginnen, gespeichert wurden. Diese können unterschiedlicher Art sein, einschließlich Zeichenfolge, Liste, Fließkommazahl und Wörterbuch. Das ursprüngliche Tupel für self.color wurde in eine Liste umgewandelt, aber ansonsten sind alle geladenen Attribute dieselben.
Bestimmte Attribute speichern
Wir können eine Klasse ähnlich der ersten definieren, die bestimmte zu speichernde Attribute implementiert.
# various_states.py
class CustomStates:
def __init__(self, obj):
obj.addProperty("App::PropertyLength", "Length")
obj.addProperty("App::PropertyArea", "Area")
obj.Length = 15
obj.Area = 300
obj.Proxy = self
Type = dict()
Type["Version"] = "Custom"
Type["Release"] = "production"
self.Type = Type
self.ver = "0.18"
self.color = (0, 0, 1)
self.width = 2.5
def execute(self, obj):
pass
def dumps(self):
return self.color, self.width
def loads(self, state):
self.color = state[0]
self.width = state[1]
Der Rückgabewert von dumps ist das Objekt, das serialisiert wird. Dies kann ein einzelner Wert oder ein Tupel von Werten sein. Wenn das Objekt wiederhergestellt wird, ruft die Klasse die Methode loads auf und übergibt dabei die Variable state mit dem serialisierten Inhalt. In diesem Fall ist state ein Tupel, das in die entsprechenden Variablen entpackt wird, um den ursprünglich vorhandenen Zustand wiederherzustellen.
state = (self.color, self.width)
state = ((0, 0, 1), 2.5)
Wir können mit dieser Klasse ein Objekt erstellen und das Dokument speichern, genau wie im vorherigen Beispiel. Wenn wir die Datei erneut öffnen, können wir das Wörterbuch der Klasse des Objekts überprüfen.
>>> obj2 = App.ActiveDocument.Custom2
>>> print(obj2.Proxy)
<various_states.CustomStates object at 0x7fb399496630>
>>> print(obj2.Proxy.__dict__)
{'color': [0, 0, 1], 'width': 2.5}
Das ursprüngliche Tupel für self.color wurde in eine Liste konvertiert, aber ansonsten wurden die Informationen einwandfrei wiederhergestellt. Anstatt wie im vorherigen Fall alle Attribute wiederherzustellen, wurden nur die Attribute wiederhergestellt, die wir in dumps und loads angegeben hatten.
Anwendung
Identifizieren des Typs
Normalerweise sollten skriptgenerierte Objekte Eigenschaften zum Speichern von Informationen verwenden, da diese beim Öffnen des Dokuments automatisch wiederhergestellt werden.
Manchmal stellt die Klasse jedoch interne Informationen wieder her, die nicht geändert werden sollen, deren Überprüfung jedoch hilfreich ist.
Beispielsweise richten die meisten Objekte des Arbeitsbereichs Draft ein Attribut Type ein, mit dem der Typ des verwendeten Objekts bestimmt werden kann.
class DraftObject:
def __init__(self, obj, _type):
self.Type = _type
def dumps(self):
return self.Type
def loads(self, state):
if state:
self.Type = state
Alle Objekte haben eine Eigenschaft TypeId, aber für skriptgenerierte Objekte ist diese Eigenschaft nicht nützlich, da sie sich immer auf die übergeordnete C++-Klasse bezieht, zum Beispiel auf Part::Part2DObjectPython oder Part::FeaturePython. Daher ist es sinnvoll, dieses zusätzliche Attribut Proxy.Type in der Klasse zu haben, um jedes Objekt auf eine bestimmte Weise zu bearbeiten.
Das Objekt migrieren
Versionsinformationen können innerhalb der Klasse gespeichert werden, um die Herkunft eines Objekts zu überprüfen.
class CustomObject:
def __init__(self, obj, _type):
self.Type = _type
self.version = "0.18"
def dumps(self):
return self.Type, self.version
def loads(self, state):
if state:
self.Type = state[0]
self.version = state[1]
Wenn sich die Struktur der Klasse ändert, d. h. wenn ihre Eigenschaften oder Methoden geändert, umbenannt oder entfernt werden, könnten wir das Versionsattribut testen, um das ältere Objekt auf einen neuen Satz von Eigenschaften oder eine neue Klasse zu migrieren. Dies kann durch Implementierung der Methode onDocumentRestored erfolgen, wie in Migration skriptgesteuerter Objekte erläutert.
class CustomObject:
def onDocumentRestored(self, obj):
if hasattr(obj.Proxy, "version") and obj.Proxy.version:
if obj.Proxy.version == "0.18":
self.migrate_from_018(obj)
def migrate_from_018(self, obj):
...
Verweise
- Skriptgenerierte Objekte
- Migration skriptgenerierter Objekte
- FreeCAD Forums-Diskussion: Skriptbasierte Objektserialisierung: json oder pickle?
- obj.Proxy.Type ist ein Dict, kein String, Erklärung von
dumpsundloadsim Forum. - Das Pickle-Modul in der Python-Dokumentation.
- FreeCAD scripting: Python, Introduction to Python, Python scripting tutorial, FreeCAD Scripting Basics
- Modules: Builtin modules, Units, Quantity
- Workbenches: Workbench creation, Gui Commands, Commands, Installing more workbenches
- Meshes and Parts: Mesh Scripting, Topological data scripting, Mesh to Part, PythonOCC
- Parametric objects: Scripted objects, Viewproviders (Custom icon in tree view)
- Scenegraph: Coin (Inventor) scenegraph, Pivy
- Graphical interface: Interface creation, Interface creation completely in Python (1, 2, 3, 4, 5), PySide, PySide examples beginner, intermediate, advanced
- Macros: Macros, How to install macros
- Embedding: Embedding FreeCAD, Embedding FreeCADGui
- Other: Expressions, Code snippets, Line drawing function, FreeCAD vector math library (deprecated)
- Hubs: User hub, Power users hub, Developer hub