Scripted objects saving attributes/de

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