Create a FeaturePython object part I/fr

Introduction
Les objets FeaturePython (également appelés «objets scriptés») offrent aux utilisateurs la possibilité d'étendre FreeCAD avec des objets qui s'intègrent de manière transparente dans le framework FreeCAD.

Cela favorise:
 * Le prototypage rapide de nouveaux objets et outils avec des classes personnalisées de Python.
 * La sérialisation via les objets 'App::Property' sans incorporer de scripts dans le fichier document FreeCAD.
 * Une liberté créative pour adapter FreeCAD à n'importe quelle tâche!

Ce wiki vous fournira une compréhension complète de l'utilisation des objets FeaturePython et des classes Python personnalisées dans FreeCAD. Nous allons construire un exemple complet et fonctionnel d'une classe personnalisée FeaturePython, identifiant tous les principaux composants et comprendre de manieère fine dont tout cela fonctionne au fur et à mesure.

Comment ça marche?
FreeCAD est fourni avec un certain nombre de types d'objets par défaut pour gérer différents types de géométrie. Certains d'entre eux ont des alternatives 'FeaturePython' qui permettent la personnalisation de l'utilisateur avec une classe python personnalisée.

La classe personnalisée de Python prend simplement une référence à l'un de ces objets et le modifie de plusieurs façons. Par exemple, la classe Python peut ajouter des propriétés directement à l'objet, en modifiant d'autres propriétés lorsqu'il est recalculé ou en le liant à d'autres objets. De plus, la classe Python implémente certaines méthodes pour lui permettre de répondre aux événements du document, ce qui permet de piéger les changements de propriétés des objets et les recalculs du document.

Il est important de se rappeler, cependant, que pour autant que l'on puisse accomplir avec des classes personnalisées et des objets FeaturePython, quand vient le temps d'enregistrer le document, seul l'objet FeaturePython lui-même est sérialisé. La classe personnalisée et son état ne sont pas conservés entre les rechargements de documents. Cela nécessiterait d'incorporer un script dans le fichier de document FreeCAD, ce qui pose un risque de sécurité important, tout comme les risques posés par embedding VBA macros in Microsoft Office documents.

Ainsi, un objet FeaturePython existe finalement en dehors de son script. L'inconvénient posé par le fait de ne pas empaqueter le script avec l'objet dans le fichier de document est bien moindre que le risque posé par l'exécution d'un fichier incorporé avec un script inconnu. Cependant, le chemin du module de script est stocké dans le fichier de document. Par conséquent, un utilisateur n'a qu'à installer le code de classe python personnalisé en tant que module importable suivant la même structure de répertoires pour retrouver la fonctionnalité perdue.

Configuration de votre environnement de développement
Pour commencer, les classes d'objets FeaturePython doivent agir comme des modules importables dans FreeCAD. Cela signifie que vous devez les placer dans un chemin qui existe dans votre environnement Python (ou l'ajouter spécifiquement). Pour les besoins de ce tutoriel, nous allons utiliser le dossier Macro utilisateur FreeCAD, mais si vous avez une autre idée en tête, n'hésitez pas à l'utiliser à la place!

Si vous ne savez pas où se trouve le dossier Macro FreeCAD, saisissez 'FreeCAD.getUserMacroDir(True)' dans la console Python de FreeCAD. L'endroit est configurable mais, par défaut, pour s'y rendre:
 * Windows: tapez '%APPDATA%/FreeCAD/Macro' dans la barre de chemin de fichier en haut de l'Explorateur
 * Linux: accédez à /home/USERNAME/.FreeCAD/Macro
 * Mac: accédez à /Users/USERNAME/Library/Preferences/FreeCAD/Macro

Maintenant, nous devons créer des fichiers.
 * Dans le dossier Macro, créez un nouveau dossier appelé fpo.
 * Dans le dossier fpo, créez un fichier vide: __init__.py.
 * Dans le dossier fpo, créez un nouveau dossier appelé box.
 * Dans le dossier de la boîte, créez deux fichiers: __init__.py et box.py (laissez les deux vides pour l'instant)

Remarques:
 * Le dossier fpo fournit un endroit propice pour jouer avec les nouveaux objets FeaturePython et le dossier box est le module dans lequel nous travaillerons.
 * '__init__.py indique à Python que dans le dossier se trouve un module importable et box.py sera le fichier de classe pour notre nouvel objet FeaturePython.

La structure de votre répertoire devrait ressembler à ceci:

.FreeCAD |--> Macro |--> fpo |--> __init__.py            |--> box |--> __init__.py                 |--> box.py

Avec nos chemins de modules et fichiers créés, assurez-vous que FreeCAD est correctement configuré:


 * Démarrez FreeCAD (s'il n'est pas déjà ouvert)
 * Activer la console Python et les vues de rapport (Affichage -> Panneaux -> Vue rapport et console Python) (FreeCAD Débuter avec les scripts)
 * Dans votre éditeur de code préféré, accédez au dossier /Macro/fpo/box et ouvrez box.py

Il est temps d'écrire du code!

-

Un objet FeaturePython très basique
Commençons par écrire notre classe et son constructeur:

class box: def __init__(self, obj): """         Constructor          Arguments          -          - obj: a variable created with FreeCAD.Document.addObject('App::FeaturePython', '{name}').          """ self.Type = 'box' obj.Proxy = self

La méthode breakdown 

Dans le fichier box.py en haut, ajoutez le code suivant:

import FreeCAD as App def create(obj_name): """     Object creation method      """ obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name) fpo = box(obj) return obj

