Python Script - Setting Parameters Creates Weird Overwrites

I have some python code that correctly creates a line based detail component at the specified location.

for rebar in rebarData:txt = rebar[0] if isinstance(rebar, (list, tuple)) else rebar #location of the rebarquant = rebar[1] #to be set to correct parametersize = rebar[2] #to be set to correct parameter
xyz_points = parse_to_xyz_pairs(txt)
if len(xyz_points) >= 2:
    start_pt = xyz_points\[-2\]
    end_pt   = xyz_points\[-1\]
    revit_line = Line.CreateBound(start_pt, end_pt)
    revitLines.append(revit_line.Length)
    try:
        fi = doc.Create.NewFamilyInstance(revit_line, fs, view)

The problem comes in when try to set parameter values like strings and integers to the created elements. When setting the parameters, they are correctly set, but it will overwrite the length/location of the detail component for some reason I can not pinpoint. Sample of setting the parameters is shown below.

def set_param(elem, param_name, value):
“”“Safely set a parameter by name, with type handling.”“”p = elem.LookupParameter(param_name)if p and not p.IsReadOnly:try:if p.StorageType == StorageType.String:return p.Set(str(value))elif p.StorageType == StorageType.Double:
# Convert inches to internal feet if number looks like a dimensionval_ft = float(value) / 12.0 if abs(float(value)) > 3 else float(value)return p.Set(val_ft)elif p.StorageType == StorageType.Integer:return p.Set(int(value))elif p.StorageType == StorageType.ElementId:return p.Set(ElementId(int(value)))except:passreturn False

quant_param = “Rebar Quantity”                      #stored as a stringrebar_size_param = “Rebar Size”                     #stored as a string

for rebar in rebarData:txt = rebar[0] if isinstance(rebar, (list, tuple)) else rebar #location of the rebarquant = rebar[1] #to be set to correct parametersize = rebar[2] #to be set to correct parameter
xyz_points = parse_to_xyz_pairs(txt)
if len(xyz_points) >= 2:
    start_pt = xyz_points\[-2\]
    end_pt   = xyz_points\[-1\]
    revit_line = Line.CreateBound(start_pt, end_pt)
    revitLines.append(revit_line.Length)
    try:
        fi = doc.Create.NewFamilyInstance(revit_line, fs, view)
        
        # --- Set parameters ---
        if quant: set_param(fi, quant_param, quant)
        if size:  set_param(fi, rebar_size_param, size)

Once I add the set_param function to the loop, the detail components bug out and get repositioned.

The dynamo graph works if I use the created elements and then use the Element.SetParameterByName node outside of the python node.

Any ideas and what is going wrong?

We can only guess as you haven’t provided full context. But as a guess… is there only one transaction in the execution, or are you using a transaction group or two transactions?

There is only one transaction in the execution.

Would making a transaction group solve the issue?

Full code if you would like to review.


# Revit: Place rebar detail component along given coordinates
import clr, re

# Revit API imports
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# ---------- Inputs ----------
rebarData = IN[0]                       # list of strings or [string] lists
inp_elem  = UnwrapElement(IN[1])        # FamilySymbol OR Family for the detail component
roundingDimension = IN[2]               # Round to the nearest x inches

# ---------- Revit context ----------
doc   = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
view  = uidoc.ActiveView

# ---------- Helpers ----------
def get_familysymbol_from_input(elem):
    """Return a FamilySymbol from either a FamilySymbol or Family."""
    if isinstance(elem, FamilySymbol):
        return elem
    if isinstance(elem, Family):
        ids = list(elem.GetFamilySymbolIds())
        if ids:
            return doc.GetElement(ids[0])
    return None

def ensure_symbol_active(fs):
    """Activate the symbol if needed."""
    if hasattr(fs, "IsActive") and not fs.IsActive:
        fs.Activate()
        doc.Regenerate()

def parse_to_xyz_pairs(text):
    """Return list of XYZ points parsed from text like '(32.64,-134.7)'."""
    pairs = re.findall(r'\((-?\d*\.?\d+),\s*(-?\d*\.?\d+)\)', text)
    return [XYZ(float(x), float(y), 0.0) for x, y in pairs]

