rxylab
April 11, 2025, 9:13pm
1
Hi all,
I am using the passthrough/waitfor logic and it’s not working how I thought it would.
The scenario is that I want to wait for levels to be created and then those new levels can be assigned an appropriate parameter change. I have done the below:
As you can see above, the final python node cannot see the newly created levels, but they have been created after the whole graph has finished.
In all honesty, this is driving me crazy. I would appreciate any input.
Thanks.
Hard to say without seeing your python.
Try a doc.Regenerate() as you enter the python to make sure the database is up–to-date.
1 Like
Add a Transaction.End and a Transaction.Start node after the level creation but before setting the parameter.
1 Like
rxylab
April 11, 2025, 11:54pm
4
Thanks both.
I just tried the transaction nodes and it didn’t work, unless I positioned them incorrectly.
File attached.
Create Levels from Revit Link 1.0.1.dyn (48.4 KB)
i found that using a dummy python node as a wait-for is more deterministic. and alternatively u could control them nicely by putting these five nodes in the same python node as well.
and what about link binding?
rxylab
April 12, 2025, 7:39am
6
Thanks for the suggestions. Have you got an example for a dummy python node acting as wait-for?
I have tried this, for example having a confirmation UI button to confirm the action and then it moves onto next node for filling parameters but it hasn’t worked as I thought. This dummy is in the script I have provided.
@rxylab might not be the perfect example, but lets say i create a wall and another thing im doing is to collect all walls. (given an empty file, no walls or anything.) as u could see, obviously the collector finish its execution first. this is not always true though, creation could finish first as well.
so i add an in-between python node which pretty much does nothing but “wait-for” the creation node and “passthrough” a category.
elems = IN[0]
OUT = IN[1]
Hi @rxylab
Here is a quick fix for the last Python node (see comments)
import clr
clr.AddReference("RevitAPI")
clr.AddReference("RevitServices")
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")
import System.Windows.Forms as WinForms
from System.Drawing import Point as WinPoint, Size as WinSize, Font
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
# === INPUTS
level_names = [l.Name for l in IN[0]] # FIX 1 need a list of string not of Element Level
continue_flag = IN[1]
debug_log = []
updated_levels = []
if not continue_flag:
# === Cancelled by user earlier
cancel_form = WinForms.Form()
cancel_form.Text = "Workset Assignment Skipped"
cancel_form.Size = WinSize(500, 200)
cancel_form.StartPosition = WinForms.FormStartPosition.CenterScreen
msg = WinForms.TextBox()
msg.Multiline = True
msg.ReadOnly = True
msg.Font = Font("Consolas", 10)
msg.Size = WinSize(460, 100)
msg.Location = WinPoint(10, 10)
msg.Text = "⏸ Workset assignment skipped.\nLevel confirmation was cancelled."
cancel_form.Controls.Add(msg)
ok_btn = WinForms.Button()
ok_btn.Text = "OK"
ok_btn.Size = WinSize(100, 30)
ok_btn.Location = WinPoint(200, 120)
ok_btn.DialogResult = WinForms.DialogResult.OK
cancel_form.AcceptButton = ok_btn
cancel_form.Controls.Add(ok_btn)
cancel_form.ShowDialog()
OUT = ([], ["⏸ Workset assignment skipped."])
else:
# === Find workset
target_workset = None
collector = FilteredWorksetCollector(doc).OfKind(WorksetKind.UserWorkset)
for ws in collector:
if "MEP Shared Levels" in ws.Name:
target_workset = ws
break
if not target_workset:
debug_log.append("❌ No workset found containing 'MEP Shared Levels'.")
else:
# === Match levels by name
TransactionManager.Instance.EnsureInTransaction(doc)
all_levels = FilteredElementCollector(doc).OfClass(Level).ToElements()
name_to_level = {lvl.Name: lvl for lvl in all_levels}
for name in level_names:
if name in name_to_level:
lvl = name_to_level[name]
try:
param = lvl.get_Parameter(BuiltInParameter.ELEM_PARTITION_PARAM)
if param and not param.IsReadOnly:
param.Set(target_workset.Id.IntegerValue) # FIX 2 need a integervalue instead of elementId
debug_log.append("✓ Level: {:<30} Elev: {:>8.2f} → {}".format(
lvl.Name, lvl.Elevation, target_workset.Name))
updated_levels.append(lvl)
else:
debug_log.append("❌ Level: {:<30} — Cannot set workset.".format(lvl.Name))
except Exception as e:
debug_log.append("❌ Level: {:<30} — {}".format(lvl.Name, str(e)))
else:
debug_log.append("❌ Level not found: '{}'".format(name))
TransactionManager.Instance.TransactionTaskDone()
# === Show debug
log_form = WinForms.Form()
log_form.Text = "Workset Assignment Results"
log_form.Size = WinSize(800, 450)
log_form.StartPosition = WinForms.FormStartPosition.CenterScreen
textbox = WinForms.TextBox()
textbox.Multiline = True
textbox.ScrollBars = WinForms.ScrollBars.Vertical
textbox.ReadOnly = True
textbox.Font = Font("Consolas", 9)
textbox.Size = WinSize(760, 340)
textbox.Location = WinPoint(10, 10)
textbox.Text = "\r\n".join(debug_log)
log_form.Controls.Add(textbox)
ok_btn = WinForms.Button()
ok_btn.Text = "OK"
ok_btn.Size = WinSize(100, 30)
ok_btn.Location = WinPoint(340, 360)
ok_btn.DialogResult = WinForms.DialogResult.OK
log_form.AcceptButton = ok_btn
log_form.Controls.Add(ok_btn)
log_form.ShowDialog()
OUT = (updated_levels, debug_log)
Transaction.End + Transaction.Start are not necessarily required, you can use doc.regenerate()
instead
1 Like
rxylab
April 12, 2025, 4:08pm
9
Thanks so much. So it wasn’t a problem with order of actions but needing a list of strings instead of element and needing the workset id integer. I couldn’t see the wood for the trees I’d been looking at this for so long
I will look at using doc.regenerate() for other processes. I know @aaronrumple mentioned it too but I haven’t had a chance to look at it properly yet.
Thanks to everyone.
3 Likes