Scripted objects saving attributes/fr: Difference between revisions

From FreeCAD Documentation
(Created page with "Tous les objets ont une propriété {{incode|TypeId}}, mais pour les objets créés par script, cette propriété n'est pas utile car elle fait toujour...")
(Created page with "=== Migration de l'objet ===")
Line 139: Line 139:
Tous les objets ont une propriété {{incode|TypeId}}, mais pour les [[scripted_objects/fr|objets créés par script]], cette propriété n'est pas utile car elle fait toujours référence à la classe C++ parente, par exemple, [[Part_Part2DObject/fr|{{incode|Part::Part2DObjectPython}}]] ou [[Part_Feature/fr|{{incode|Part::FeaturePython}}]]. Par conséquent, le fait d'avoir cet attribut supplémentaire {{incode|Proxy.Type}} dans la classe est utile pour traiter chaque objet d'une manière particulière.
Tous les objets ont une propriété {{incode|TypeId}}, mais pour les [[scripted_objects/fr|objets créés par script]], cette propriété n'est pas utile car elle fait toujours référence à la classe C++ parente, par exemple, [[Part_Part2DObject/fr|{{incode|Part::Part2DObjectPython}}]] ou [[Part_Feature/fr|{{incode|Part::FeaturePython}}]]. Par conséquent, le fait d'avoir cet attribut supplémentaire {{incode|Proxy.Type}} dans la classe est utile pour traiter chaque objet d'une manière particulière.


=== Migrating the object ===
=== Migration de l'objet ===


Version information can be stored inside the class in order to verify the origin of an object.
Version information can be stored inside the class in order to verify the origin of an object.

Revision as of 10:09, 5 December 2021

Other languages:

Introduction

Les Objets créés par script sont reconstruits à chaque fois qu'un document au format FCStd est ouvert. Pour ce faire, le document conserve une référence au module et à la classe Python qui ont été utilisés pour créer l'objet, ainsi que ses propriétés.

Les attributs de la classe utilisée pour créer l'objet peuvent également être enregistrés, c'est-à-dire "sérialisés". Ceci peut être contrôlé par les méthodes __getstate__ et __setstate__ de la classe.

Sauvegarde de tous les attributs

Par défaut, les attributs enregistrés dans une classe d'objets sont ceux du dictionnaire __dict__ de la classe.

# 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

Un objet peut être créé à l'aide de cette classe et il peut être enregistré dans my_document.FCstd. Si aucun viewprovider particulier n'est attribué au nouvel objet, sa classe proxy est simplement définie sur une valeur différente de None, dans ce cas, sur 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()

Lorsque nous rouvrons le fichier, nous pouvons inspecter le dictionnaire de la classe de l'objet.

>>> 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}

Nous voyons que tous les attributs qui commencent par self dans la classe ont été sauvegardés. Ceux-ci peuvent être de différents types, notamment chaîne de caractères, liste, flottant et dictionnaire. Le tuple original pour self.color a été converti en liste, mais sinon tous les attributs ont été chargés de la même manière.

Sauvegarde d'attributs spécifiques

Nous pouvons définir une classe similaire à la première, qui implémente des attributs spécifiques à sauvegarder.

# 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 __getstate__(self):
        return self.color, self.width

    def __setstate__(self, state):
        self.color = state[0]
        self.width = state[1]

La valeur de retour de __getstate__ est l'objet qui sera sérialisé. Il peut s'agir d'une valeur unique ou d'un tuple de valeurs. Lorsque l'objet est restauré, la classe appelle la méthode __setstate__, en passant la variable state avec le contenu sérialisé. Dans ce cas, state est un tuple qui est décomposé dans les variables respectives pour reconstruire l'"état" qui existait à l'origine.

state = (self.color, self.width)
state = ((0, 0, 1), 2.5)

Nous pouvons créer un objet avec cette classe et enregistrer le document, comme dans l'exemple précédent. Lorsque nous rouvrons le fichier, nous pouvons inspecter le dictionnaire de la classe de l'objet.

>>> obj2 = App.ActiveDocument.Custom2
>>> print(obj2.Proxy)
<various_states.CustomStates object at 0x7fb399496630>
>>> print(obj2.Proxy.__dict__)
{'color': [0, 0, 1], 'width': 2.5}

Le tuple original de self.color a été converti en liste, mais les autres informations ont été récupérées sans problème. Au lieu de restaurer tous les attributs comme dans le cas précédent, seuls les attributs que nous avons spécifiés dans __getstate__ et __setstate__ ont été restaurés.

Utilisation

Identifier le type

Normalement, les objets créés par script devraient utiliser des propriétés pour stocker des informations, car celles-ci sont automatiquement restaurées à l'ouverture du document.

Cependant, il arrive que la classe restitue des informations internes qui ne sont pas destinées à être modifiées, mais qu'il est utile d'inspecter.

Par exemple, la plupart des objets de l'atelier Draft configurent un attribut Type qui peut être utilisé pour déterminer le type d'objet utilisé.

class DraftObject:
    def __init__(self, obj, _type):
        self.Type = _type

    def __getstate__(self):
        return self.Type

    def __setstate__(self, state):
        if state:
            self.Type = state

Tous les objets ont une propriété TypeId, mais pour les objets créés par script, cette propriété n'est pas utile car elle fait toujours référence à la classe C++ parente, par exemple, Part::Part2DObjectPython ou Part::FeaturePython. Par conséquent, le fait d'avoir cet attribut supplémentaire Proxy.Type dans la classe est utile pour traiter chaque objet d'une manière particulière.

Migration de l'objet

Version information can be stored inside the class in order to verify the origin of an object.

class CustomObject:
    def __init__(self, obj, _type):
        self.Type = _type
        self.version = "0.18"

    def __getstate__(self):
        return self.Type, self.version

    def __setstate__(self, state):
        if state:
            self.Type = state[0]
            self.version = state[1]

If the structure of the class changes, that is, if its properties or methods change, are renamed, or are removed, we could test the version attribute in order to migrate the older object to a new set of properties or to a new class. This can be done by implementing the onDocumentRestored method, as explained in Scripted objects migration.

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):
        ...

Links