MatchParams — MatchProperties for VisualARQ Document Parameters (Python)

I needed to quickly match and distribute certain parameters (manually) - in case anyone needs this.

MatchParams — MatchProperties for VisualARQ Document Parameters (Python)

I needed a quick way to copy Document Parameter values from one object to others — like MatchProperties, but for custom VA parameters. Since neither Rhino’s MatchProperties nor any built-in VA tool supports this, I wrote a script.

Key finding: Document Parameters (Rhino Options > Parameters) are managed by VisualARQ, not by Rhino’s UserText system. They are not accessible via rs.GetUserText/rs.SetUserText or RhinoCommon UserStrings. The correct API is VisualARQ.Script with GetAllDocumentParameterIds(), GetParameterValue(), and SetParameterValue().

The script has three modes:

  • Match (m): Pick a source object, select targets — all parameter values are copied. Values are stored in session memory.

  • Repeat (r): Apply the stored values to a new selection without picking a source again.

  • Show (s): Inspect parameter values of a picked object.

Works with any geometry type, no hardcoded parameter names — it reads whatever Document Parameters are defined in the document.

# -*- coding: utf-8 -*-
"""
MatchParams (MP)
Copy VisualARQ Document Parameter values from source to target objects.
Uses VisualARQ.Script API (GetParameterValue / SetParameterValue).

Modes:
  m = Match from source object
  r = Repeat last parameter set
  s = Show parameters of picked object

Alias: MP
"""

import rhinoscriptsyntax as rs
import scriptcontext as sc
import System
import clr

STICKY_PARAMS = "MP_LastParams"  # {param_name: value}

# --- VA Setup ---

def load_va():
    try:
        clr.AddReference("VisualARQ.Script")
    except:
        pass
    try:
        import VisualARQ.Script as va
        return va
    except:
        print("ERROR: VisualARQ.Script not available.")
        print("Run any VA command first (e.g. vaAbout), then retry.")
        return None


def get_doc_params(va):
    """Get all document parameter IDs and names."""
    param_ids = va.GetAllDocumentParameterIds()
    if not param_ids:
        return []
    params = []
    for pid in param_ids:
        name = va.GetParameterName(pid)
        params.append((pid, name))
    return params


# --- Core Functions ---

def read_params(va, obj_id, params):
    """Read all document parameter values from an object."""
    guid = System.Guid(str(obj_id))
    result = {}
    for pid, name in params:
        try:
            val = va.GetParameterValue(pid, guid)
            result[name] = (pid, val)
        except:
            result[name] = (pid, None)
    return result


def write_params(va, obj_id, param_values):
    """Write parameter values to an object. param_values: {name: (pid, val)}"""
    guid = System.Guid(str(obj_id))
    ok = 0
    for name, (pid, val) in param_values.items():
        try:
            va.SetParameterValue(pid, guid, val)
            ok += 1
        except Exception as e:
            print("  WARN: {} -> {}".format(name, e))
    return ok


def show_params(va, obj_id, params, label="Object"):
    """Print parameter values for an object."""
    data = read_params(va, obj_id, params)
    has_values = False
    print("--- {} Parameters ---".format(label))
    for name in sorted(data.keys()):
        pid, val = data[name]
        display = val if val is not None else "(empty)"
        print("  {} = {}".format(name, display))
        if val is not None and str(val).strip():
            has_values = True
    print("")
    return data, has_values


# --- Modes ---

def mode_match(va, params):
    """Pick source, read params, pick targets, write params."""
    source = rs.GetObject("Pick SOURCE object (with parameter values)", preselect=False)
    if not source:
        return

    data, has_values = show_params(va, source, params, "Source")
    if not has_values:
        print("Source has no parameter values set.")
        return

    # Filter to only params that have values
    valid = {k: v for k, v in data.items() if v[1] is not None and str(v[1]).strip()}

    # Store for repeat
    sc.sticky[STICKY_PARAMS] = valid

    targets = rs.GetObjects("Select TARGET objects")
    if not targets:
        return

    apply_to_targets(va, targets, valid)


def mode_repeat(va, params):
    """Apply last stored parameter set to new targets."""
    if STICKY_PARAMS not in sc.sticky or not sc.sticky[STICKY_PARAMS]:
        print("No stored parameters. Run Match (m) first.")
        return

    valid = sc.sticky[STICKY_PARAMS]
    print("--- Stored Parameters ---")
    for name in sorted(valid.keys()):
        pid, val = valid[name]
        print("  {} = {}".format(name, val))
    print("")

    targets = rs.GetObjects("Select TARGET objects for repeat")
    if not targets:
        return

    apply_to_targets(va, targets, valid)


def mode_show(va, params):
    """Show parameters of picked object."""
    obj = rs.GetObject("Pick object to inspect", preselect=True)
    if not obj:
        return
    show_params(va, obj, params, "Inspected")


def apply_to_targets(va, targets, valid):
    """Apply parameter values to target objects."""
    count = 0
    for t in targets:
        n = write_params(va, t, valid)
        if n > 0:
            count += 1

    print(">> {} parameter(s) applied to {} object(s):".format(len(valid), count))
    for name in sorted(valid.keys()):
        pid, val = valid[name]
        print("   {} = {}".format(name, val))


# --- Main ---

def main():
    va = load_va()
    if not va:
        return

    params = get_doc_params(va)
    if not params:
        print("No Document Parameters defined.")
        print("Create them in Rhino Options > Parameters first.")
        return

    print("Document Parameters: {}".format(
        ", ".join(name for _, name in params)
    ))

    stored = sc.sticky.get(STICKY_PARAMS, {})
    info = " [{} stored]".format(len(stored)) if stored else ""

    mode = rs.GetString(
        "Mode: m=Match, r=Repeat{}, s=Show".format(info),
        "m"
    )
    if not mode:
        return

    mode = mode.strip().lower()

    if mode == "m":
        mode_match(va, params)
    elif mode == "r":
        mode_repeat(va, params)
    elif mode == "s":
        mode_show(va, params)
    else:
        print("Unknown mode '{}'. Use m, r, or s.".format(mode))


if __name__ == "__main__":
    main()

PS: I let the AI write the text… I am lazy

1 Like