Skip to content

How the Qt widgets work

redsun.view.qt gives you Qt widgets to use in the views of a session: the controls of a plan, buttons for the actions of a running plan, and a tree of a device's settings.


Plan widgets

create_plan_widget builds a plan widget from a PlanSpec: an input per parameter and a Run button, plus, for a continuous plan, a toggle, a pause button and a button per action. It returns a PlanWidget, a frozen dataclass owning the widget tree, whose group_box your view adds to its layout:

A plan widget, its view and the presenter

A plan widget runs nothing itself. Step through what happens when the user presses Run.

plan widgetviewpresenter
plan widgetviewpresenter

The plan widget calls the view back.

plan widgetviewpresenter
plan widgetviewpresenter

The view sends the plan's name and PlanWidget.parameters to the presenter, which runs the plan.

plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.
plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.

The presenter reports back that the plan started, paused or ended.

plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.
plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.

The view follows it on the widget, with toggle and pause.

plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.
plan widgetviewpresenterThe presenter runs the plan.The presenter runs the plan.

Each input starts from its default and returns a value of the annotated type. Inputs nest, so a dict[str, list[float]] becomes a table whose values are lists. While an input holds a value the plan can't take, such as a repeated key or no device chosen, Run stays disabled and the first of PlanWidget.problems shows under the parameters. How to run a plan from a presenter, How to write a plan that runs until stopped and How to choose the inputs of a plan show them in use.

Document callbacks

A plan's own document callbacks, which receive a run's documents, and those the user may attach are listed in a Callbacks group:

widget = create_plan_widget(
    spec,
    plan_callbacks=[median_filter],
    extendable=True,
    available_callbacks={"live_plot": live_plot, "table": table},
    attached_callbacks=["table"],
    selection_callback=on_selection,
)
widget.callbacks  # [median_filter, table]
widget.attached_callbacks  # ["table"]

The plan's own callbacks come first, checked and fixed; each shows its name in available_callbacks, else its name attribute or class name, and appears once even when also available. The user checks and reorders the others. callbacks returns the checked ones in order and attached_callbacks the names the user attached; with attached_callbacks=None every available one starts checked, and a name no longer available is ignored. A plan with no callback that isn't extendable gets no group. The widget only reports the choice; the presenter subscribes the callbacks to the RunEngine.


A widget for each parameter

create_param_widget turns each parameter's ParamDescription into a widget from magicgui, a package that builds widgets from Python types, by asking fixed questions in order:

How create_param_widget picks a widget

create_param_widget asks these questions in order and builds the widget of the first one answered yes. Step through four parameters.

a parameterhidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.
a parameterhidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.

detectors: Sequence[MyCamera] is a sequence of devices, so it gets a list of checkboxes, one per matching device.

detectors: Sequence[MyCamera]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.
detectors: Sequence[MyCamera]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.

camera: MyCamera is one device, so it gets a combo box of the session's matching devices.

camera: MyCamerahidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.
camera: MyCamerahidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.

mode: Literal["fast", "slow"] gets a combo box of its choices.

mode: Literal["fast", "slow"]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.
mode: Literal["fast", "slow"]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.

positions: list[float] is none of these, so it gets an input built from its type: a list of float inputs.

positions: list[float]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.
positions: list[float]hidden, or does itcarry actions?placeholderline editA plan widget leaves these parameters out, so the placeholder is never shown in one.a sequence or set ofdevices, or *args?a list ofcheckboxesOne checkbox per device of the session that matches the annotation, in the Devices group of the plan widget.a device?a combo boxof the devicesThe devices of the session that match the annotation, in the Devices group of the plan widget.a Literal?a combo boxof the choicesanything elsean input builtfrom its typeA list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type. nonononoyesyesyesyesA plan widget leaves these parameters out, so the placeholder is never shown in one. One checkbox per device of the session that matches the annotation, in the Devices group of the plan widget. The devices of the session that match the annotation, in the Devices group of the plan widget. A list, set, mapping, fixed-length tuple, optional value or union gets an input built from the inputs of its parts. Anything else gets the magicgui widget for its type.

How an annotation is read lists every annotation a plan widget can show.


Action buttons

ActionButton is a QPushButton that carries a PlanAction. An action with toggle_states gets a button you can check, whose label follows the state:

from redsun.view.qt.utils import ActionButton
from redsun.engine.actions import PlanAction

action = PlanAction(name="record", toggle_states=("Start", "Stop"))
btn = ActionButton(action)
# label shows "Record (Start)" when unchecked, "Record (Stop)" when checked

An action whose toggle_states is None gets a plain button you click, with a label that doesn't change.

To enable and disable a button as the plan offers its action and takes it back, see Following an action from a view.


A tree of device settings

DescriptorTreeView shows a device's describe_configuration and read_configuration as an editable two-column tree. It reports edits but writes nothing; a presenter does, through Deferrals while a plan runs (How to change a device setting while a plan runs):

from redsun.view.qt.treeview import DescriptorTreeView

tree = DescriptorTreeView(
    device.describe_configuration(),
    device.read_configuration(),
    parent=self,
)
tree.sig_property_changed.connect(on_property_changed)

Rows are grouped by their name-property key, with one header per device name. A property whose name holds a dash of its own gets a second header under that one, so cam-properties-Binning shows as Binning under properties under cam. A property whose source ends in :readonly shows as a greyed label.

An edit is only a request. The tree emits sig_property_changed and keeps the edit pending, showing what was typed, until it's told how the request ended:

An edit in the tree
treeviewpresenterdeviceif the device took itif the device refused it sig_property_changed,the edit stays pendingthe requestset the valueread it backannounce the value,or that the device refused itset_valuerevert
treeviewpresenterdeviceif the device took itif the device refused it sig_property_changed,the edit stays pendingthe requestset the valueread it backannounce the value,or that the device refused itset_valuerevert
tree.set_value("stage-position", 12.5)  # what the device read back
tree.revert("stage-position")  # the device refused: show the value before

set_value shows the value it's given, which may differ from what was typed when the device rounds or clips it, and neither call emits sig_property_changed, so the tree only ever shows values the device reported.


See also