def set_param(elem, param_name, value):
    """Safely set a parameter by name, with type handling."""
    p = elem.LookupParameter(param_name)
    if p and not p.IsReadOnly:
        try:
            if p.StorageType == StorageType.String:
                return p.Set(str(value))
            elif p.StorageType == StorageType.Double:
                # Convert inches to internal feet if number looks like a dimension
                val_ft = float(value) / 12.0 if abs(float(value)) > 3 else float(value)
                return p.Set(val_ft)
            elif p.StorageType == StorageType.Integer:
                return p.Set(int(value))
            elif p.StorageType == StorageType.ElementId:
                return p.Set(ElementId(int(value)))
        except:
            pass
    return False
    
def round_up_length_ft(length_ft, rounding_inches):
    """
    Round UP a length in feet to the nearest rounding increment in inches.
    e.g., length_ft=11.5, rounding_inches=12 -> 12.0 ft
    """
    try:
        ri = float(rounding_inches)
    except:
        ri = 0.0
    if ri <= 0:
        return length_ft  # no rounding if invalid
    # convert to inches, ceil to increment, back to feet
    inches = length_ft * 12.0
    steps = math.ceil(inches / ri)
    return (steps * ri) / 12.0
# ----------- Parameters -----------------------------------------------------

quant_param = "Rebar Quantity"                      #stored as a string
rebar_size_param = "Rebar Size"                     #stored as a string
rebar_length_param = "Rebar Length"                 #stored as a float, in feet
top_bot_param = "Top Bar?"                          #stored as a bool
top_bar_left_hook_param = "Top Bar (Left Hook)"     #stored as a bool
top_bar_right_hook_param = "Top Bar (Right Hook)"   #stored as a bool
bot_bar_left_hook_param = "Bot Bar (Left Hook)"     #stored as a bool
bot_bar_right_hook_param = "Bot Bar (Right Hook)"   #stored as a bool

# ---------- Main ------------------------------------------------------------

fs = get_familysymbol_from_input(inp_elem)



if fs is None:
    OUT = ["❌ Could not resolve a FamilySymbol from input.", None]
else:
    instances = []
    TransactionManager.Instance.EnsureInTransaction(doc)
    ensure_symbol_active(fs)
revitLines = []
for rebar in rebarData:
    txt = rebar[0] if isinstance(rebar, (list, tuple)) else rebar #location of the rebar
    quant = rebar[1] #to be set to correct parameter
    size = rebar[2] #to be set to correct parameter
    
    xyz_points = parse_to_xyz_pairs(txt)
    if len(xyz_points) >= 2:
        start_pt = xyz_points[-2]
        end_pt   = xyz_points[-1]
        revit_line = Line.CreateBound(start_pt, end_pt)
        revitLines.append(revit_line.Length)
        try:
            fi = doc.Create.NewFamilyInstance(revit_line, fs, view)
            
            # --- Set parameters ---
            #if quant: set_param(fi, quant_param, quant)
            #if size:  set_param(fi, rebar_size_param, size)
            
            # -- Compute raw length (feet), round UP to nearest rounding_in (inches), set parameter in feet
            #raw_len_ft = revit_line.Length  # Revit internal units are feet
            #rounded_len_ft = round_up_length_ft(raw_len_ft, rounding_in)
            #set_param(fi, rebar_length_param, rounded_len_ft)
            
            instances.append(fi)
        except Exception as e:
            instances.append("⚠️ Failed to place: {}".format(e))
    else:
        instances.append("⚠️ Not enough points parsed to make a line.")

TransactionManager.Instance.TransactionTaskDone()
OUT = ["âś… Finished", instances, revitLines]

I was able to solve this by modifying the base detail component that I believe was improperly made.

When interacting with the detail component in a project, there would be times where the element would become unstable and snap oddly. Rebuilding the detail component seems to have solved the issue.

1 Like

Glad you are sorted out, so no need to pull this thread. However some parameters are not able to be directly set in the same action in which the element is created. This is a restriction of the Revit API in order to keep your mode secure and stable. Some families (often with a somewhat broken nature) get into positions where similar restrictions apply.

1 Like