21 oct. 2021

[Dynamo += Python] Obtenir le nom de certains Eléments

 



AttributeError: Name ? 

if this article is not in your language, use the Google Translate widget (bottom of page for Mobile version)

C’est une question qui revient souvent, comment obtenir le nom d'un Type ou d'une Pièce depuis Python  ?

Pour certaines Propriétés, l'interpréteur Python ne semble pas aller chercher au-dessus dans l'arbre d'héritage.

Le problème vient du fait que la Propriété .NET n'est pas accessible dans les classes dérivées où seul le setter ou le getter a été implémenté. 

À noter que seul le language Python semble être exposé à ce problème (IronPython ou CPython/PythonNet)

Aperçu des 2 cas les plus fréquents :


Voici quelques méthodes pour y parvenir, qui fonctionnent ou non suivant le moteur Python



Résumé comparatif de méthodes avec un temps d'exécution moyen pour 5000 éléments.

'e' étant l'élément (ElementType ou Pièce) dont l'on cherche le Nom 


Méthodes                     \                     Moteur Python

IronPython 2.7

CPython3 / PythonNet2.5

IronPython 3.x

CPython3 / PythonNet3.x
Property :
e.Name

System.Reflection:
clr.GetClrType(e.GetType()).GetMethod('get_Name').Invoke(...)

temps pour 5000 éléments : 0.022 s

temps pour 5000 éléments : 0.177 s

System.Reflection.PropertyInfo
BaseType.GetProperty('Name').GetValue(e)

temps pour 5000 éléments : 0.049 s

temps pour 5000 éléments : 0.289 s

Unbound_class_instance
Element.Name.GetValue(e)
ou 
Element.Name.__get__(e)

temps pour 5000 éléments : 0.044 s

get_Method
e.get_Name()


temps pour 5000 éléments : 0.046 s

ToDSType() wrapper conversion
e.ToDSType(False).Name

temps pour 5000 éléments : 1.379 s

temps pour 5000 éléments : 1.141 s

BuiltInParameter
e.get_Parameter(BuiltInParameter.XX).AsString()

temps pour 5000 éléments : 0.028 s

temps pour 5000 éléments : 0.089 s

Le problème est désormais corrigé avec PythonNet 3. et IronPython3 

Le code Python pour ceux qui voudraient tester

 

import clr
import sys
import System
import timeit

# import Revit API
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument
e=FilteredElementCollector(doc).OfClass(ViewFamilyType).FirstElement()


def Get_Name(obj, type = None, i = 0):	
	if isinstance(obj, Autodesk.Revit.DB.Element) and i < 10:
		type = type if type is not None else obj.GetType()
		if type.ToString() == 'Autodesk.Revit.DB.Element': 
			return type.GetProperty('Name').GetValue(obj) 
		else:
			return Get_Name(obj, type.BaseType, i + 1)

class MyTestClass:
	def __init__(self):
		pass
	def get_ByProperty(self):
		"""Property -> e.Name"""
		viewName = e.Name
		
	def get_ByReflection(self):
		"""Method -> clr.GetClrType(type).GetMethod('get_Name').Invoke(...)"""
		viewName = clr.GetClrType(e.GetType()).GetMethod('get_Name').Invoke(e, None)
		
	def get_ByReflectionBaseType(self):
		"""Method -> e.GetType().BaseType.GetProperty('Name').GetValue(e)"""
		viewName = Get_Name(e)
		
	def get_ByUnbound_class_instance(self):
		"""Method -> Element.Name.GetValue()"""
                # OR
                # DB.Element.Name.__get__(e)
		viewName = Element.Name.GetValue(e)
		
	def get_ByMethod(self):
		"""Method -> get_Name()"""	
		viewName = e.get_Name()
		
	def get_ByDSType(self):
		"""Property -> e.ToDSType(False).Name"""	
		viewName = e.ToDSType(False).Name
		
	def get_ByParameter(self):
		"""Property -> e.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()"""
		viewName = e.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
		# Example for Spatial Element
		#spName = e.get_Parameter(BuiltInParameter.ROOM_NAME).AsString()

obj = MyTestClass()
methods_list = [getattr(obj, attribute) for attribute in dir(obj) if callable(getattr(obj, attribute)) and not attribute.startswith('__')]

result = []
for method in methods_list:
	try:
		t = timeit.Timer(lambda : method())
		testTime = str(round(t.timeit(5000), 3)) + " s"
		result.append("'{}'\nSUCCESS 5000 times at {}\nin {}\n{}".format(method.__doc__, testTime, sys.implementation.name, sys.version))
	except Exception as ex:
		result.append("'{}'\n FAILED in {}\n{}\nError : {}".format(method.__doc__, sys.implementation.name, sys.version, ex))

	result.append('-' * 20)

OUT = result


Alors c'est quoi ton nom ?



 

5 commentaires:

  1. Bon article.
    Même genre de problème pour aller chercher le nom des paramètres de projet ou paramètres partagés. <- sujet d'article potentiel?

    RépondreSupprimer
    Réponses
    1. Salut Jean-Marc,
      regarde cet article il pourra peut-être t’intéresser
      https://voltadynabim.blogspot.com/2020/04/dynamopython-exemple-ui-visualisation.html

      Supprimer
  2. Very nice, Cyril. Cheers for the explanation :-)

    RépondreSupprimer