La méthode breakdown 

La méthode  n'est pas requise mais elle fournit un bon moyen d'encapsuler le code de création d'objet.

-

Test du code
Maintenant, nous pouvons essayer notre nouvel objet. Enregistrez votre code et revenez à FreeCAD, assurez-vous d'avoir ouvert un nouveau document. Vous pouvez le faire en appuyant sur CTRL+n ou en sélectionnant Fichier -> Nouveau

Dans la console Python, tapez ce qui suit:

>>> from fpo.box import box

Maintenant, nous devons créer notre objet:

>>> box.create('my_box')

Vous devriez voir un nouvel objet apparaître dans l'arborescence en haut à gauche intitulé my_box. Notez que l'icône est grise. FreeCAD nous dit simplement que l'objet n'est pas en mesure d'afficher quoi que ce soit dans la vue 3D... pour l'instant. Cliquez sur l'objet et notez ce qui apparaît dans le panneau de propriétés en dessous. Il n'y a pas grand-chose juste le nom de l'objet. Nous devrons ajouter quelques propriétés sous peu. Rendons également le référencement de notre nouvel objet un peu plus pratique:

>>> mybox = App.ActiveDocument.my_box

Et puis nous devrions jeter un oeil aux attributs de notre objet:

>>> dir(mybox) ['Content', 'Document', 'ExpressionEngine', 'InList', 'InListRecursive', 'Label', 'MemSize', 'Module', 'Name', 'OutList', 'OutListRecursive', 'PropertiesList', 'Proxy', 'State', 'TypeId', 'ViewObject', '__class__', ... 'setEditorMode', 'setExpression', 'supportedProperties', 'touch']

Il y a beaucoup d'attributs parce que nous accédons à l'objet FreeCAD FeaturePyton natif que nous avons créé dans la première ligne de notre  méthode. La propriété  que nous avons ajoutée dans notre méthode   est également là.

Inspectons cela en appelant le sur l'objet Proxy:

>>> dir(mybox.Proxy) ['Object', 'Type', '__class__', '__delattr__', '__dict__', '__dir__', ... '__str__', '__subclasshook__', '__weakref__']

Une fois que nous avons inspecté la propriété Proxy, nous pouvons voir notre  et   propriétés. Cela signifie que nous accédons à l'objet Python personnalisé défini dans box.py.

Appelez la propriété  et regardez le résultat: >>> mybox.Proxy.Type 'box'

Bien sûr, il retourne la valeur que nous avons assignée, donc nous savons que nous accédons à la classe personnalisée elle-même par le biais de l'objet FeaturePython.

De même, nous pouvons accéder à l'objet FreeCAD (pas à notre objet Python) en utilisant le méthode: >>> mybox.Proxy.Object

C'était amusant! Mais voyons maintenant si nous pouvons rendre notre classe un peu plus intéressante ... et peut-être plus utile.

-

Ajout de propriétés
Les propriétés sont l'élément vital d'une classe FeaturePython. Heureusement, FreeCAD prend en charge un certain nombre de types de propriétés personnalisées de FeaturePython pour les classes FeaturePython. Ces propriétés sont attachées directement à l'objet FeaturePython lui-même et entièrement sérialisées lorsque le fichier est enregistré. Cela signifie que, sauf si vous souhaitez sérialiser les données vous-même, vous devrez trouver un moyen de les ancrer dans un type de propriété pris en charge. L'ajout de propriétés se fait tout simplement en utilisant le  méthode. La syntaxe de la méthode est:

add_property(type, name, section, description)

Essayons d'ajouter une propriété à notre classe box. Basculez vers votre éditeur de code et passez à la  méthode.

Ensuite, à la fin de la méthode, ajoutez:

obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = "" Notez comment nous utilisons la référence à l'objet FeaturePython (sérialisable),  et non l'instance de classe Python (non sérialisable),  Quoi qu'il en soit, une fois que vous avez terminé, enregistrez les modifications et revenez à FreeCAD. Avant de pouvoir observer les modifications que nous avons apportées à notre code, nous devons recharger le module. Cela peut être accompli en redémarrant FreeCAD, mais le redémarrage de FreeCAD chaque fois que nous apportons une modification au code de classe python peut devenir un peu gênant. Pour le rendre plus facile, essayez ce qui suit dans la console Python:

>>> from importlib import reload >>> reload(box) Cela rechargera le module box, incorporant les modifications que vous avez apportées au fichier box.py comme si vous aviez redémarré FreeCAD. Le module étant rechargé, voyons maintenant ce que nous obtenons lorsque nous créons un objet:

