Today I saw a post on the Dynamo Forum where there was confusion between using Element.SetParameterByName() on a Revit API Element in python and it failing. For beginners (and even some intermediate users) this is quite confusing, so I thought I would at least help clear this up.
To begin with, Element.SetParameterByName() is a Dynamo node and isn’t a method on the actual Revit API object. When using Dynamo Nodes in python, the arguments (which are the inputs between the () at the end of the method name) are the same as the IN ports to the node. For instance, to create a simple Line.ByStartPointEndPoint(), you would write is like so…
import clr clr.AddReference('ProtoGeometry') from Autodesk.DesignScript.Geometry import * p1 = IN[0] p2 = IN[1] l = Line.ByStartPointEndPoint(p1,p2) OUT = l
which is equivalent to the actual node in Dynamo…
So, you can see that the Inputs of the node and Arguments are the same. To use the Element.SetParameterByName() method correctly, look at the following python example…
Example 01…
import clr clr.AddReference("ProtoGeometry") from Autodesk.DesignScript.Geometry import * clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.Elements) clr.ImportExtensions(Revit.GeometryConversion) # Used to Ensure that object is in a list... def tolist(obj1): if hasattr(obj1,"__iter__"): return obj1 else: return [obj1] ### Input Variables ### # Ensure elems is a list to satisfy For Loop if only one element is passed. NOTE: Element has NOT been Unwrapped using the UnwrapElement conversion method as not using Revit API. elems = tolist(IN[0]) # List of Parameter Names to find and update... pNames = tolist(IN[1]) # List of Parameter Values to update to... pVals = tolist(IN[2]) ### Output Variables ### outList = [] ### Main Body ### # Test if number of Parameter Names is equal to number of values... if len(pNames) == len(pVals): # Loop through all the input Elements... for e in elems: # Loop through all parameter names and values at once... for n,v in zip(pNames,pVals): # using Try/Except statement to handle errors... try: # Using Element.SetParameterByName Node. Note that the Revit.Elements.Element.SetParameterByName() is the same as the Library browser location. This is important. ie you can't just call Element.SetParameterByName()... elem = Revit.Elements.Element.SetParameterByName(e,n,v) outList.append(elems) # Report Exception message... except Exception, ex: outList.append(ex.message) # Return Results... OUT = outList else: OUT = "The number of Parameter Names does not match the number of Parameter Values"
Example 02
import clr
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
# Used to Ensure that object is in a list...
def tolist(obj1):
if hasattr(obj1,"__iter__"): return obj1
else: return [obj1]
### Input Variables ###
# Ensure elems is a list to satisfy For Loop if only one element is passed. NOTE: Since we are wanting to use the Revit API, you need to use the UnwrapElement() method. This means that you can use the Revit API methods on the Object.
elems = tolist(UnwrapElement(IN[0]))
# List of Parameter Names to find and update...
pNames = tolist(IN[1])
# List of Parameter Values to update to...
pVals = tolist(IN[2])
### Output Variables ###
outList = []
### Main Body ###
# Get Document Display Unit Type for Length to convert any Parameters of type Length later on...
dut = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
# Test if number of Parameter Names is equal to number of values...
if len(pNames) == len(pVals):
# Transaction required to make change in Revit...
TransactionManager.Instance.EnsureInTransaction(doc)
# Loop through all Elements...
for e in elems:
# Loop through all parameter names and values at once...
for n,v in zip(pNames,pVals):
# using Try/Except statement to handle errors...
try:
# Using LookupParameter method since we are searching by Parameter Name...
p = e.LookupParameter(n)
# Check if the Parameters Type is Length and convert to Revits Internal Units. ie if you are using metric mm, this will convert all length parameters to imperial ft. Revit uses imperial for everything, therefore you need to convert. Add additional logic if you need to convert other Parameter Types ie Forces etc
if p.Definition.ParameterType == ParameterType.Length:
v = UnitUtils.ConvertToInternalUnits(v,dut)
# Set the Parameters value. If unit type does not match the Parameters Unit Type, then this will fail. ie passing a string into a parameter that takes an integer will fail...
p.Set(v)
outList.append(e)
# Report Exception message...
except Exception, ex:
outList.append(ex.message)
# Closing current Transaction to commit changes...
TransactionManager.Instance.TransactionTaskDone()
# Return results...
OUT = outList
else:
OUT = "The number of Parameter Names does not match the number of Parameter Values"
In your python code, if you are planning to use the Revit API methods on an Element (Example 02), you need to UnwrapElement(DSObject). Dynamo wraps up Revit API objects so they can be used via DS Nodes in the graph. If you don’t do this you will get the error that this object does not have a method or property that you are trying to apply to the object. On the other hand, if you are using Revit Nodes in your code (Example 01), Unwrapping is not required, nor are Transactions and other bits of code like unit conversion as this is done inside the method already.
I prefer to stick to one or the other and not mix them up, so either pure Revit API or pure Dynamo Revit Nodes (mostly pure Revit API though because the biggest reason to do this is that there are no nodes Built-In Revit Nodes to do it). Although, having said that, you could wrap/unwrap to make use of both Revit Nodes and Revit API if you wanted to.
For example, to wrap up a Revit API Object to a Design Script Object to use with the Revit Nodes (NOT Revit API methods/properties) you would do this…
e = e.ToDSType(True)
and vice versa, if you wanted to unwrap the Element so you can perform Revit API methods calls, you can do it like so…
e = UnwrapElement(e)
This is a very long answer to a quite a simple question, but I thought I would be thorough.
Hope this helps and clarifies the different ways in which you can leverage Revit Nodes or Revit API effectively in python and you know the difference between a Design Script Object and a Revit API Object.
November 18, 2019 at 6:55 am
Thank you for the article clarifies clearly. I am a beginner in dynamo and writing python script, So I was wondering if there is a difference between: “clr.ImportExtensions(Revit.Elements)” and “from Revit.Elements import*”? And what is the advantage of using one over the other?
Thank you in advance
LikeLike
January 10, 2020 at 10:37 pm
Hi Cathy,
First of all, apologies for the delay in response, wordpress isn’t notifying me when there are comments which is a little annoying. Also, I’m glad you are finding the content useful! 🙂
As for your question, it is better practice to use the clr.ImportExtension(Type or “namespace”) method. This will import all the extension methods in that class. have a look at the accepted answer here on stack exchange…
https://stackoverflow.com/questions/628482/can-you-use-linq-types-and-extension-methods-in-ironpython
Note what the output is before and after the clr.ImportExtension(System.Linq) for dir(List). After, you will see that there is bunch of additional methods (extension methods) which you can now call on that type.
A good example using the
clr.ImportExtension(Revit.GeometryConversion)
vs…
From Revit Import Revit.GeometryConversion Import *
would be converting Revit Geometry to DS Geometry using the extension methods “.ToPoint()” or “.ToProtoType()”. Try the following code on a beam for instance to see what I mean…
With clr.ImportExtension()…
import clr
clr.AddReference(“RevitNodes”)
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference(“RevitAPI”)
from Autodesk.Revit.DB import *
elem = UnwrapElement(IN[0])
OUT = elem.Location.Curve.ToProtoType()
Without clr.importExtension()…
import clr
clr.AddReference(“RevitNodes”)
import Revit
from Revit.GeometryConversion import *
clr.AddReference(“RevitAPI”)
from Autodesk.Revit.DB import *
elem = UnwrapElement(IN[0])
OUT = elem.Location.Curve.ToProtoType()
You will see that without you will get an error saying…
AttributeError: ‘Line’ object has no attribute ‘ToProtoType’
I feel that’s quite a long answer for a reply, but doesn’t feel long enough, so may create a article specifically for this as this is a fantastic question and one I actually didn’t know the answer to initially! So, thank you for teaching me too! : )
LikeLike
January 13, 2020 at 11:29 pm
Hi cathy,
I have added a new article for this here…
https://danimosite.wordpress.com/2020/01/13/importextensions-vs-import-from/
LikeLike
May 4, 2021 at 6:15 am
Thanks for a good article.
LikeLiked by 1 person