Scripted objects/fr

Introduction
Outre les types d'objets standard tels que les annotations, les maillages et les objets de pièces, FreeCAD offre également l'étonnante possibilité de construire des objets paramétrique 100% Python, appelés Python Features. Ces objets se comporteront exactement comme tout autre objet FreeCAD, et seront enregistrés et restaurés automatiquement lors de la sauvegarde/du chargement du fichier.

Une particularité doit être comprise : pour des raisons de sécurité, les fichiers FreeCAD ne portent jamais de code embarqué. Le code Python que vous écrivez pour créer des objets paramétriques n'est jamais enregistré dans un fichier. Cela signifie que si vous ouvrez un fichier contenant un tel objet sur une autre machine, si ce code Python n'est pas disponible sur cette machine, l'objet ne sera pas entièrement recréé. Si vous distribuez de tels objets à d'autres, vous devrez également distribuer votre script Python, par exemple sous forme de Macro.

Remarque : Il est possible d'empaqueter du code Python dans un fichier FreeCAD en utilisant la sérialisation json avec un App::PropertyPythonObject, mais ce code ne peut jamais être exécuté directement et a donc peu d'utilité pour notre propos ici.

Les Python Features suivent la même règle que toutes les fonctionnalités FreeCAD : elles sont séparées en plusieurs parties App (application) et GUI (interface graphique). La partie applicative, l'objet document, définit la géométrie de notre objet, tandis que sa partie graphique, l'objet fournisseur de vues, définit la façon dont l'objet sera affiché à l'écran. L'outil View Provider Object (créateur de vue), comme toute autre fonctionnalité de FreeCAD, n'est disponible que lorsque vous exécutez FreeCAD dans sa propre interface graphique. Plusieurs propriétés et méthodes sont disponibles pour construire votre objet. Les propriétés doivent être de l'un des types de propriétés prédéfinis offerts par FreeCAD et apparaîtront dans la fenêtre de visualisation des propriétés, de sorte qu'elles puissent être modifiées par l'utilisateur. De cette façon, les objets FeaturePython sont véritablement et totalement paramétriques. Vous pouvez définir les propriétés de l'objet et de l'affichage ViewObject de l'objet séparément.



Exemples de base
L'exemple suivant peut être trouvé dans le fichier src/Mod/TemplatePyMod/FeaturePython.py, avec beaucoup d'autres exemples:



Choses à noter
Si votre objet doit être recalculé dès sa création, vous devez le faire manuellement dans la fonction car il n'est pas appelé automatiquement. Cet exemple n'en a pas besoin car la méthode de la classe  a le même effet que la fonction, mais les exemples ci-dessous reposent sur le fait d'être recalculés avant que quoi que ce soit ne soit affiché dans la vue 3D. Dans les exemples, cela est fait manuellement avec mais dans des scénarios plus complexes, vous devez décider où recalculer soit le document entier, soit l'objet FeaturePython.

Cet exemple produit un certain nombre de traces de la pile d'exception dans la fenêtre de visualisation du rapport. En effet, la méthode de la classe  est appelée chaque fois qu'une propriété est ajoutée dans. Lorsque la première est ajoutée, les propriétés Width et Height n'existent pas encore et la tentative d'y accéder échoue donc.

Une explication de et  se trouve dans le fil de discussion du forum obj.Proxy.Type is a dict, not a string.



Méthodes disponibles
Voir Méthodes FeaturePython pour la référence complète.



Propriétés disponibles
Les propriétés sont les bases des FeaturePython objets. Grâce à elles, l'utilisateur est en mesure d'interagir et de modifier son objet. Après avoir créé un nouveau FeaturePython dans votre document ( obj=FreeCAD.ActiveDocument.addObject("App::FeaturePython","Box") ), ses propriétés sont directement accessibles, vous pouvez obtenir la liste,

en faisant:

Vous obtiendrez une liste des propriétés disponibles, décrites plus en détail sur la page Propriétés personnalisées de FeaturePython :


 * App::PropertyAcceleration
 * App::PropertyAngle
 * App::PropertyArea
 * App::PropertyBool
 * App::PropertyBoolList
 * App::PropertyColor
 * App::PropertyColorList
 * App::PropertyDirection
 * App::PropertyDistance
 * App::PropertyEnumeration
 * App::PropertyExpressionEngine
 * App::PropertyFile
 * App::PropertyFileIncluded
 * App::PropertyFloat
 * App::PropertyFloatConstraint
 * App::PropertyFloatList
 * App::PropertyFont
 * App::PropertyForce
 * App::PropertyFrequency
 * App::PropertyInteger
 * App::PropertyIntegerConstraint
 * App::PropertyIntegerList
 * App::PropertyIntegerSet
 * App::PropertyLength
 * App::PropertyLink
 * App::PropertyLinkChild
 * App::PropertyLinkGlobal
 * App::PropertyLinkHidden
 * App::PropertyLinkList
 * App::PropertyLinkListChild
 * App::PropertyLinkListGlobal
 * App::PropertyLinkListHidden
 * App::PropertyLinkSub
 * App::PropertyLinkSubChild
 * App::PropertyLinkSubGlobal
 * App::PropertyLinkSubHidden
 * App::PropertyLinkSubList
 * App::PropertyLinkSubListChild
 * App::PropertyLinkSubListGlobal
 * App::PropertyLinkSubListHidden
 * App::PropertyMap
 * App::PropertyMaterial
 * App::PropertyMaterialList
 * App::PropertyMatrix
 * App::PropertyPath
 * App::PropertyPercent
 * App::PropertyPersistentObject
 * App::PropertyPlacement
 * App::PropertyPlacementLink
 * App::PropertyPlacementList
 * App::PropertyPosition
 * App::PropertyPrecision
 * App::PropertyPressure
 * App::PropertyPythonObject
 * App::PropertyQuantity
 * App::PropertyQuantityConstraint
 * App::PropertySpeed
 * App::PropertyString
 * App::PropertyStringList
 * App::PropertyUUID
 * App::PropertyVacuumPermittivity
 * App::PropertyVector
 * App::PropertyVectorDistance
 * App::PropertyVectorList
 * App::PropertyVolume
 * App::PropertyXLink
 * App::PropertyXLinkList
 * App::PropertyXLinkSub
 * App::PropertyXLinkSubList
 * Mesh::PropertyCurvatureList
 * Mesh::PropertyMeshKernel
 * Mesh::PropertyNormalList
 * Part::PropertyFilletEdges
 * Part::PropertyGeometryList
 * Part::PropertyPartShape
 * Part::PropertyShapeHistory
 * Path::PropertyPath
 * Path::PropertyTool
 * Path::PropertyTooltable
 * Sketcher::PropertyConstraintList
 * Spreadsheet::PropertyColumnWidths
 * Spreadsheet::PropertyRowHeights
 * Spreadsheet::PropertySheet
 * Spreadsheet::PropertySpreadsheetQuantity
 * TechDraw::PropertyCenterLineList
 * TechDraw::PropertyCosmeticEdgeList
 * TechDraw::PropertyCosmeticVertexList
 * TechDraw::PropertyGeomFormatList

Lors de l'ajout de propriétés à vos objets, prenez soin de ceci :
 * Ne pas utiliser de caractères "<" ou ">" dans les descriptions des propriétés (qui coupent des portions de code dans le fichier xml.Fcstd)
 * Les propriétés sont stockées dans un fichier texte .Fcstd.
 * Toutes les propriétés dont le nom vient après "Shape" sont triés dans l'ordre alphabétique, donc, si vous avez une forme dans vos propriétés, et comme les propriétés sont chargées après la forme, il peut y avoir des comportements inattendus!

Une liste complète des attributs de propriété est disponible dans le fichier d’en-tête PropertyStandard C++. Par exemple, si vous souhaitez autoriser l'utilisateur à saisir uniquement une plage de valeurs limitée (par exemple, à l'aide de PropertyIntegerConstraint), vous affecterez à Python un tuple contenant non seulement la valeur de la propriété, mais également les limites inférieure et supérieure, ainsi que l'incrément, comme ci-dessous :



Type de propriété
Par défaut, les propriétés peuvent être actualisées. Il est possible de rendre les propriétés en lecture seule, par exemple dans le cas ou l'on veut montrer le résultat d'une méthode. Il est également possible de cacher la propriété. Le type de propriété peut être définie à l'aide :