>>> box.create('box_property_test') Vous devriez voir le nouvel objet boîte apparaître dans l'arborescence à gauche. Mais avant de quitter le sujet des propriétés pour le moment, revenons en arrière et ajoutons quelques propriétés qui rendraient un objet boîte personnalisé *vraiment* utile: à savoir length, width et height. Revenez à votre code source et ajoutez les propriétés suivantes à :
 * Sélectionnez-le et regardez le panneau des propriétés. Là, vous devriez voir la propriété 'Description'.
 * Passez la souris sur le nom de la propriété à gauche et voyez l'infobulle apparaître avec le texte de description que vous avez fourni.
 * Sélectionnez le champ et saisissez ce que vous voulez. Vous remarquerez que les commandes de mise à jour Python sont exécutées et affichées dans la console lorsque vous tapez des lettres et que la propriété change.

obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0 obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm' obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'

Une dernière chose: avez-vous remarqué comment la coche bleue apparaît à côté de l'objet FeaturePython dans l'arborescence à gauche? En effet, lorsqu'un objet est créé ou modifié, il est "touché" et doit être recalculé. Cliquer sur les flèches "recycler" (les deux flèches formant un cercle) accomplira cela. Mais nous pouvons accomplir cela automatiquement en ajoutant la ligne suivante à la fin du  méthode:

App.ActiveDocument.recompute Maintenant, testez vos modifications comme suit: Une fois la boîte créée (et vous avez vérifié qu'elle a bien été recalculée!), sélectionnez l'objet et examinez vos propriétés. Vous devriez noter deux choses: Notez également comment les propriétés ont des dimensions. Plus précisément, ils prennent la dimension linéaire des unités définies dans les préférences de l'utilisateur (voir Modifier -> Préférence ... -> Onglet Unités). En fait, si vous faites attention lors de la saisie du code, vous aurez remarqué que trois valeurs distinctes ont été saisies pour chaque dimension. La longueur était une valeur à virgule flottante (10.0), la largeur était une chaîne, spécifiant les millimètres ('10 mm') et la hauteur était une chaîne spécifiant les centimètres ('1 cm'). Pourtant, la propriété a rendu les trois valeurs de la même manière: 10 mm. Plus précisément, une valeur à virgule flottante est supposée être dans les unités de document actuelles, et les valeurs de chaîne sont analysées selon les unités spécifiées, puis converties en unités de document. Ce qui est bien avec le type , c'est qu'il s'agit d'un type 'unit'. Les valeurs sont comprises comme ayant des unités spécifiques. Par conséquent, chaque fois que vous créez une propriété qui utilise des dimensions linéaires, utilisez  comme type de propriété.
 * Enregistrez vos modifications et revenez à FreeCAD.
 * Supprimez tous les objets existants et rechargez votre module.
 * Enfin, créez un autre objet boîte à partir de la ligne de commande en appelant fpo_box_properties.png
 * Trois nouvelles propriétés (length, width, and height)
 * Un nouveau groupe de propriétés,Dimensions.

-

Piège à événements
Le dernier élément requis pour un objet FeaturePython de base est le piégeage des événements. Plus précisément, nous devons piéger l'événement, qui est appelé lorsque l'objet est recalculé. Il y a plusieurs autres événements au niveau du document qui peuvent être piégés dans notre objet, à la fois dans l'objet FeaturePython lui-même et dans le ViewProvider, que nous couvrirons dans une autre section. Ajoutez ce qui suit après la fonction :

def execute(self, obj): """    Called on document recompute     """ print('Recomputing {0:s} ({1:s})'.format(obj.Name, self.Type))

Testez le code comme suit:
 * Enregistrez les modifications et rechargez le module box dans la console python FreeCAD.
 * Supprimer tous les objets dans l'arborescence.
 * Recréez l'objet boîte.

Vous devriez voir le résultat dans la console Python, grâce à l'appel  que nous avons ajouté à la méthode.

Bien sûr, la méthode  ne fait rien ici (sauf nous dire qu'elle a été appelée), mais c'est la clé de la magie des objets FeaturePython.

Et voilà! Vous savez maintenant comment construire un objet FeaturePython basique et fonctionnel!

-

Le code terminé
import FreeCAD as App def create(obj_name): """   Object creation method    """ obj = App.ActiveDocument.addObject('App::FeaturePython', obj_name))   fpo = box(obj)    return obj class box:    def __init__(self, obj):        """        Default Constructor        """        self.Type = 'box'       obj.addProperty('App::PropertyString', 'Description', 'Base', 'Box description').Description = ""       obj.addProperty('App::PropertyLength', 'Length', 'Dimensions', 'Box length').Length = 10.0       obj.addProperty('App::PropertyLength', 'Width', 'Dimensions', 'Box width').Width = '10 mm'       obj.addProperty('App::PropertyLength', 'Height', 'Dimensions', 'Box height').Height = '1 cm'        obj.Proxy = self    def execute(self, obj):        """        Called on document recompute        """        print('Recomputing {0:s} {1:s}'.format(obj.Name, self.Type))