Nous allons commencer à construire des objets plus complexes sur Allplan, mais avant tout, un mot sur la Programmation Orientée Objet (POO)…
1) Programmation Orientée Objet
La Programmation Orientée Objet (ou POO) est une manière d’organiser son code. Elle nous permet de créer des entités appelées objets que l’on peut manipuler.
En POO, un objet est une instance d’une classe, qui est une structure de données définissant les attributs (variables) et les méthodes (fonctions) associées à cet objet.
Les objets peuvent interagir entre eux en échangeant des messages, ce qui permet une communication simplifiée et une organisation modulaire du code.
La POO permet ainsi de créer des programmes plus flexibles, maintenables et évolutifs en encapsulant la complexité, en facilitant la réutilisation du code et en favorisant la modularité.
Le Python est un langage de programmation parfaitement adapté à la POO, voyons maintenant comment adapter notre code…
2) Script Interface
Notre travail va concerner principalement le code en Python, il n’y a aura donc pas de changement majeur sur le fichier .pyp à l’exception de la liaison avec le script principal :
<Script>
<Name>APIHub\objects_2D.py</Name>
<Title>Objects2D</Title>
<Version>1.0</Version>
</Script>
Ma variable pour la longueur de la ligne a été correctement nommée en vue des prochains exemples :
<Parameter>
<Name>LineLength</Name>
<Text>Longueur</Text>
<FontFaceCode>4</FontFaceCode>
<Value>1000.0</Value>
<ValueType>Length</ValueType>
</Parameter>
A noter : j’ai inséré le champ FontFaceCode qui permet de personnaliser le rendu du texte (ici la valeur 4 souligne le texte)
Je vous invite à regarder cette page pour plus d’informations.
Voici le fichier complet :
3) Script Principal
-
Importation des modules
Comme tout fichier Python, nous allons commencer par importer les modules nécessaires à la bonne exécution du script :
import NemAll_Python_BaseElements as BaseElements
import NemAll_Python_BasisElements as BasisElements
import NemAll_Python_Geometry as Geometry
import NemAll_Python_IFW_ElementAdapter as ElementAdapter
from BuildingElement import BuildingElement
from CreateElementResult import CreateElementResult
from PythonPartUtil import PythonPartUtil
from HandlePropertiesService import HandlePropertiesService
from HandleDirection import HandleDirection
from HandleParameterData import HandleParameterData
from HandleParameterType import HandleParameterType
from HandleProperties import HandleProperties
-
Vérification de la version d’Allplan
Pas de changement pour la fonction de base check_allplan_version, par défaut toutes les versions d’Allplan sont acceptées :
def check_allplan_version(build_ele, version):
# Support all versions
return True
-
Utilisation des poignées
Idem pour l’ « écoute » des poignées, pas d’évolution :
def move_handle(build_ele, handle_prop, input_pnt, doc):
HandlePropertiesService.update_property_value(build_ele, handle_prop, input_pnt)
return create_element(build_ele, doc)
-
Création des éléments
Comme vu précédemment, create_element est la 2ème fonction requise pour les PythonParts standards.
Je commence par déclarer ma liste d’objets ainsi que ma liste des poignées que je place également en renvoi de la fonction :
def create_element(build_ele, doc):
model_ele_list = []
handle_list = []…
return CreateElementResult(model_ele_list, handle_list)
Je déclare ensuite ma variable qui me servira à la création des PythonParts : pyp_util.
J’affecte également ma liste d’éléments à cette fonction :
pyp_util = PythonPartUtil()
…
# Create the PythonPart
model_ele_list = pyp_util.create_pythonpart(build_ele)
Comme dans les exemples précédents, je récupère les différents paramètres depuis la palette Allplan :
# Extract palette parameter values
line_length = build_ele.LineLength.value
# Define common properties
if build_ele.UseGlobalProperties.value:
com_prop = BaseElements.CommonProperties()
com_prop.GetGlobalProperties()
else:
com_prop = build_ele.CommonProperties.value
A noter : précédemment, la condition a été mise en place pour un rendu personnalisé de notre objet.
-
Classe Mère : Objects2D
Cette classe sera générique à tous nos objets 2D, je vais donc m’en servir comme classe mère.
Je commence par lui passer la variable suivante : les propriétés de rendu, puis j’initialise la géométrie de mon objet : self.geo.
class Objects2D:
def __init__(self, object_2d_prop):
self.object_2d_prop = object_2d_prop
self.geo = None
Je prépare ma fonction pour générer la géométrie de mon objet.
Comme cette fonction dépendra de mon objet en question (ligne, rectangle, cercle, …), je laisse cette fonction vide dans ma classe mère.
def create_geo(self):
pass
A noter : en Python, les fonctions doivent avoir au minimum une instruction, je place donc pass qui a une valeur nulle.
Vient ensuite la création du rendu en vue en plan de mon objet à partir des paramètres de rendu (plume, couleur, etc…) et de la géométrie :
def add_view(self):
object_2d = BasisElements.ModelElement2D(self.object_2d_prop, self.geo)
return object_2d
Avant de revoir la mise en place des poignées, voici un rappel sur la syntaxe :
# Create the handles
handle = HandleProperties(« LineLengthHandle », # identifiant de la poignée
Geometry.Point3D(line_length, 0, 0), # point d’accroche de la poignée
Geometry.Point3D(), # point d’origine
[HandleParameterData(« LineLength », HandleParameterType.POINT_DISTANCE)], # identifiant du paramètre dans la palette
HandleDirection.X_DIR # direction de la poignée)
handle.info_text = « Longueur » # texte d’aide visible dans Allplan
handle_list.append(handle)
Nous avons ici 6 variables identifiées pour un nombre de poignées inconnu je vais donc créer une classe Handle pour la gestion de ces dernières.
class Handle:
def __init__(self, handle_id, handle_point, ref_point, handle_param_data, handle_move_dir, handle_info_text):
self.handle_id = handle_id
self.handle_point = handle_point
self.ref_point = ref_point
self.handle_param_data = handle_param_data
self.handle_move_dir = handle_move_dir
self.handle_info_text = handle_info_text
A noter : on retrouve bien l’ensemble de nos variables nommées de manière explicite
-
Classe Enfant : Line2D
Directement liée à la Object2D, nous allons créer une classe Line2D qui héritera de toutes ses fonctions, on l’appelle alors classe enfant.
class Line2D(Objects2D):
def __init__(self, object_2d_prop, line_length):
Objects2D.__init__(self, object_2d_prop)
self.line_length = line_length
self.handles_prop = [Handle(« LineLengthHandle »,
Geometry.Point3D(self.line_length, 0, 0),
Geometry.Point3D(),
« LineLength »,
HandleDirection.X_DIR,
« Longueur »
)
]
A noter :
- Line2D reprend toutes les variables d’Object2D et je complète avec la longueur de la ligne : line_length ;
- je renseigne les 6 propriétés de ma poignée.
Je génère ensuite la géométrie de ma ligne en écrasant ma fonction create_geo :
def create_geo(self):
self.geo = Geometry.Line2D(0, 0, self.line_length, 0)
-
Appel des Fonctions
Ultime étape, je retourne dans ma fonction create_element pour appeler tout ce dont je vais avoir besoin pour créer mon objet (ici une ligne) :
- création de la géométrie ;
- rendu en vue en plan ;
- création des poignées.
Comme toutes ces étapes ont été correctement préparées dans les classes, la syntaxe est :
-
Création de la Géométrie
# Create 2D object
object_2d = Line2D(com_prop, line_length)
object_2d.create_geo()
-
Rendu
# Add object to view
pyp_util.add_pythonpart_view_2d(object_2d.add_view())
-
Création des Poignées
# Create the handles
for item in object_2d.handles_prop:
handle = HandleProperties(item.handle_id,
item.handle_point,
item.ref_point,
[HandleParameterData(item.handle_param_data, HandleParameterType.POINT_DISTANCE)],
item.handle_move_dir
)
handle.info_text = item.handle_info_text
handle_list.append(handle)
A noter : on ignore pour l’instant le nombre exact de poignées à mettre en place. En effet, cela dépendra du futur objet à créer.
Je prépare donc une boucle avec for … in pour toutes les instances de poignées dont j’aurais besoin puis je les ajoute à la liste de mes poignées.
Voici le fichier complet :
Dans ce chapitre nous avons vu comment préparer notre code en POO avec notamment une gestion de classe mère et classe enfant.
Dans le prochain article, cette organisation prendra tout son sens avec la mise en place d’autres objets 2D à créer.
0 commentaires