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…

Line.png

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
    elsereturn [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 01.png

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"

Example 02.png

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.

Advertisement