Scripted objects saving attributes/de

Einleitung

Geskriptete Objekte werden jedes Mal neu aufgebaut, wenn ein FCStd Dokument geöffnet wird. Zu diesem Zweck behält das Dokument eine Referenz auf das Modul und die Python Klasse, die zur Erstellung des Objekts verwendet wurden, zusammen mit seinen Eigenschaften.

Attribute der Klasse, die zur Erstellung des Objekts verwendet wurde, können ebenfalls gespeichert, d.h. "serialisiert" werden. Dies kann weiter durch die Methoden dumps und loads der Klasse gesteuert werden.

Speichern aller Attribute

Standardmäßig werden in einer Objektklasse die Attribute aus dem __dict__ Wörterbuch der Klasse gespeichert.

# 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.Type = "Custom"
        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 mein_Dokument.FCstd gespeichert werden. Wenn dem neuen Objekt kein bestimmter Ansichtsanbieter 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, die in der Klasse mit self beginnen, gespeichert wurden. Diese können von unterschiedlichem Typ sein, einschließlich Zeichenfolge, Liste, Fließkommazahl und Wörterbuch. Das ursprüngliche Tupel für self.color wurde in eine Liste konvertiert, aber ansonsten wurden alle Attribute gleich geladen.

Speichern besonderer Attribute

Wir können eine Klasse ähnlich der ersten definieren, die bestimmte Attribute zum Speichern 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 Type-Attribut 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 TypeId-Eigenschaft, aber für skriptgenerierte Objekte ist diese Eigenschaft nicht nützlich, da sie sich immer auf die übergeordnete C++-Klasse bezieht, zum Beispiel Part::Part2DObjectPython oder Part::FeaturePython verweist. Daher ist es sinnvoll, dieses zusätzliche Attribut Proxy.Type in der Klasse zu haben, um jedes Objekt auf eine bestimmte Weise zu behandeln.

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