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

Soumettre un commentaire

Objects3D V2.0

Nouvelle étape dans la modélisation de notre PythonPart "Poteau en Béton Armé", regardons aujourd'hui comment configurer les ancrages de notre objet 3D. Par ancrage je pense tout particulièrement à...

Objects3D V1.0

Nouvelle série dans l'apprentissage des PythonParts, étudions la modélisation d'un objet 3D : un poteau en béton armé.1) Script InterfaceDans cet exemple nous allons mettre en place les premiers...

Objects2D V3.0

Dernière étape pour cet exemple de PythonParts, je vous propose de mettre en place une légende pour notre objet. Cette dernière devra reprendre les informations suivantes : le nom de mon objet ; le...

Objects2D V2.0

Dans le précédent article, nous avons vu comment préparer notre code en POO, voyons aujourd'hui comment en exploiter le potentiel avec ce nouvel exemple. En effet, nous allons compléter notre...

HelloWorld V3.0

Dernière étape pour notre 1er exemple de script HelloWorld, nous allons maintenant voir comment personnaliser le rendu de notre objet.1) Script InterfaceDe retour dans notre palette, je vais tout...

HelloWorld V2.0

Dans l'article précédent, nous avons appris comment générer un objet (une ligne de longueur fixe) via l'API PythonParts. Aujourd'hui je vais vous montrer la mise en place de poignées pour notre...

HelloWorld V1.0

HelloWorld est par tradition écrit pour donner un exemple d'un langage de programmation. Nous ne ferons pas exception ici avec l'écriture de notre 1er script. Le but est simple, créer une ligne de...

Structure des PythonParts

Allplan est installé, votre IDE est prêt... parfait, voyons en détail le fonctionnement des PythonParts.1) Description des fichiersPour fonctionner, un PythonPart a besoin d'au moins 2 fichiers :...

Introduction

Dans cette série d'articles, nous allons étudier l'édition de scripts en langage de programmation Python pour le logiciel Allplan.Pour vous permettre de bien suivre ces tutoriels, je vais partir sur...