Studio
Macro Argument Form and Runtime Dashboard: UI Builder
Design a macro argument form (.uibproj) and runtime dashboard (.uibrt) in the MAS UI Builder, export ui.xml and script_args.py, and drive widgets from Python.
- Windows
- Mac
- Emulator
- Cloud device
- Studio
- Python SDK
On this page
- The two project types
- Open the UI Builder from the Code Editor
- The editor at a glance
- The macro argument form (.uibproj)
- The 11 argument widgets
- Layout widgets
- Argument metadata
- Validation
- The action bar
- What the form exports
- Read the values in your script
- The native settings dialog on a device
- The runtime dashboard (.uibrt), driven from Python
- The 7 runtime widgets
- How updates reach the dashboard
- Checklists before you ship
- Layout tips
The UI Builder is the drag-and-drop designer that ships with Macro Automation Studio. It gives a Python macro two things a command line cannot: a typed argument form the user fills in before a run, and a live runtime dashboard that shows progress while the run happens. You place widgets on a canvas; MAS turns the form into Python arguments and turns the dashboard into one-line mas.ui.* calls. This page is for anyone who has a working script and wants to make it usable by other people.
The two project types
| Project | File | What it is | Widgets |
|---|---|---|---|
| Argument form | .uibproj | The window the user fills in before the macro runs | 11 argument widgets, 3 layout widgets, an action bar |
| Runtime dashboard | .uibrt | The live panel shown while the macro runs | 7 runtime widgets |
A macro can have one, the other, both, or neither. They are separate files in the project folder. Only the script itself, src/app.py, is required.
Open the UI Builder from the Code Editor
The UI Builder is a separate application that MAS launches for you. It is part of the standard install from the download page, so there is nothing extra to set up. You never start it by hand.
- Open your macro in the Code Editor.
- In the toolbar, click Add Args UI to design an argument form, or Add Runtime UI to design a dashboard.
- If the project has no such file yet, MAS creates
script_runner.uibprojorruntime_ui.uibrt, saves it to the project, and then opens the UI Builder on it. - Once a file exists, the same buttons read Edit Args UI and Edit Runtime UI.
The UI Builder runs as a single instance. If it is already open, clicking a button switches it to the file you asked for. When a .uibrt exists, the Code Editor’s bottom panel gains a runtime tab next to the console, with a refresh control that re-reads the file after you edit it.
The editor at a glance
- Palette (left): every widget, grouped into argument, layout and runtime kinds. Drag one onto the canvas.
- Canvas (center): a scale model of the window. Widgets have absolute pixel positions and sizes. A 10 pixel snap grid is on by default.
- Tabs (above the canvas): a form can have several tabs. Dashboards can use tabs as well.
- Inspector (right): every property of the selected widget, including its argument key or its runtime name.
- Toolbar (top): the snap grid toggle, an auto-layout button for the active tab, Preview, Save and Save As.
Save always writes the project file. For an argument form it also exports ui.xml and src/script_args.py, but only when validation passes. If there are issues, the project is saved and the exports are skipped until you fix them.
The macro argument form (.uibproj)
An argument form is a window of input fields. Each field carries a key. At run time MAS turns every field into a --key=value argument that your script reads.
The 11 argument widgets
Each one produces exactly one argument.
| Widget | Kind | Value in Python | Use for |
|---|---|---|---|
| Text Input | text-input | str | names, URLs, short free text |
| Text Area | text-area | str | long text, lists, pasted blocks; you set the rows |
| Number Input | number-input | int or float | counts and thresholds, with min, max, step, precision and a suffix |
| Slider | slider | int or float | a bounded number chosen by dragging; can show its value |
| Checkbox | checkbox | bool | on or off flags |
| Combo Box | combobox | one option’s str | pick one from a list; can be editable |
| Radio Group | radio-group | one option’s str | pick one when every option should be visible |
| File Picker | file-picker | str path | choose a file, with optional filters such as *.txt;*.csv |
| Directory Picker | directory-picker | str path | choose a folder |
| Date Picker | date-picker | str | a date in the format you set, for example YYYY-MM-DD |
| Time Picker | time-picker | str | a time as HH:mm, HH:mm:ss, hh:mm A or hh:mm:ss A |
A Number Input or Slider becomes float when its step is not a whole number, otherwise int.
Layout widgets
These three add structure and produce no argument.
- Label: static helper text with an optional font size.
- Separator: a horizontal or vertical divider with an optional caption.
- Group Box: a titled container that groups related fields.
Argument metadata
Every argument widget has a block of metadata in the Inspector.
- key (required): the argument name. It must be a valid Python identifier (a letter or underscore, then letters, digits or underscores) and unique across the whole form.
- required: the form will not run without a value, and the generated argument is marked
required=True. - helpText: a short explanation that becomes the argument’s help string.
- showLabel, labelPosition (top or left) and labelGap: control how the field’s label is drawn.
Validation
Before exporting, the UI Builder checks that every argument widget has a key, that each key is a valid identifier, and that no key is used twice. A project must also contain at least one tab. Fix every issue, then click Save again to export.
The action bar
Below the tabs sits the row of buttons that closes the window. New forms start with Cancel and Run. In the Inspector you can edit each button’s text, role (run, cancel, reset or custom) and style (primary, secondary or danger), and set the bar’s alignment to right, center, left or spread. For most macros the default is right.
What the form exports
src/script_args.py: generated argparse code, grouped by tab. It parsessys.argvon import. Never hand-edit it; it is rewritten on every Save.ui.xml: the structural description Studio reads to render the native input dialog.
Read the values in your script
Each tab becomes a namespace. A tab’s name is lowercased and non-alphanumeric characters become underscores, so “My Settings” becomes my_settings. Access a value as args.<tab>.<key>.
import mas
from src.script_args import args
target = args.general.target_name # Text Input, key "target_name"
rounds = args.general.rounds # Number Input, key "rounds"
dry_run = args.advanced.dry_run # Checkbox, key "dry_run"
for i in range(rounds):
mas.log(f"round {i + 1} of {rounds}")
if dry_run:
continue
match = mas.find_object_retry(1234, total_tries=3, time_sleep=2.0)
if match:
mas.click(match.x, match.y)Combo Box and Radio Group values are restricted to their options with choices=[...]. A Checkbox uses action='store_true', so it is False unless ticked.
The native settings dialog on a device
Studio does not embed the UI Builder canvas at run time. It reads ui.xml and renders one native form, with the same tabs and fields, in two places:
- In the Code Editor, when you click Run on a script that has arguments.
- On a device card in Device Groups, behind the Macro settings button.
On a device card the values you enter are saved for that device, and a Profile bar at the top of the dialog lets you share one set of values across many devices. See Settings profiles. If a macro has no ui.xml, Studio falls back to reading the argparse definitions in script_args.py.
The runtime dashboard (.uibrt), driven from Python
A runtime dashboard is the live panel Studio shows while a macro runs. You design it in the UI Builder and drive it from your script with mas.ui.* calls. The dashboard appears in the Code Editor’s bottom panel and in the Dashboard view of the device card in Device Groups.
The one rule that connects everything is the widget name, set in the Inspector. That name is the first argument to every mas.ui.* call. A typo between the name in the file and the name in your code is the most common dashboard bug: the call simply updates nothing. Make each name a valid Python identifier, unique within the dashboard, and descriptive (status_label, items_chart).
The 7 runtime widgets
| Widget | Kind | Shows or does | Driven or read by |
|---|---|---|---|
| Label | runtime-label | one line of text with font size, color and alignment | set_text |
| Progress bar | runtime-progress | a value between min and max, optional percentage | set_progress, set_text for its label |
| Chart | runtime-chart | a line, bar or pie chart that keeps up to maxDataPoints | add_data_point, set_chart_data, clear_chart |
| Text area | runtime-textarea | scrolling text or a log, sans or mono, optional line numbers and maxLines | append_text, set_textarea, clear_text |
| Table | runtime-table | rows under fixed columns, optional maxRows | set_table_data, append_table_row, clear_table, cell_button |
| Button | runtime-button | a clickable button in the default, outline or destructive style | wait_for_event, on_click |
| Input | runtime-input | a text field the user types into | get_input_value, on_change |
The first five are display widgets: the script writes, the user reads. The last two are interactive: the user acts, the script reads.
How updates reach the dashboard
Every mas.ui.* call is one JSON-RPC message from your script to the MAS app. The app forwards it as an event keyed by the device, and the dashboard re-renders that widget. Text areas and tables keep a local mirror in the SDK so append_text and append_table_row can build on the previous content without asking the app. Wrap a multi-widget refresh in mas.ui.batch() so it travels as a single message; that is the difference between a smooth dashboard and one that flickers inside a tight loop. Batches cannot be nested.
import mas
steps = ["open_app", "collect", "claim"]
mas.ui.set_text("status", "Starting")
for i, step in enumerate(steps, start=1):
mas.log(f"running {step}")
with mas.ui.batch():
mas.ui.set_text("status", f"Step {i} of {len(steps)}: {step}")
mas.ui.set_progress("main_bar", i * 100 // len(steps))
mas.ui.append_text("log", f"{step} done")For interactive widgets, register handlers and start the listener once. The listener is a daemon thread, so the script still exits when your main function returns.
import mas
paused = False
def toggle_pause():
global paused
paused = not paused
mas.ui.set_text("status", "Paused" if paused else "Running")
mas.ui.on_click("pause_btn", toggle_pause)
mas.ui.start_listener()mas.ui.wait_for_event(timeout=30.0) is the blocking alternative when you want the script to stop and wait for a click. A table can hold buttons too: build a cell with mas.ui.cell_button(id=..., label=...) and handle its id like any other button. The full method list, with signatures and defaults, is on Runtime UI.
Checklists before you ship
Argument form:
- Every argument widget has a non-empty, identifier-safe, unique key.
- Required fields are marked required.
- Combo Box and Radio Group widgets have their options filled in.
- Number Input and Slider have sensible min and max values.
- You ran Preview and the form is legible.
- You clicked Save after the last change, so
ui.xmlandscript_args.pyare current.
Runtime dashboard:
- Every widget has a meaningful, identifier-safe, unique name.
- Every
mas.ui.*call uses a name that exists in the.uibrtfile. - Table row keys match the table’s columns.
- Multi-widget refreshes are wrapped in
mas.ui.batch(). start_listener()is called once, after all handlers are registered.- The macro still completes if the user never touches an interactive widget.
Layout tips
- Work within 932 pixels. The default window is 980 pixels wide with 24 pixels of padding on each side.
- Stack top to bottom. Forms read as a vertical list. Use the auto-layout button to tidy a tab, and the snap grid to keep edges aligned.
- Group with intent. A Group Box or a captioned Separator does more for readability than pixel nudging.
- Preview often. Preview renders the real window and is 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
Related pages
Thanks. If something is wrong, tell us in Discord.
Questions? Ask in Discord