Vous voulez anticiper la migration de votre code Python vers PythonNet3, bonne décision, voici quelques conseils.
#DynamoBIM #Python #RevitAPI #PythonNet3
#AutodeskExpertElite #AutodeskCommunity 
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
Vous découvrirez dans cet article différents conseils et astuces (non
exhaustifs) pour passer à PythonNet3.
Cet article s'adresse à tous, mais par défaut on va prendre le cas le
plus extrême, une migration depuis IronPython2.7 vers PythonNet3.
Si vous utilisez déjà CPython3 il est probable que certains conseils vous soient déjà familiers.
Résumé audio (IA)
📌Pourquoi PythonNet3 ?
Dynamo_PythonNet3 est le nouveau moteur Python pour Dynamo,
basé sur CPython 3.11 et la librairie Python.NET v3. Il vise à
rapprocher l’expérience “Python dans Dynamo” de ce qu’on avait avec
IronPython, tout en profitant de tout l’écosystème CPython (numpy,
pandas, etc.).
Python.NET crée un pont bidirectionnel entre CPython et le
CLR/.NET : il permet à du code Python d’utiliser des assemblies
.NET et, inversement, d’embarquer Python dans une appli .NET.
Dynamo_PythonNet3 existe à ce jour en tant que package et
sera le moteur par défaut dans les futures versions de Dynamo. Les
codes réalisés avec CPython3 devraient s'exécuter sans problème avec
ce nouveau moteur.
Voici un résumé des différentes versions de Python disponibles dans
Dynamo à ce jour.
Caractéristiques | IronPython 2.7 | IronPython 3 | Python.NET 2.5.x | Python.NET 3.x |
---|---|---|---|---|
Nom Dynamo | IronPython2 | IronPython3 | CPython3 | PythonNet3 |
Concept Principal |
Implémentation de Python sur .NET
(avec DLR) |
Implémentation de Python sur .NET
(avec DLR) |
Passerelle vers CPython | Passerelle vers CPython |
Version Python | Python 2.7 | Python 3.4 (actuellement) |
Python 2.7, Python 3.5-3.9 |
Python 3.7+ (moderne) |
Support .NET |
.NET Framework & .Net Core/ .NET 6/7/8 Incompatibilité avec les futures versions .NET |
.NET Framework & .NET Core / .NET 5+ | .NET Framework (principalement) | .NET Framework & .NET Core / .NET 5+ |
Compatibilité Librairies | ⚠️ Faible. Incompatible avec les librairies Python qui utilisent des extensions en C (ex: NumPy, Pandas, Scikit-learn). | ⚠️ Faible. Même limitation fondamentale que la v2.7, bien que l'écosystème pur Python 3 soit plus riche. | ✅ Excellente. Accès à tout l'écosystème de CPython. | ✅ Excellente. Accès à tout l'écosystème PyPI moderne (NumPy, Pandas, etc.). |
Performance | Bonne pour l'interopérabilité .NET pure. | Bonne pour l'interopérabilité .NET pure. | Dépend de CPython. Léger surcoût pour les appels. |
Dépend de CPython. Léger surcoût pour les appels, mais très
optimisé.
Correction de bugs. |
État du Projet | ❌ Obsolète. Plus maintenu activement. | ✅ Actif. La version moderne et recommandée si vous choisissez IronPython. | ❌ Obsolète. Remplacé par la version 3.x. | ✅ Actif. Le standard de l'industrie pour l'interopérabilité Python/.NET. |
📌Migration Python2.7 vers Python3
D'abord, si vous migrez depuis IronPython2.7, il faudra prendre en
considération les nouvelles syntaxes de Python3.
Il existe plusieurs guides sur le Web.
Il existe plusieurs guides sur le Web.
https://docs.python.org/3/howto/pyporting.html
devient
- Exemple
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import DockStyle
dockstyle = DockStyle.None
logger.warn(dockstyle)
devient
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import DockStyle
dockstyle = getattr(DockStyle, 'None')
logger.warning(dockstyle)
📌Nouveautés et changements majeurs de PythonNet3
Si vous migrez depuis CPython3 qui est basé sur PythonNet2.5, voici une liste des principaux changements et nouveautés.- Changements majeurs (BREAKING CHANGES)
-
Héritage et constructeurs : si vous surchargez la méthode
__init__
d'un type .NET en Python, vous devez dorénavant appeler explicitement le constructeur de la classe de base en utilisantsuper().__init__(...)
.
-
Conversions d'énumérations : la conversion implicite entre les
énumérations C# et les entiers Python est désactivée. Vous devez à
présent utiliser les membres de l'énumération (par exemple,
MonEnum.Option
).
De plus, la méthode .NETEnum.Value.ToString()
retourne désormais le nom de la valeur au lieu de son entier.
-
Collections et tableaux : les collections et tableaux .NET ne sont
plus convertis automatiquement en collections Python.
Néanmois, ils implémentent les interfaces de collections Python standard de collections.abc.
Ce changement permet d'utiliser des méthodes .NET comme LINQ directement sur ces objets.
-
Améliorations et corrections de bugs
-
PyObject implémente maintenant IEnumerable en plus de
IEnumerable<T>
. Un bug où toutes les instances de classes .NET étaient considérées comme Iterable a été corrigé.
Ce qui résout des problèmes avechasattr(pyObject, "__iter__")
.
-
Vous pouvez maintenant surcharger des méthodes .NET en Python qui
utilisent les paramètres
out
ouref
. Pour ce faire, vous devez retourner les valeurs de ces paramètres modifiés sous forme de tuple.
-
Les opérateurs arithmétiques binaires et unaires de Python appellent
désormais les méthodes d'opérateurs C# correspondantes.
- La prise en charge de la surcharge des méthodes a été améliorée, y compris les méthodes ayant des paramètres de type générique (<T>).
-
La possibilité d'utiliser simplement des Interfaces de Classe
.NET (feature Dynamo).
-
Extension de méthodes LINQ sur les
IEnumerable<T>
(feature Dynamo).
Vous trouverez la liste complète ici.
https://github.com/pythonnet/pythonnet/blob/master/CHANGELOG.md
📌Net Framework 4.x VS .Net 8+ (Net Core)
Dynamo_PythonNet3 est disponible à partir de Revit 2025 en tant que package, ce qui signifie qu'il est exécuté sous .Net8+ au lieu du .Net Framework 4.x.
Cela entraîne certaines conséquences, en voici quelques une :
- Accès aux objets COM
- Impossible d'utiliser directement les "assemblies" présentes dans
le G.A.C. Windows (comme Excel Interop ou Word Interop).
- Necessité d'utiliser la Reflexion pour acceder aux objets COM (contrairement a IronPython qui utilise le DLR).
Ainsi cette ligne échouera.
clr.AddReference(“Microsoft.Office.Interop.Excel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”)
Si vous vous trouvez dans ce cas, envisagez de migrer vers OpenXML
SDK ou openpyxl.
Si pour une raison quelconque, vous avez besoin d'utiliser Excel Interop ou une interface COM, il faudra :
- Potentiellement créer une instance du Type d'Application
- Utiliser la Reflexion .Net (pas d'acces direct aux méthodes et propriétés comme sur IronPython)
import clr
import sys
import System
from System import Array
from System.Collections.Generic import List, Dictionary, IDictionary
clr.AddReference("System.Reflection")
from System.Reflection import BindingFlags
from System.Runtime.InteropServices import Marshal
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
xls_filePath = IN[0]
xls_SheetName = IN[1]
dict_values = {}
systemType = System.Type.GetTypeFromProgID("Excel.Application", True)
try:
ex = System.Activator.CreateInstance(systemType)
except:
methodCreate = next((m for m in clr.GetClrType(System.Activator)\
.GetMethods() if "CreateInstance(System.Type)" in m.ToString()), None)
ex = methodCreate.Invoke(None, (systemType, ))
ex.Visible = False
workbooks = ex.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty , None, ex, None)
workbook = workbooks.GetType().InvokeMember("Open", BindingFlags.InvokeMethod , None, workbooks, (xls_filePath, ))
worksheets = workbook.GetType().InvokeMember("Worksheets", BindingFlags.GetProperty , None, workbook, None)
#
ws = worksheets.GetType().InvokeMember("Item", BindingFlags.GetProperty , None, worksheets, (xls_SheetName,))
- Windows.Forms
-
Windows.Forms contient une nouvelle Classe TaskDialog qui
peut être en conflit avec la classe de l'API RevitAPIUI .
- La taille du Font par défaut a changé.
# in the __init__ function
self.Font = System.Drawing.SystemFonts.DefaultFont
- Changement d'Espaces de Noms
Certaines technologies spécifiques à Windows ou obsolètes ont été supprimées ou remplacées par de nouvelles implémentations avec de nouveaux espaces de noms.
-
Changement de la méthode Diagnostics.Process.Start()
ProcessStartInfo.UseShellExecute
a une valeur par défaut de
False
sur .NET Core(sur .NET Framework, sa valeur par défaut est
True
).
psi = ProcessStartInfo()
psi.FileName = fullpath
psi.UseShellExecute = True
System.Diagnostics.Process.Start(psi)
-
Marshal.GetActiveObject()
Marshal.GetActiveObject()
n'est plus disponible.
Vous pouvez utiliser
BindToMoniker
si vous connaissez le chemin du fichier en cours
d'utilisation.
import clr
import os
import time
import System
clr.AddReference("System.Reflection")
from System.Reflection import BindingFlags
clr.AddReference("AcMgd")
clr.AddReference("AcCoreMgd")
clr.AddReference("Autodesk.AutoCAD.Interop")
from System import *
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.Interop import *
from Autodesk.AutoCAD.ApplicationServices import Application as acapp
changeViewCommand = "_VIEW "
adoc = Application.DocumentManager.MdiActiveDocument
currentFileName = adoc.Name
print(currentFileName)
com_doc = System.Runtime.InteropServices.Marshal.BindToMoniker(currentFileName)
args = System.Array[System.Object]([changeViewCommand])
com_doc.GetType().InvokeMember("SendCommand", BindingFlags.InvokeMethod, None, com_doc, args)
OUT = True
Vous pouvez retrouver une liste complète ici.
📌Conseils et Astuces
- Respecter les signatures.
La signature d'une méthode doit être bien respectée, ce qui implique
:
-
le nom de la méthode doit être correct.
-
la méthode doit être appelée sur le bon type d'objet (méthode
d'instance VS méthode statique).
- les types des objets passés en arguments doivent être corrects.
Dans cet exemple avec IronPython, nous pouvons faire ceci.
from System.Collections.Generic import List
# some imports
selelemz=[ElementId(124291), ElementId(124292), ElementId(124293)]
elements=FilteredElementCollector(doc,selelemz).WhereElementIsNotElementType().ToElements()
Avec PythonNet, il faut caster/convertir la liste Python.
from System.Collections.Generic import List
# some imports
selelemz=List[ElementId][ElementId(124291), ElementId(124292), ElementId(124293)])
elements=FilteredElementCollector(doc,selelemz).WhereElementIsNotElementType().ToElements()
- Les Collections .Net
Comme mentionné plus haut, PythonNet3 ne convertit plus les
.Net Collection
liste Python native.
-
Plus besoin de caster/convertir vers une
.Net Collection
lorsqu'on appelle une méthode .Net
- Possibilité d'utiliser les extensions Linq (feature by Dynamo).
Caractéristique | IronPython(2 ou 3) | Python.NET 2 (alias CPython3) | Python.NET 3 |
---|---|---|---|
Mécanisme de conversion | Les collections.NET sont intégrées de manière native, sans conversion ni copie de données. | Conversion automatique et implicite en listes Python natives. | La conversion automatique a été supprimée. Les collections.NET implémentent maintenant les interfaces de collections standard de Python (collections.abc). |
Conséquences pour le développeur | Les collections.NET se comportent comme des objets Python, mais les manipulations ne génèrent pas de copies. C'est performant, mais le framework ne prend pas en charge les bibliothèques CPython comme NumPy. | Pratique mais coûteux en performance à cause d'une copie profonde des données. Les modifications apportées à la liste Python ne sont pas répercutées sur l'objet.NET d'origine. |
L'objet est une "vue" de la collection.NET, sans copie de
données, ce qui améliore la performance. Les opérations de base
(comme l'itération) fonctionnent, mais les méthodes spécifiques
aux listes Python (comme
append()) nécessitent une conversion explicite via
list(). Possibilité d'utiliser les extensions LINQ |
REMARQUES:
- Utilisation des méthodes d'extension LINQ
Voir mon article à ce sujet.
https://voltadynabim.blogspot.com/2025/03/dynamo-python-dspytonnet3-et-linq.html
À titre d'exemple, si vous utilisiez précédemment les Predicate sur des
Exemple 1
devient
À titre d'exemple, si vous utilisiez précédemment les Predicate sur des
IList<T>
, vous pouvez migrer ainsi :Exemple 1
fillPatterns = FilteredElementCollector(doc).OfClass(FillPatternElement).ToElements().FindAll(lambda x : x.GetFillPattern().GridCount == 2)
devient
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
fillPatterns = FilteredElementCollector(doc).OfClass(FillPatternElement)\
.Where(System.Func[DB.Element, System.Boolean](lambda x : x.GetFillPattern().GridCount == 2))\
.ToList()
Exemple 2
fillPattern = FilteredElementCollector(doc).OfClass(FillPatternElement).ToElements().Find(lambda x : x.GetFillPattern().GridCount == 2)
devient
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
fillPattern = FilteredElementCollector(doc).OfClass(FillPatternElement).FirstOrDefault(System.Func[DB.Element, System.Boolean](lambda x : x.GetFillPattern().GridCount == 2))
- Attention, IEnumerable<T> ≠ List<T>
L'indexation avec les crochets
[index]
n'est pas possible sur IEnumerables.
Voici un exemple avec PythonNet3 où nous ne pouvons pas utiliser
l'indexation car la méthode
face.ToProtoType()
retourne un IEnumerable et non une
IList<T>
import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
element = UnwrapElement(IN[0])
ref = HostObjectUtils.GetSideFaces(element, ShellLayerType.Exterior)[0] # GetSideFaces return an Ilist so we can use indexer
face = element.GetGeometryObjectFromReference(ref)
ds_surface = face.ToProtoType()[0] # ToProtoType() on Revit face return an IEnumerable so we can't use indexer
OUT = ds_surface
Une solution consiste à convertir l'IEnumerable en liste Python ou à utiliser directement une méthode d'extension LINQ
ElementAt()
ou First()
import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
element = UnwrapElement(IN[0])
ref = HostObjectUtils.GetSideFaces(element, ShellLayerType.Exterior)[0] # GetSideFaces return an Ilist so we can use indexer
face = element.GetGeometryObjectFromReference(ref)
ds_surface = face.ToProtoType().First() # ToProtoType() on Revit face return an IEnumerable so we can use LINQ
# OR convert to python list
ds_surface = list(face.ToProtoType())[0] # ToProtoType() on Revit face return an IEnumerable so we need convert to python list to user indexer
OUT = ds_surface
- Profiter des librairies Python
Si d'origine vous utilisiez IronPython(2 ou 3) vous pourrez désormais profiter des librairies Python telles que :
- numpy
- pandas
- openpyxl
- scipy
Voici quelques exemples d'utilisation avec scipy
- Classe qui hérite d'un type .Net¶
Désormais dans les classes qui héritent d'un type .Net (Winform, WPF,
DataTable, Interface), si vous surchargez la méthode
__init__
, vous devez dorénavant appeler explicitement le constructeur (initialisateur) de la
classe de base en utilisant super().__init__(...)
.
Exemple:
class TestForm(Form):
def __init__(self):
super().__init__() # add this line
self.Font = System.Drawing.SystemFonts.DefaultFont
self.InitializeComponent()
def InitializeComponent(self):
self._buttonCancel = System.Windows.Forms.Button()
self._buttonOK = System.Windows.Forms.Button()
self.SuspendLayout()
- Implémentation des Interfaces de Classe .NET
Dans le CPython3 (PythonNet2.5) il est impossible d'utiliser facilement
des interfaces de classe .Net, c'est désormais corrigé avec
Dynamo_PythonNet3.
Rappel:
Voir cet article pour plus de détails
https://voltadynabim.blogspot.com/2024/10/dynamo-python-pythonnet-et-les.html
Exemple
La syntaxe est différente sous IronPython et PythonNet3.
Avec PythonNet3, lorsqu'une méthode comporte des paramètres
Néanmoins, avec quelques concessions sur du Binding, il est toujours possible d'utiliser WPF (même avec le pattern MVVM) via
Vous trouverez des exemples ici
https://voltadynabim.blogspot.com/2025/02/dynamo-python-pythonnet-serie-wpf-et-le.html
La collection Python
Il est nécessaire de retirer ce type d'objet dans la construction d'un d'un
à remplacer par
Dans le cas d'objets qui héritent d'une Interface .Net, il faut
caster (convertir) l'objet sur cette dernière pour utiliser les
méthodes héritées.
Exemple sur conversion sur les méthodes d'Interfaces Enums (
Une classe Python qui dérive d'une classe .NET doit possèder l'attribut
__namespace__
Voir cet article pour plus de détails
https://voltadynabim.blogspot.com/2024/10/dynamo-python-pythonnet-et-les.html
Exemple
class Custom_SelectionElem(ISelectionFilter):
__namespace__ = "SelectionNameSpace_tEfYX0DHE"
#
def __init__(self, bic):
super().__init__() # necessary if you override the __init__ method
self.bic = cls.builtInCategory
def AllowElement(self, e):
if e.Category.Id == ElementId(self.bic):
return True
else:
return False
def AllowReference(self, ref, point):
return True
#
class Custom_FamilyOption(IFamilyLoadOptions) :
__namespace__ = "FamilyOptionNameSpace_tEfYX0DHE"
def __init__(self):
super().__init__() # necessary if you override the __init__ method
def OnFamilyFound(self, familyInUse, _overwriteParameterValues):
overwriteParameterValues = True
return (True, overwriteParameterValues)
def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, _overwriteParameterValues):
overwriteParameterValues = True
return (True, overwriteParameterValues)
- MAJ Ref Out Parameters
La syntaxe est différente sous IronPython et PythonNet3.
Avec PythonNet3, lorsqu'une méthode comporte des paramètres
out
ou
ref
, les arguments apparaissent comme des arguments normaux en Python,
mais la valeur de retour de la méthode est modifiée.
Exemple avec IronPython.
Attention, il y a une différence avec l'ancienne version "PythonNet2.5" lorsque la méthode ne retourne rien (Void)
Ainsi cette ligne échouera.
curvA = cableTray1.Location.Curve
curvB = cableTray2.Location.Curve
outrefClosest = clr.Reference[IList[ClosestPointsPairBetweenTwoCurves]](List[ClosestPointsPairBetweenTwoCurves]())
curvA.ComputeClosestPoints(curvB, True, True, True, outrefClosest)
listOfPoint = [[x.XYZPointOnFirstCurve.ToPoint(), x.XYZPointOnSecondCurve.ToPoint()] for x in outrefClosest.Value]
devient
curvA = cableTray1.Location.Curve
curvB = cableTray2.Location.Curve
outrefClosest = IList[ClosestPointsPairBetweenTwoCurves](List[ClosestPointsPairBetweenTwoCurves]())
outrefClosestA = curvA.ComputeClosestPoints(curvB, True, True, True, outrefClosest)
listOfPoint = [[x.XYZPointOnFirstCurve.ToPoint(), x.XYZPointOnSecondCurve.ToPoint()] for x in outrefClosestA]
Lien documentationAttention, il y a une différence avec l'ancienne version "PythonNet2.5" lorsque la méthode ne retourne rien (Void)
-
"System.Object" VS "object"
Avec IronPython "object" retourne l'objet .Net
Avec PythonNet "object" est l'objet intégré à Python (built-in), et non l'objet .NET
System.Object
Avec PythonNet "object" est l'objet intégré à Python (built-in), et non l'objet .NET
System.Object
.
Ainsi cette ligne échouera.
a = Array.CreateInstance(object, nbr_row, nbr_colum)
à remplacer par
a = Array.CreateInstance(System.Object, nbr_row, nbr_colum)
📌Problèmes connus et solutions
-
Propriétés de type "Indexer"
Remplacer les propriétés de type "Indexer" par la
méthode
get_
space = elem.Space[phase]
à remplacer par
space = elem.get_Space(phase)
- Pas de librairie WPF comme IronPython
Néanmoins, avec quelques concessions sur du Binding, il est toujours possible d'utiliser WPF (même avec le pattern MVVM) via
XamlReader.Load(StringReader(xaml))
Vous trouverez des exemples ici
https://voltadynabim.blogspot.com/2025/02/dynamo-python-pythonnet-serie-wpf-et-le.html
-
Erreur sur les collections python
set()
La collection Python
set()
n'accepte pas les objets de type
ElementId.InvalidElementId
. Il est nécessaire de retirer ce type d'objet dans la construction d'un d'un
set()
ou d'utiliser la classe .Net HashSet<T>
.
setViewsIds = set([w.OwnerViewId for w in allWires])
à remplacer par
setViewsIds = set([w.OwnerViewId for w in allWires if w.OwnerViewId != ElementId.InvalidElementId])
# OR
setViewsIds = HashSet[ElementId](List[ElementId]([w.OwnerViewId for w in allWires]))
- Objets .Net qui héritent d'une Interface
Exemple sur conversion sur les méthodes d'Interfaces Enums (
Enum.IConvertible
)
BuiltInParameter.VIS_GRAPHICS_MODEL.ToInt32(None)
à remplacer par
value = System.IConvertible(BuiltInParameter.VIS_GRAPHICS_MODEL).ToInt32(System.Globalization.CultureInfo.InvariantCulture)
# or just for this example
int(BuiltInParameter.VIS_GRAPHICS_MODEL)
BeginInit()
class Form8(Form):
def __init__(self):
self.InitializeComponent()
def InitializeComponent(self):
self._pictureBox1 = System.Windows.Forms.PictureBox()
self._pictureBox1.BeginInit()
self.SuspendLayout()
à remplacer par
class Form8(Form):
def __init__(self):
super().__init__() # necessary if you override the __init__ method
self.InitializeComponent()
def InitializeComponent(self):
self._pictureBox1 = System.Windows.Forms.PictureBox()
System.ComponentModel.ISupportInitialize(self._pictureBox1).BeginInit()
self.SuspendLayout()
-
Instruction "
with
" sur des objets .Net
Sous IronPython le mot clé
Actuellement avec PythonNet3 cela génère une erreur.
une solution consiste à construire son propre "context manager"
Dans de rares cas, le numéro de ligne d'erreur dans le message d'erreur du nœud Python est manquant.
with
permet de gérer une ressource en garantissant l'invocation d'une méthode
d'acquisition et d'une autre de libération de cette ressource.Actuellement avec PythonNet3 cela génère une erreur.
import clr
import sys
import re
import System
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
with Transaction(doc, 'CommandName') as t:
print(f"{t=}")
print(t.GetType().ToString())
t.Start()
# do stuff
t.Commit()
une solution consiste à construire son propre "context manager"
import sys
import clr
# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')
# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *
# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *
# The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN
adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor
class CManager:
"""
a custom context manager for Disposable Object
"""
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj
def __exit__(self, exc_type, exc_value, exc_tb):
self.obj.Dispose()
if exc_type:
error = f"{exc_value} at line {exc_tb.tb_lineno}"
raise ValueError( error)
return self
dynCADObjects = IN[0]
with adoc.LockDocument():
with CManager(adoc.Database) as db:
print(db)
with CManager(db.TransactionManager.StartTransaction()) as t:
print(t)
for dynObj in dynCADObjects:
ent = t.GetObject(dynObj.AcadObjectId, OpenMode.ForWrite)
ent.Layer = "Layer1"
#a = 10 /0
t.Commit()
print(f"{db.IsDisposed=}")
print(f"{t.IsDisposed=}")
- Ligne d'erreur manquante
Dans de rares cas, le numéro de ligne d'erreur dans le message d'erreur du nœud Python est manquant.
-
Implémenter un bloc
try-except
avec la librairie traceback. -
Implémenter un bloc
try-except
avec un logger. - Implémenter un debugger.
-
OUT
Python DynamoWrapper break .Net Class
Des instance de classe Python qui dérive d'une classe .NET ont leurs
attributs supprimés par le Wrapper Dynamo
Une solution temporaire consiste à emballer l'objet .Net dans une classe Python avant de l'attribuer à la variable
OUT
class WrappNetObj:
def __init__(self, obj):
self.InnerObject = obj
def __repr__(self):
return "WrappNetObj_" + self.InnerObject.GetType().ToString()
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
class Form8(Form):
def __init__(self):
super().__init__()
self.InitializeComponent()
def InitializeComponent(self):
self.SuspendLayout()
#
# Form8
#
self.ClientSize = System.Drawing.Size(284, 261)
self.Name = "Form8"
self.Text = "Form8"
self.ResumeLayout(False)
win_obj = Form8()
OUT = WrappNetObj(win_obj)
0 commentaires:
Enregistrer un commentaire