Skip to content

Python 0.6.3 to 0.7.x Migration

Steve Elliott edited this page Jun 18, 2014 · 21 revisions

Input Variable(s)

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[0], the second is IN[1], 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[0]

This will allow the rest of your code to continue using the IN variable.

RevitAPI

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

The Revit Document can be accessed via the Dynamo DocumentManager.

import clr

# Import DocumentManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

Elements

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 Revit.Elements namespace:

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[0]
endRefPt = IN[1]
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[0] )
endRefPt = UnwrapElement( IN[1] )

# 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)

Unwrapping

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[0]
unwrappedElement = UnwrapElement( wrappedElement )
# Now I can use 'unwrappedElement' with the RevitAPI

Wrapping

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
]

Transactions

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.

  • 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 TransactionManager.ForceCloseTransaction().

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.

Releases

Roadmap

How To

Dynamo Internals

Contributing

Python3 Upgrade Work

Libraries

FAQs

API and Dynamo Nodes

Clone this wiki locally