mte.ui — form-spec builders¶
Every Phase 3 UI surface — ctx.docks.add_panel
and ctx.settings_page.register — takes
a form spec: a plain dict describing a widget tree. Build these
specs with the mte.ui.* helpers so you don't have to write the
JSON by hand.
from mte.ui import (
Column, Row, Group,
Label, Button, Checkbox, LineEdit, NumberSpin, ComboBox, List,
)
Nothing in mte.ui talks to Qt or PySide — each helper returns a
dict following the wire schema the host's C++ form parser expects.
Feel free to compose them, splice lists, or generate them
programmatically:
from mte.ui import Column, Row, Label, Button
def make_toolbar(commands):
return Row([Button(text=c["label"], on_click=c["id"])
for c in commands])
form = Column([Label(text="Actions:"),
make_toolbar(my_commands)])
The spec is validated on the host side; unknown control types or
missing required fields produce an InvalidParams error you'll see
in the log.
Layouts¶
Layout builders take a list of child dicts and arrange them vertically or horizontally.
Column(children)¶
Stacks children top-to-bottom with a trailing stretch — controls stay at their natural height instead of ballooning to fill a tall dock.
Row(children)¶
Same idea, left-to-right, with a trailing horizontal stretch.
Group(children, *, title="")¶
A titled frame (rendered as QGroupBox) containing a vertical
stack. Use it to visually group related controls:
Group([
Checkbox(id="cs", label="Case sensitive", binds="settings:cs"),
Checkbox(id="ww", label="Whole words", binds="settings:ww"),
], title="Search options")
Layouts nest freely — a Row inside a Column inside a Group
is fine.
Leaf controls¶
Every leaf accepts the same four shared bindings via keyword args:
| Argument | Meaning |
|---|---|
id= |
Stable id used by ctx.ui.get / ctx.ui.set and (implicitly) binds. |
binds= |
"settings:<key>" — auto-persist through ctx.settings. |
on_click= |
Command id to invoke — Buttons only. |
on_change= |
Handler id (ctx.docks.on_change(...)) — every other control. |
Only leaves that expose a "value" support binds and on_change.
Label(*, text="", id=None)¶
Static text.
Button(*, text, on_click=None, id=None)¶
A push button. Firing on_click invokes the registered command;
if you want to keep the button in sync with something else you
can ctx.ui.set its text.
Checkbox(*, label="", default=False, id=None, binds=None, on_change=None)¶
Boolean control. default is the initial state; binds overrides
it with the persisted value if one exists.
LineEdit(*, placeholder="", default="", id=None, binds=None, on_change=None)¶
Single-line text input.
NumberSpin(*, default=0, min=None, max=None, step=1, id=None, binds=None, on_change=None)¶
Integer spin box. min and max clamp the range (both optional;
the underlying QSpinBox uses its default range when unset).
step is the increment on the arrows.
int range
NumberSpin is backed by QSpinBox, which stores an int
(32-bit). Values outside -2_147_483_648 … 2_147_483_647 will
saturate at the platform limit.
ComboBox(*, items, default="", id=None, binds=None, on_change=None)¶
Drop-down. items is a list — each entry may be either a plain
string, or an object like {"value": "cpp", "label": "C++"} (only
value is used today; label is reserved for a future refinement).
List(*, items, id=None, on_change=None)¶
List widget with single-selection. on_change fires with the
newly selected item's text. No binds — persist the selection
yourself if you need it.
Runtime access¶
Once the panel is registered, drive it from any command:
panel_id = ctx.docks.add_panel(id="my.panel", ..., form=Column([
LineEdit(id="needle"),
Checkbox(id="cs"),
]))
# Read current values
needle = ctx.ui.get(panel_id, "needle")
cs = ctx.ui.get(panel_id, "cs") == "true"
# Write values (does NOT fire on_change)
ctx.ui.set(panel_id, "needle", "hello")
ctx.ui.set(panel_id, "cs", True)
See ctx.ui for the full reference.
Persisting values¶
binds="settings:<key>" is the shortest way to persist a control's
value:
- On panel build, the widget reads its initial value from
ctx.settings.get(key, <default>). - On every user edit, the widget writes back via
ctx.settings.set(key, new_value).
Because both panels and Preferences pages share ctx.settings, two
controls with the same binds string mirror each other in real
time. That's how HelloPython's dock and Preferences page keep the
"Loud" checkbox in sync.
If you need to persist something the vocabulary doesn't cover
(e.g. a List selection), read/write through ctx.settings
directly in an on_change callback.