Mode est un integer court qui peut avoir la valeur: 0 -- mode par défaut, lecture et écriture 1 -- lecture seule 2 -- caché

Les EditorModes ne sont pas fixés dans le fichier reload de FreeCAD. Cela pourrait être fait par la fonction __setstate__. Voir http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=10#p108072 En utilisant les propriétés de setEditorMode vous ne savez que lire dans PropertyEditor. Les propriétés pourraient encore être modifiées à partir d'une commande Python. Pour faire une lecture seul le réglage doit être transmis directement à la fonction d'ajout de propriété. Voir le topic http://forum.freecadweb.org/viewtopic.php?f=18&t=13460&start=20#p109709 pour voir un exemple.

En utilisant le paramètre direct dans la fonction addProperty, vous avez également plus de possibilités. En particulier, un point intéressant est de marquer une propriété en tant que propriété en sortie. De cette façon, FreeCAD ne marquera pas la fonctionnalité comme étant touchée lors de la modification (inutile donc de recalculer).

Exemple de sortie de property (see also https://forum.freecadweb.org/viewtopic.php?t=24928) :

Les types de propriétés pouvant être définis au dernier paramètre de la fonction addProperty sont les suivants: 0 - Prop_None, pas de type de propriété spécial 1 - Prop_ReadOnly, la propriété est en lecture seule dans l'éditeur 2 - Prop_Transient, la propriété ne sera pas sauvegardée dans un fichier 4 - Prop_Hidden, la propriété n'apparaîtra pas dans l'éditeur 8 - Prop_Output, modifier la propriété ne touche pas son conteneur parent 16 - Prop_NoRecompute, modifier la propriété ne touche pas son conteneur pour le recalcul

Vous pouvez trouver ces différents types de propriétés définis dans source code C++ header for PropertyContainer.



Autres exemples plus complexes
Cet exemple utilise l'Atelier Part pour créer un octaèdre, puis crée sa représentation coin avec pivy

En premier, c'est l'objet document lui-même:

Puis, nous avons view provider object, qui est responsable d'afficher l'objet dans la scène 3D (votre projet à l'écran) :

Enfin, une fois que notre objet et son viewobject sont définis, nous n'avons plus qu'a les appeler (la classe Octahedron et le code de la classe viewprovider peuvent être copiés directement dans la console python FreeCAD) :



Création d'objets sélectionnables
Si vous souhaitez rendre votre objet sélectionnable, ou au moins une partie de celui-ci, en cliquant dessus dans la fenêtre, vous devez inclure sa géométrie de pièce dans un nœud SoFCSelection. Si votre objet a une représentation complexe, avec des widgets, des annotations, etc..., vous souhaiterez peut-être n'en inclure qu'une partie dans une SoFCSelection. Tout ce qui est un SoFCSelection est constamment analysé par FreeCAD pour détecter la sélection/présélection, il est donc logique de ne pas le surcharger avec un balayage inutile.

Une fois que les parties du scénario qui doivent être sélectionnables se trouvent à l'intérieur des nœuds SoFCSelection, vous devez alors fournir deux méthodes pour gérer le chemin de sélection. Le chemin de sélection peut prendre la forme d'une chaîne donnant les noms de chaque élément du chemin ou d'un tableau d'objets scénographiques. Les deux méthodes que vous fournissez sont qui convertit un chemin de chaîne en un tableau d'objets de scénario, et  qui prend un élément sur lequel on a cliqué dans le scénario et renvoie son nom de chaîne (notez, pas son chemin de chaîne).

Voici l'exemple de molécule ci-dessus, adapté pour rendre les éléments de la molécule sélectionnables :



Travailler avec des formes simples
Si votre objet paramétrique renvoie simplement une forme, vous n'avez pas besoin d'utiliser un objet créateur de vue (view provider object). La forme sera affichée à l'aide du module standard de représentation des formes de FreeCAD :

Même code en utilisant ViewProviderLine



Structure du scénogramme
Vous avez peut-être remarqué que les exemples ci-dessus construisent leurs scénarios de manière légèrement différente. Certains utilisent tandis que d'autres utilisent.

Chaque fonctionnalité d'un document FreeCAD est basée sur la structure du scénogramme suivant :

n'affiche qu'un seul de ses enfants selon le mode d'affichage sélectionné dans FreeCAD.

Les exemples qui utilisent construisent leurs scénogrammes uniquement à partir d'éléments de scénogrammes coin3d. Sous le capot, ajoute un nouvel enfant à. Le nom de ce nœud correspondra au mode d'affichage auquel il a été transmis.

Les exemples qui utilisent construisent également une partie de leur géométrie à l'aide des fonctions de l'atelier Part, telles que. Cela construit les différents scénogrammes de mode d'affichage sous le. Lorsque nous arriverons plus tard à ajouter des éléments coin3d au scénogramme, nous devrons les ajouter aux scénogrammes existants en mode d'affichage en utilisant plutôt que de créer un nouvel enfant de.

Lorsque vous utilisez pour ajouter une géométrie au graphe de scène, chaque mode d'affichage doit avoir son propre nœud qui est transmis à. Ne réutilisez pas le même nœud pour cela. Cela entraînerait une confusion dans le mécanisme de sélection. C'est correct si chaque nœud du mode d'affichage a les mêmes nœuds de géométrie ajoutés en dessous, juste la racine de chaque mode d'affichage doit être distincte.

Exemple de molécule ci-dessus, adapté pour être dessiné uniquement avec des objets scénégraphiques Coin3D au lieu d'utiliser des objets de l'atelier Part :



Objets scriptés dans Part Design
Lors de la création d'objets scriptés dans Part Design, le processus est similaire à celui des objets scriptés abordés ci-dessus, mais avec quelques considérations supplémentaires. Nous devons gérer deux propriétés de forme, l'une pour la forme que nous voyons dans la vue 3D et l'autre pour la forme utilisée par les outils de patronage, comme les caractéristiques du motif polaire. Les formes de l'objet doivent également être fusionnées à tout matériau existant déjà dans le corps (ou découpées dans le cas de caractéristiques soustractives). Et nous devons tenir compte de l'emplacement et de la fixation de nos objets de manière un peu différente.

Les caractéristiques d'objet solide écrites dans Part Design doivent être basées sur PartDesign::FeaturePython, PartDesign::FeatureAdditivePython ou PartDesign::FeatureSubtractivePython plutôt que sur Part::FeaturePython. Seules les variantes additives et soustractives peuvent être utilisées dans les caractéristiques de motifs, et si elles sont basées sur Part::FeaturePython, lorsque l'utilisateur dépose l'objet dans un corps Part Design, il devient une BaseFeature au lieu d'être traité par le corps comme un objet Part Design natif. Remarque : toutes ces caractéristiques sont censées être des solides, donc si vous créez une caractéristique non solide, elle doit être basée sur Part::FeaturePython, sinon la caractéristique suivante dans l'arbre tentera de fusionner avec un solide et échouera.

Voici un exemple simple de création d'une primitive Tube, similaire à la primitive Tube dans l'atelier Part sauf que celle-ci sera un objet solide Part Design. Pour cela, nous utiliserons deux fichiers distincts : pdtube.FCMacro et pdtube.py. Le fichier .FCMacro sera exécuté par l'utilisateur pour créer l'objet. Le fichier .py contiendra les définitions des classes, importées par le fichier .FCMacro. La raison pour laquelle nous procédons de cette manière est de maintenir la nature paramétrique de l'objet après avoir redémarré FreeCAD et ouvert un document contenant l'un de nos Tubes.

Tout d'abord, le fichier de définition de la classe :

Et maintenant le fichier macro pour créer l'objet :



Plus d'informations
Pages supplémentaires :
 * Objets créés par script enregistrant des attributs
 * Scripted objects migration
 * Objets créés par script avec pièce jointe
 * Viewproviders

Fils de discussion intéressants sur les objets scriptés :


 * Python object attributes lost at load
 * New FeaturePython is grey
 * Explanation on __getstate__ and __setstate__, official documentation
 * Eigenmode frequency always 0?
 * how to implement python feature's setEdit properly?

En plus de ces exemples, vous pouvez voir dans le code source de FreeCAD src/Mod/TemplatePyMod/FeaturePython.py pour plus d'exemples.