Parfois la mise consécutive de nœuds Python requière une petite
vigilance, pour exemple lorsqu'il faut exécuter des nœuds python avec
plusieurs chemin d'exécution possible, il faut gérer les conditions
d'exécution dans chaque nœud.
Bien que pour les petits/moyens scripts, la gestion des opérations
conditionnel peut être réalisé dans un seul nœud Python, pour les plus
gros script, une alternative peut être de scinder le script en plusieurs
"morceaux", comme lorsqu'un travaille sur un projet Python avec
plusieurs fichiers/classes.
L'objectif est donc d'utiliser des noeuds Python comme
librairies/modules interne.
Un exemple avec un mini projet (chaque couleur représente un module), ici
le but est d'extraire des informations des éléments de la maquette (sous
projets ou utilisateur/créateur ) et d'exporter le résultat en Excel ou au
format csv.
Ce qui donne sous Dynamo le graphe suivant où chaque module est en fait
une classe Python:
2 méthodes pour utiliser ces classes dans le nœud principal (nœud Main) :
- La méthode simple :
Une instance de classe est créée dans les nœuds respectifs ou dans le
nœud Python principal, puis on utilise les propriétés et méthodes des
objets créés.
- La méthode plus complexe :
On importe les classes comme des modules Python interne.
Enfin pour que l'importation soit valide et que l'on puisse filtrer les
imports personnalisés il est nécessaire d'implémenter 2 méthodes :
"find_module()" et "load_module()"
Note 1 :
Les méthodes "find_module()" et
"load_module()" sont dépréciées depuis la version 3.4 de
Python. Les méthodes respectives à utiliser sont
"find_spec()" et (create_module() + exec_module())
À noté que le mécanisme d'importation essaie
find_module() uniquement si le chercheur n'implémente pas
find_spec().
De plus amples informations ici :
https://docs.python.org/fr/3/reference/import.html (voir paragraphe sur les Métas-chemins)
https://docs.python.org/3/library/importlib.html
Un des avantages avec le moteur CPython3 de Dynamo c'est que l'ensemble des nœuds Python sont exécuté dans un même environnement Python.
Nœud Python de Classe Python convertie en module
-
# https://stackoverflow.com/questions/65009309/dynamically-import-module-from-memory-in-python-3-using-hooks
import sys
import clr
import importlib
from importlib.abc import Loader, MetaPathFinder
import types
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
class StringLoader(Loader):
def __init__(self, modules):
self._modules = modules
def has_module(self, fullname):
return (fullname in self._modules)
def create_module(self, spec):
if self.has_module(spec.name):
module = types.ModuleType(spec.name)
return self._modules[spec.name]()
def exec_module(self, module):
pass
class StringFinder(MetaPathFinder):
def __init__(self, loader):
self._loader = loader
def find_spec(self, fullname, path, target=None):
if self._loader.has_module(fullname):
return importlib.machinery.ModuleSpec(fullname, self._loader)
class Foo():
guid = "a8c3aa76-f731-4086-ae08-8cb41464e425"
def __init__(self):
self.args = [chr(i) for i in range(97, 103)]
def __name__(self):
return "Foo"
def bar(self):
return ', '.join(self.args)
def Test(self):
return Point.ByCoordinates(1,2,-3)
moduleName = Foo.__name__
# MAKE A DICT OF MODULES
modules = {moduleName: Foo}
# PURGE PREVIOUS MODULES
for i in range(10):
for idx, m in enumerate(sys.meta_path):
if hasattr(m, "_loader") and hasattr(m._loader, "_modules") and moduleName in m._loader._modules:
sys.meta_path.pop(idx)
break
# DEL PREVIOUS MODULE IN sys.modules
if moduleName in sys.modules:
del sys.modules[moduleName]
# IMPORT MODULE IN sys.meta_path
finder = StringFinder(StringLoader(modules))
sys.meta_path.append(finder)
import Foo
####################################################################################
##### NOW MODULE Foo IS ACCESSIBLE IN ALL OTHER PYTHON NODES JUST NEED IMPORT ######
####################################################################################
OUT = 0
Nœud Python de l'utilisation de Classe Python
import sys
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Foo
import Foo2
pta = Foo.Test()
ptb = Foo2.Test()
OUT = Line.ByStartPointEndPoint(pta, ptb), Foo.bar()
-
-
Note 2:
- afin de facilité l'import les classes passées à la
variable OUT n'ont pas de paramètres dans leur
constructeur.
- la variable/propriété "guid" (généré aléatoirement)
permet de filtrer les imports d'autres librairies afin qu'elles
ne passent pas par ce
hook.
Le contenu des différents nœuds Python
- Les modules
- La classe pour l'exportation
- La classe pour l'interface "Utilisateur"
- La classe pour l'Analyse des Éléments de la maquette
- Enfin le nœud principal où l'on gère l'import des modules et les différentes conditions suivant le choix d'utilisateur
Outre d'avoir un script avec des méthodes mieux organisées, un
des avantages c'est l'ajout facilité ultérieur de nouvelles
fonctionnalités / méthodes.
L'exemple des scripts ci-dessus au format Dynamo est disponible ici
0 commentaires:
Enregistrer un commentaire