Hello,
I am trying to create an assembly instance in Dynamo Python. I am using ironpython, but cpython3 would also be acceptable. Here is the code:
import System
import clr
# dynamo transaction
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# revit dlls
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI.Selection import *
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
if IN[1]: # boolean node
# unwrap dynamo elements to revit elements
el_L = UnwrapElement( IN[0] )
# convert el_id python List to .NET list
el_id_L = [el.Id for el in el_L]
el_id_NET_L = System.Collections.Generic.List[ElementId](el_id_L)
# get catergory of the assembly instance
cat = uiapp.ActiveUIDocument.Document.Settings.Categories.get_Item('Generic Models')
# check if category can be used
if (not AssemblyInstance.IsValidNamingCategory(doc, cat.Id, el_id_NET_L)):
raise ValueError('"{}" category can not be used for the input els.'.format(cat.Name) )
# first transaction - create assembly
TransactionManager.Instance.EnsureInTransaction( doc )
assembly = AssemblyInstance.Create(doc, el_id_NET_L, cat.Id)
doc.Regenerate()
TransactionManager.Instance.TransactionTaskDone()
# second transaction - modify assembly name
TransactionManager.Instance.EnsureInTransaction( doc )
assembly.AssemblyTypeName = "Assembly Random Name" # THIS LINE RAISES AN ERROR: 'Exception: No valid type for the assembly instance.'
TransactionManager.Instance.TransactionTaskDone()
The code creates an assembly instance, but when it gets to the line: assembly.AssemblyTypeName = "Assembly Random Name" it raises an error: Exception: No valid type for the assembly instance.
Thank you for the reply @Hyunu_Kim ,
But I didnât understand your reply. Are you suggestion the last lines from the upper code to look like this:
# second transaction - modify assembly name
TransactionManager.Instance.EnsureInTransaction( doc )
item = assembly
n = "Assembly Random Name"
item.AssemblyTypeName = n #Set name of item as n
TransactionManager.Instance.TransactionTaskDone()
I just tried - it raises the same error: No valid type for the assembly instance.
import sys
import clr
import System
from System.Collections.Generic import List
# dynamo transaction
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# revit dlls
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI.Selection import *
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
if IN[1]: # boolean node
# unwrap dynamo elements to revit elements
el_L = UnwrapElement( IN[0] )
TransactionManager.Instance.ForceCloseTransaction()
# convert el_id python List to .NET list
el_id_NET_L = List[ElementId]([el.Id for el in el_L])
bic = BuiltInCategory.OST_GenericModel
bic_Id = ElementId(bic)
# check if category can be used
if (not AssemblyInstance.IsValidNamingCategory(doc, bic_Id, el_id_NET_L)):
raise ValueError('"{}" category can not be used for the input els.'.format(DB.LabelUtils.GetLabelFor(bic)) )
tg = DB.TransactionGroup(doc, "create assembly")
tg.Start()
#
with DB.Transaction(doc) as t1:
t1.Start("create assembly")
assembly = AssemblyInstance.Create(doc, el_id_NET_L, bic_Id)
t1.Commit()
#
with DB.Transaction(doc) as t2:
t2.Start("change Name")
assembly.AssemblyTypeName = "Assembly Random Name"
t2.Commit()
tg.Assimilate()
Hi @c.poupin ,
Thank you very much for posting the solution!
So the dynamo transactions I posted in my code are not 2 separate transactions? (I also tried with Revit AP transactions, and it didnât work). You meant: I need to create a transaction group from two separate transactions?
Can you give me an advice, when I should make this transaction group? Is there some some general rule? And when it the group is not necessary, and I can just use two transactions.
Hi @c.poupin ,
Thank you again for the reply. I tried replacing the Dynamo transactions with Revit API, and still got the same error. So it must be that fact that they have not been grouped, which resulted in the raised error.
Can you give me an advice, when I should make the transaction group?
Thank you very much once again for the shared knowledge @c.poupin .
When I run the code the same way you showed (inside a python node), it works, without issues.
Interestingly, when I make a function from your code (def Assembly) and save the function in a dynamo.py file, and import the file into dynamo python node with:
import sys
sys.path.append('c:\revit\python')
import dynamo
the following error is raised:
Transaction group cannot be started during an active transaction.
But when I rerun the python node again, then it works.
So on the first run, it always raises the upper error, and the afterwards it works.
I donât understand what is the issue. In that python node, I am literally only calling the dynamo.Assembly() function, nothing else. So there shouldnât be any other transactions started.
The dynamo.py looks exactly as your code, I just replaced the if IN[1] with def Assembly, thatâs all.
Hi @c.poupin ,
It now works, even on the first run of the python! Thank you.
But if I click on âUndoâ button in Revit (to undo the creation of the assembly and remove it from the âProject Browserâ) - and if run the Python node again (I add just # to trick the Python node into thinking that the code changed), the following error appears:
One or more element ids was not permitted for membership in the assembly instance. Elements should be of a valid category and should not be a member of an existing assembly.
Parameter name: assemblyMemberIds
The problem goes away if I follow your principle: of closing the .dyn file after each run of the Python node. And opening the .dyn file again and rerunning it.
I donât really understand why this happens.
Thank you for the new explanation about this @c.poupin !
I consider your method on solving this issue, much better (faster):
âRunâ the 'dyn file to create an assembly via Python node
click on âUndoâ button in Revit, to remove the assembly
in Dynamo choose: âFile->Open Recent Filesâ and choose the top most (latest one). When asked if I want to save the current file: choose âNoâ.
âRunâ the .dyn file to create an assembly again for the same inputs
The method from Jasonâs topic:
âRunâ the 'dyn file to create the assembly via Python node
click on âUndoâ button in Revit, to remove the assembly
Disconnect âSelect Model Elementsâ node from the python node
âRunâ the .dyn file
âSaveâ the .dyn file
Connect the âSelect Model Elementsâ node to the python node
âSaveâ the .dyn file
âRunâ the .dyn file to create an assembly again for the same inputs
The only part which confuses me from that topic is:
Custom nodes and python based nodes will not necessarily work this way.
For python nodes youâll either need to revise how transactions and elements are handled in your code, or wrap it in a custom node.
hello I tried the code and it does not work, it indicates expected BuiltinparameterGroup, got BuiltinCategory. I am using Revit 2019 with Dynamo 2.0.4.
The code only works for GenericModels because of âbic = BuiltInCategory.OST_GenericModelâ
with a small change âbic = el_L[0].Category.Id.IntegerValueâ you can used it for different categories.
But if I want to assemble elements from different categories (letâs say one generic model and a wall) it wonât work.
If not mistaken, the way assemblies are created, if you input two different elements, you will get two different assemblies. So I would look into incorporating a second selection method and use the Assembly.AddMembers node from clockwork.
If you have additional questions, please start a new thread and reference this thread in your post. That way will warrant more input from others.