Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Python 0.6.3 to 0.7.x Migration
In the 0.6.3 environment, inputs to the
Python Script node are accessed via the
IN variable from inside of the Python script. In the case of the
Python Node With Variable Inputs, each
IN varable had the port index appended to the end, so the first input is accessed thru
IN0, the second from
IN1, and so on.
In 0.7.x, the
Python Script node accepts a variable number of inputs. Unlike in 0.6.3, each input is no longer given it's own variable. Instead, they are all packed into a list and assigned to the single
IN variable. You can access individual inputs by indexing: the first input is
IN, the second is
IN, and so on.
This allows you to do things like loop over all of the inputs, for example:
count = 0 for number in IN: count += number OUT = count
If you are migrating from a single input
Python Script node, you can simply add the following line to the beginning of your old script:
IN = IN
This will allow the rest of your code to continue using the
To facilitate easier interaction between the RevitAPI and Dynamo, we have written a comprehensive library for interacting with Revit. This means that all of our Revit data types passed between Dynamo nodes are actually wrappers around the core data types; these wrappers help Dynamo remain in sync with Revit. These changes will affect old Python scripts written for 0.6.3.
Document and Application
The Revit Document/Application can be accessed via the Dynamo
import clr # Import DocumentManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager doc = DocumentManager.Instance.CurrentDBDocument uiapp = DocumentManager.Instance.CurrentUIApplication app = uiapp.Application
All Elements coming out of Dynamo Nodes are actually wrappers around core Revit Elements. Inside of Python, you can operate on these types directly by calling our nodes from inside of Python, which are all located in the
import clr # Import RevitNodes clr.AddReference("RevitNodes") import Revit # Import types we need. Instead of listing individual types, # you can do 'from Revit.Elements import *' from Revit.Elements import CurveByPoints, ReferencePoint import System startRefPt = IN endRefPt = IN refPtArray = System.Array[ReferencePoint]([startRefPt, endRefPt]) OUT = CurveByPoints.ByReferencePoints(refPtArray)
If you would prefer to use the RevitAPI directly, you will need to unwrap the Element before operating on it, use our
TransactionManager to ensure that you're operating inside of a RevitAPI Transaction, and wrap any Element you wish to return.
import clr # Import RevitAPI clr.AddReference("RevitAPI") import Autodesk from Autodesk.Revit.DB import ReferencePointArray # Import DocumentManager and TransactionManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # Import ToDSType(bool) extension method clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.Elements) # Unwrap startRefPt = UnwrapElement( IN ) endRefPt = UnwrapElement( IN ) # Start Transaction doc = DocumentManager.Instance.CurrentDBDocument TransactionManager.Instance.EnsureInTransaction(doc) # Make the CurveByPoints arr = ReferencePointArray() arr.Append(startRefPt) arr.Append(endRefPt) cbp = doc.FamilyCreate.NewCurveByPoints(arr) # End Transaction TransactionManager.Instance.TransactionTaskDone() # Wrap OUT = cbp.ToDSType(false)
Wrapped Elements are located in the
Revit.Elements namespace. All wrapped Elements extend the
Revit.Elements.Element abstract class. This class provides a public property
InternalElement which contains a reference to the underlying RevitAPI Element, of type
Autodesk.Revit.DB.Element. Alternatively, Dynamo provides a convenience function
UnwrapElement(element) function that accepts wrapped Elements or arbitrarily nested lists of wrapped Elements. If passed a non-Element it will simply return the object unmodified.
wrappedElement = IN unwrappedElement = UnwrapElement( wrappedElement ) # Now I can use 'unwrappedElement' with the RevitAPI
In order to interoperate with our Revit nodes, any raw
Autodesk.Revit.DB.Element being returned from a Python script must be wrapped in a
Revit.Elements.Element. This can be done by using the
ToDSType(bool) extension method. The
bool argument determines whether or not the Element is "Revit-owned." This distinction is important: Revit-owned Elements are not controlled by Dynamo, non-Revit-owned Elements are. Basically, if you are creating a new Element in your Python script, then you should not mark the wrapped Element as Revit-owned. If you are selecting an Element from the Document, then you should mark the wrapped Element as Revit-owned.
import clr # Import ToDSType(bool) extension method clr.AddReference("RevitNodes") import Revit clr.ImportExtensions(Revit.Elements) docPt = FetchRefPtFromDoc() #Fetch an existing ref pt (not a real method) newPt = CreateNewRefPt() #Create a new ref pt (not a real method) OUT = [ docPt.ToDSType(True), #Not created in script, mark as Revit-owned newPt.ToDSType(False) #Created in script, mark as non-Revit-owned ]
Dynamo uses meters for length units and the Revit API uses feet for length units. This is true regardless of what the "user-facing" units are in either application. Dynamo's Geometry Conversion capabilities make it so you don't often have to think about this when converting Geometry. However, you will need to manually do a unit conversion when:
- passing a length value to a Python node and then invoking the Revit API using those lengths
- extracting a length from the RevitAPI and then returning that value from a Python node
metersToFeet = 0.3048 feetToMeters = 1 / metersToFeet # Convert a length from Dynamo to Revit API units dynamoUnitsLength = someDynamoLengthFunction() revitUnitsAfterConvert = dynamoUnitsLength * metersToFeet # Convert a length from the Revit API to Dynamo units revitUnitsLength = someRevitLengthFunction() dynamoUnitsLengthAfterConvert = revitUnitsLength * feetToMeters
The latest Dynamo Build (1.+) for Revit no longer uses meters for length units. Instead, Dynamo units are derived from the active Revit document and this is problematic if units other than m are in use since the above example shows a hard coded conversion multiplier. A simple method for dynamically assigning the correct units conversion can be achieved by using the Revit API
getDocUnits = doc.GetUnits() getDisplayUnits = getDocUnits.GetFormatOptions(UnitType.UT_Length).DisplayUnits unitConversion = UnitUtils.ConvertFromInternalUnits(1, getDisplayUnits )
Revit Geometry (solids, points, curves, etc) are all kinds of
Geometry coming out of Dynamo Nodes are NOT Revit
GeometryObject's, so they need to be converted when used with the Revit API. To make matters worse, Dynamo represents all Geometry in meters, while Revit uses feet. Dynamo's GeometryConversion facilities make this relatively painless.
To import the GeometryConversion tools, do this:
import clr clr.AddReference("RevitNodes") import Revit # Import ToProtoType, ToRevitType geometry conversion extension methods clr.ImportExtensions(Revit.GeometryConversion)
To convert a Revit
GeometryObject to Dynamo's geometry system, use:
dynamoGeometry = revitGeometryObject.ToProtoType()
To convert a piece of Dynamo
Geometry to a Revit
revitGeometryObject = dynamoGeometry.ToRevitType()
A Revit XYZ is not a GeometryObject (unfortunately, there is such a thing as a Revit Point, which is handled correctly by ToProtoType()) and there is more than one way to convert an XYZ (i.e. convert it to a Vector or Point):
point = xyz.ToPoint() vector = xyz.ToVector() xyz = pointOrVector.ToXyz()
You can omit the unit conversion, by passing false as the argument to
Here's an example:
import clr # Import RevitAPI clr.AddReference("RevitAPI") import Autodesk clr.AddReference("RevitNodes") import Revit # Import DocumentManager and TransactionManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # Import geometry conversion extension methods clr.ImportExtensions(Revit.GeometryConversion) # Import Element wrapper extension methods clr.ImportExtensions(Revit.Elements) # Unwrap the Point, yielding a Revit XYZ in Revit unit system xyz = IN.ToXyz() # Start Transaction doc = DocumentManager.Instance.CurrentDBDocument TransactionManager.Instance.EnsureInTransaction(doc) # Create a Reference Point refPt = doc.FamilyCreate.NewReferencePoint(xyz) # End Transaction TransactionManager.Instance.TransactionTaskDone() # Wrap ReferencePoint Element OUT = refPt.ToDSType(false)
Dynamo provides its own Transaction framework for working with the RevitAPI. This means that your Python script will be executing in the context of an overall Dynamo Transaction.
If you are writing RevitAPI code that requires a Transaction, then you may use the Dynamo
TransactionManager.EnsureInTransaction(): Initializes the Dynamo Transaction
TransactionManager.TransactionTaskDone(): Tells Dynamo that we are finished using the Transaction
TransactionManager.ForceCloseTransaction(): Tells Dynamo to commit the active Transaction. This is slower than
TransactionTaskDone(), so only use it when you actually need to close the Transaction for your script to work.
For your scripting, any place where you would normally create a new RevitAPI Transaction and then call
Transaction.Start(), you will instead use
TransactionManager.EnsureInTransaction(). Any place where you would normally call
Transaction.Commit(), you will instead use
TransactionManager.TransactionTaskDone(). Any place where you actually definitely need the Transaction to end (sitations where you want to operate on the model after regeneration occurs), then you may use
import clr # Import DocumentManager and TransactionManager clr.AddReference("RevitServices") import RevitServices from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # Get the document doc = DocumentManager.Instance.CurrentDBDocument # "Start" the transaction TransactionManager.Instance.EnsureInTransaction(doc) # Create a reference point (requires a transaction) refPt = doc.FamilyCreate.NewReferencePoint(XYZ(0, 0, 0)) # "End" the transaction TransactionManager.Instance.TransactionTaskDone()
You may also use RevitAPI Sub-Transactions alongside (but not as a substitute for) the
TransactionManager. Sub-Transactions will give you the ability to rollback your changes. There is no way to rollback the main Dynamo Transaction.
Passing Functions to Python
Currently, passing functions to Python scripts through Dynamo is not supported in 0.7.x. This capability will be returning some time in the future.
Passing Python Nodes as Functions
Currently, passing Python nodes to other nodes as functions is not supported in 0.7.x. This capability will be returning some time in the future.