Docs UI Builder

UI Builder

Drag-and-drop designer for argument forms (.uibproj) and runtime dashboards (.uibrt). Build a real application around your Python macro without writing any frontend code.

Why a designer? A script's natural interface is the command line, which is invisible, error-prone, and gives users no progress feedback. The UI Builder lets your macro look like a real app while you only ever write Python. Inputs become typed Python arguments; live updates become one-line mas.ui.* calls.

The Two Project Types

A macro can have one, the other, both, or neither. They are independent files. Only the Python script (src/app.py) is required.

The Editor at a Glance

┌──────────────────────────────────────────────────────────────────────┐
│  Toolbar   Save · Export · Preview · Snap · Generate Args            │
├───────────┬──────────────────────────────────────────┬───────────────┤
│  Palette  │  Tabs:  [ General ] [ Advanced ] [+]     │  Inspector    │
│           │ ┌──────────────────────────────────────┐ │               │
│  Argument │ │                                      │ │  Properties:  │
│  Inputs   │ │            Canvas                    │ │  geometry,    │
│  Layout   │ │   (the window you are designing)     │ │  key, label,  │
│  Runtime  │ │   drag, move, resize widgets here    │ │  required,    │
│           │ └──────────────────────────────────────┘ │  default...   │
└───────────┴──────────────────────────────────────────┴───────────────┘
  • Palette (left) — every widget, grouped into Argument Inputs, Layout, and Runtime Display. Drag onto the canvas.
  • Canvas (center) — a scale model of the window. Widgets are positioned with absolute pixel coordinates.
  • Tabs (above the canvas) — a window can have several tabs (forms only).
  • Inspector (right) — every property of the selected widget plus its argument metadata.
  • Toolbar (top) — save, export, Preview (renders the real form), snap-to-grid, alignment, and Generate Args.

Argument Forms (.uibproj)

An argument form is a window of input fields. Each input field carries a key, and at run time MAS turns every field into a command-line argument the script reads.

The 11 Argument Widgets

Each produces one argument. Default sizes are in pixels; the canvas is 980 × 640 with 24 px padding, so the usable width is 932 px.

Widget Produces
Text Inputstring
Text Areastring
Number Inputint / float
Sliderint / float
Checkboxbool
Combo Boxchosen string
Radio Groupchosen string
File Pickerfile path
Directory Pickerfolder path
Date Pickerdate string
Time Pickertime string

Layout Widgets

These three add visual structure and produce no argument.

Label

Static helper text

Separator

A horizontal/vertical divider with an optional caption

Group Box

A titled container that groups related fields

Argument Metadata

Every argument widget has, in addition to its own options, a block of argument metadata in the Inspector.

key (required)The argument name. Must be a valid Python identifier (start with a letter or underscore, then letters/digits/underscores). Must be unique within the project. An argument widget with an empty key is silently skipped on export.
requiredIf on, the form will not let the user proceed without a value (and the generated argument is marked required=True).
helpTextA short explanation, surfaced as a tooltip / help string.
showLabel, labelPosition, labelGapControl whether and where the field's label is drawn (top or left).

From Form to Script — the key Contract

The key is the contract between the form and the script. Choose them deliberately and keep them stable.

Click Generate Args → writes src/script_args.py

from src.script_args import args

input_path = args.general.input_file # File Picker, key "input_file"
threads = args.general.thread_count # Number Input, key "thread_count"
dry_run = args.advanced.dry_run # Checkbox, key "dry_run"

Widget types map to Python types automatically:

WidgetGenerated asPython type
Checkboxaction='store_true'bool
Number Input / Slidertype=int (or float)int / float
Combo Box / Radio Grouptype=str, choices=[...]str
everything elsetype=strstr

Tabs

Group related fields onto separate tabs when a form gets long — a General tab for everyday options and an Advanced tab for the rest. Each tab becomes a Python namespace in the generated code (args.general.input_file). A tab's name is lowercased and slugged: "My Settings" becomes my_settings.

The Action Bar

Below the tabs sits the action bar — the row of buttons that closes the window. New projects start with Cancel + Run. Edit text, role (run / cancel / reset / custom), style (primary / secondary / danger), and alignment in the Inspector. For most macros the default is exactly right.

