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.
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.
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.
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.
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):
...
dumps und loads im Forum.