Waitfor Levels to be Created

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

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?

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

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 :sweat_smile:

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