I have a Python Script node where I use the Copy function from Revit to copy/paste elements in my project.
What I would like to get is the copied elements in my script. But I have some issues.
Here is the code :
import clr
import time
clr.AddReference("RevitAPIUI")
from Autodesk.Revit.UI import RevitCommandId
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
# Get all elements in the document before the copy operation
collector = FilteredElementCollector(doc)
existing_elements = set(collector.WhereElementIsNotElementType().ToElementIds())
# Start a transaction
TransactionManager.Instance.EnsureInTransaction(doc)
# Perform the copy operation
CmndID = RevitCommandId.LookupCommandId("ID_EDIT_MOVE_COPY")
uiapp.PostCommand(CmndID)
# Commit the transaction
TransactionManager.Instance.TransactionTaskDone()
# Wait for the command to complete
time.sleep(1) # Adjust the sleep time as necessary
# Get all elements in the document after the copy operation
new_collector = FilteredElementCollector(doc)
new_elements = set(new_collector.WhereElementIsNotElementType().ToElementIds())
# Determine the copied elements by finding the difference
copied_elements = new_elements - existing_elements
# Convert the copied elements to Dynamo types and output them
output = [doc.GetElement(id).ToDSType(True) for id in copied_elements]
# Return the copied elements
OUT = output
The thing with this code, is that the new_elements list is exactly the same as the existing_elements, even with the classes TransactionManager and Time.
Is is possible, without launching the code twice, to have two different lists so the copied_elements list is not empty ?
Dynamo wraps itself in a transaction to be executed in Revit. This essentially means that everything you do in Dynamo happens âall at onceâ and then gets committed to Revit.
Your python transaction is nested inside this Dynamo transaction, so the list of new elements is returned before the Dynamo transaction finishes and the changes have actually been committed to the Revit model. You need to introduce a new transaction at the Dynamo level so that Dynamo executes your logic in two stages - once when running the command and once after. All you should need to do is split your python into two nodes and add a Transaction node in between (Python: execute command > Dynamo: commit transaction > Python: get new elements).
Iâve tried several things, but nothing worked. Both lists have the same amount of elements.
Can you tell me what is wrong with my nodes ?
First node :
# Get all elements in the document before the copy operation
collector = FilteredElementCollector(doc)
existing_elements = set(collector.WhereElementIsNotElementType().ToElementIds())
CmndID = RevitCommandId.LookupCommandId("ID_EDIT_MOVE_COPY")
uiapp.PostCommand(CmndID)
OUT = existing_elements
t = Transaction(doc, 'CopyElements')
t.Start()
t.Commit()
Third node :
# Get all elements in the document after the copy operation
new_collector = FilteredElementCollector(doc)
new_elements = set(new_collector.WhereElementIsNotElementType().ToElementIds())
# Return the copied elements
OUT = new_elements
All of my nodes contain this at the beginning :
import clr
import sys
import time
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import System
from System import Array
from System.Collections.Generic import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument
Hmm. It looks like Revit wonât let you âinterruptâ a transaction with a postable command, even if that transaction has finished.
Whatâs the reasoning for using a postable command instead of an alternative that could be automated? Does the copied placement have a known location?
EDIT: More information on what might be happening. It sounds like postable commands donât finish until after execution anyway, regardless of subtransactions.
When you use the Copy function in Revit in a Collaboration Model with Worksets, the elements that are copied are stored to the active workset.
I would like to create a script that retrieves the workset(s) stored in the selected elements before the copy and once this is done, the script associate the right workset(s) to the copied elements (like AutoCAD does with the Copy function with the blocks and layers).
Iâve managed to retrieve the worksets on selected elements but not the final part on the script (the Copy function to get the copied elements).
So you want to copy a set of elements while retaining the original associated workset. Are you also moving the copied elements or do they retain the same location as well?
But when you copy an element in the same model somewhere else in the proejct, the active workset is stored in the element and is mostly the time different from the workset that is actually stored in the existing element.
Ok - swap âsource documentâ for âactive documentâ and âtarget documentâ for âactive documentâ in my prior instructions. Rest of the process should stay the same.
It seems to be the solution of what Iâm looking for.
Iâll try this when I can and keep you posted.
Thank you
EDIT : I do not have the knowledge and the time to adapt my code on this way, unfortunatelly.
But, Iâve found another way to do this and it seems fine to me.