Validation

The UI Builder checks a project before letting you export. The rules that matter for argument forms:

  • The window must contain at least one tab.
  • Every widget id is unique across the project.
  • Every argument widget's key is non-empty and a valid identifier.
  • Argument keys are unique within the project.
  • Combo Box and Radio Group have at least one option.
  • Width and height are positive numbers.

What the UI Builder Produces

.uibproj  ──Generate Args──▶  src/script_args.py   (your script imports this)
   │
   └────────Export XML──────▶  ui.xml               (Studio renders this)
  • script_args.py — generated argparse code, grouped by tab, parses sys.argv on import. Never hand-edit it. Regenerate when the form changes.
  • ui.xml — the structural description Studio reads to render the input window. Export it whenever the form changes.

Runtime Dashboards (.uibrt)

A runtime dashboard is the live panel Studio shows next to a device while a macro runs. You design it visually here (a .uibrt file). You drive it from your script with mas.ui.* calls.

The one rule that connects everything: widget names. Every widget on the dashboard has a name (set in the UI Builder Inspector). That name is the only handle your script has on the widget. A typo between the .uibrt name and the mas.ui.* call is the most common dashboard bug — the call simply updates nothing.

Make each name a valid Python identifier (snake_case: status_label, items_chart), unique within the dashboard, and named for meaning, not type.

Display Widgets — the script writes, the user reads

Widget Shows Driven by
Runtime Labela line of textset_text
Progress Barprogress min → maxset_progress
Chartline / bar / pie chartadd_data_point, set_chart_data
Text Areascrolling text / logappend_text, set_textarea
Tablerows under fixed columnsset_table_data, append_table_row

Interactive Widgets — the user acts, the script reads

Widget Is Read with
Buttona clickable buttonwait_for_event · on_click
Text Inputa field the user types intoget_input_value · on_change

From Dashboard to Script — the name Contract

Every widget's name is the first argument to its corresponding mas.ui.* call:

# A Runtime Label named "status_label"
mas.ui.set_text("status_label", "Logging in…")

# A Progress Bar named "download_bar"
mas.ui.set_progress("download_bar", 60)

# A Button named "stop_btn" — Model B (callback)
mas.ui.on_click("stop_btn", handle_stop)
mas.ui.start_listener()

Full mas.ui.* reference: API Reference → Runtime UI.

Batch Updates for Smooth Refreshes

Each mas.ui.* call is one round-trip to Studio. Wrap a multi-widget refresh in mas.ui.batch() so they send as a single call. It is the difference between a smooth dashboard and one that flickers behind a tight loop.

with mas.ui.batch():
mas.ui.set_text("status", f"Step {i}/{n}")
mas.ui.set_progress("main_bar", i * 100 // n)
mas.ui.add_data_point("rate", value=rate, label=str(i))

Checklists Before You Ship

Argument form (.uibproj)

  • Every argument widget has a non-empty, identifier-safe, unique key.
  • Required fields are marked required.
  • Combo Box / Radio Group widgets have their options filled.
  • Number Input / Slider have sensible min / max.
  • You ran Preview and the form is legible.
  • Generate Args so script_args.py is current.
  • Export XML so ui.xml is current.

Runtime dashboard (.uibrt)

  • Every widget has a meaningful, identifier-safe, unique name.
  • Every mas.ui.* call uses a name that exists in the .uibrt.
  • Table row dict keys match the table's columns.
  • Multi-widget refreshes are wrapped in mas.ui.batch().
  • start_listener() is called once, after registering all handlers.
  • The macro still completes if the user never touches an interactive widget.

Layout Tips

  • Work within 932 px. The canvas is 980 px wide with 24 px padding each side. Wide widgets default to the full width.
  • Stack top to bottom. Forms read as a vertical list. Use the toolbar's align/distribute/stack tools to keep edges and gaps tidy.
  • Group with intent. A Group Box or captioned Separator does more for readability than careful pixel nudging.
  • Preview often. The Preview button renders the real form — the fastest way to catch a cramped layout.
  • Keep tabs shallow. Two or three tabs is plenty; more usually means the macro is trying to do too much.

Next Steps