Files
eco/docs/widget_views.md
2026-08-28 18:20:00 +02:00

8.5 KiB

Adjustable/Detector widget views

Every {py:class}~eco.elements.protocols.Adjustable or {py:class}~eco.elements.protocols.Detector in an assembly can be looked at live from either front end — the Qt desktop workbench or a browser (JupyterLab/Voila, via ipywidgets). Both build one row per item from the same underlying get_current_value()/set_target_value() interface, but they are two independent implementations with different capabilities, described below.

Default per-item row

Qt desktop (eco.widgets.display_qt)

The desktop app's dockable property grid ({py:class}~eco.widgets.display_qt.DisplayQt, also used standalone via make_assembly_qt_window) shows a name / current / control row per item, polled on a background thread:

Default Qt property-grid rows for a numeric Adjustable ("energy"), an enum Adjustable ("shutter"), and a read-only Detector ("intensity"). The numeric row has a step field, up/down tweak buttons, an absolute-value entry (commits on Enter/focus-out), and Stop/Reset buttons; the enum row is a dropdown with the same Stop/Reset pair; the Detector row is plain read-only text.

  • Detector-only items are read-only text — no controls.
  • Numeric Adjustables get a step-size field, ▲/▼ tweak buttons (move relative to the live current value, not the last-displayed one), an absolute-value entry, and 🛑 Stop / ↺ Reset (back to the value from when the row was built).
  • Enum-valued Adjustables (anything exposing enum_strs, e.g. AdjustablePvEnum) get a dropdown instead of step/absolute controls, same Stop/Reset pair.
  • Right-clicking a name label opens "Add indicator" — see below.

ipywidgets (eco.widgets.assembly_browser)

The browser-side namespace/assembly browser used by the lab/voila front ends builds each row as a plain ipywidgets HBox ({py:class}~eco.widgets.assembly_browser._ItemWidget):

Default ipywidgets rows for the same three items: name, type, current value (repr), then either a "new value" text box + Set button (Adjustable) or a Refresh button (Detector).

  • Name, Python type, and repr() of the current value are always shown.
  • Adjustables get a free-text entry plus a Set button (best-effort type coercion against the current value — bool/int/float/fallback JSON); there is no step/tweak concept here, no Stop, and no live polling — value updates only on explicit Refresh (Detectors) or after a successful Set (Adjustables).
  • Detectors get a Refresh button only.

This is a simpler, more manual sibling of the Qt row — it works anywhere a kernel/comm can reach a browser (including Voila, with no desktop Qt involved) but does not poll live and has no notion of enum dropdowns, tweak steps, or in-flight move stopping.

Indicator / gadget widgets

For a LabVIEW-style live panel — LEDs, gauges, dials — instead of a grid row, eco provides small standalone gadgets, one per item. The current direction is a matched, as-simple-as-possible pair, one module per backend, both built entirely from each toolkit's own stock controls (no new dependency, no custom-painted graphics) and sharing the same function names/signatures so either is a drop-in for the other:

  • {py:mod}eco.widgets.indicator_widgets_ipy — ipywidgets (lab/voila)
  • eco.widgets.indicator_widgets_qt_simple — Qt desktop

The seven stock-ipywidgets gadgets: a green LED-styled Button for shutter_open, a vertical FloatProgress for pressure, a horizontal FloatProgress standing in for the needle gauge on energy, a FloatProgress showing intensity's position within its observed min/max as a strip-chart stand-in, a large HTML number for counts, a row of ToggleButtons for mode's enum choices, and a FloatSlider for position.

ipywidgets — eco.widgets.indicator_widgets_ipy

The same seven gadgets on the Qt side, built from plain QWidgets: a green circular QPushButton for shutter_open, a vertical QProgressBar for pressure, a horizontal QProgressBar for energy, a QProgressBar plus a "value [lo .. hi]" label for intensity, a large QLabel for counts, a row of checkable QPushButtons for mode's enum choices, and a QSlider for position.

Qt — eco.widgets.indicator_widgets_qt_simple

Gadget (same name, both modules) ipywidgets control Qt control Notes
led_indicator Button (button_style) QPushButton (stylesheet) click to toggle if settable; a plain label/HTML can't take a click back
bar_gauge FloatProgress QProgressBar vertical or horizontal fill against [vmin, vmax]
analog_gauge FloatProgress (horizontal) QProgressBar (horizontal) approximation — no needle; see below
strip_chart FloatProgress + label QProgressBar + label approximation — position within the min/max seen so far, not a rolling line; see below
numeric_tile HTML QLabel (enlarged font) large number, no sparkline (same reasoning as strip_chart)
dial ToggleButtons (enum) / FloatSlider (numeric) checkable QPushButton row (enum) / QSlider (numeric) enum case shows every choice at once; dragging a value in a circle isn't idiomatic UX on either toolkit, so the numeric case reuses the slider rather than faking a knob
slider FloatSlider QSlider writes on release if settable

Five of the seven are exact stock-widget matches on both sides. The other two (analog_gauge, strip_chart) are deliberately left as plain bar/value approximations rather than reaching for a new dependency — see either module's docstring. If a real needle gauge or a real rolling plot is wanted later, the candidates worth reviewing first:

  • Plotly go.Indicator (mode="gauge+number" for the needle gauge, mode="number+delta" for a nicer KPI tile) — its FigureWidget is a real ipywidgets.DOMWidget, so it drops straight into the same VBox/HBox rows these gadgets already use, and updates live via fig.data[0].value = x. The single-best "one new dependency, covers both gaps" option.
  • bqplot — a proper live-updating line plot (Lines/Figure) for strip_chart; itself a real Jupyter widget, no needle-gauge support.
  • Panel (panel.widgets.indicators.Gauge/Dial/BooleanStatus) — the closest visual match to the Qt gadgets overall, but a separate framework built on Bokeh; its pn.ipywidget() bridge into plain ipywidgets layouts has known rough edges (no JS-linking, some nested-container issues), and it pulls in Bokeh as a second rendering stack.
  • anywidget — the modern, low-effort way to wrap any JS/web-component (e.g. a real rotary-drag knob, a glowing LED) as a first-class Jupyter widget, if a literal LabVIEW look is ever wanted beyond what the above give. Worth knowing about, not needed for anything on this page.

Neither module has a discovery UI yet ("right-click a name → add gadget" on Qt, or an equivalent on the notebook side) — call the function directly for now, e.g. eco.widgets.indicator_widgets_ipy.bar_gauge(my_assembly.pressure, vmin=0, vmax=100) or the equivalent eco.widgets.indicator_widgets_qt_simple.bar_gauge(...).

The older, hand-painted Qt gadgets

eco.widgets.indicator_widgets — the original, richer Qt-only module (real needle gauge, a radial mode-select dial face, a live rolling strip-chart) — still exists and is unrelated to the simple pair above, not superseded by it. It's wired into the desktop app's right-click "Add indicator" menu and a droppable {py:class}~eco.widgets.dashboard_qt.Dashboard, and its gadgets double as valid Qt Designer "promoted widgets":

Five hand-painted indicator gadgets against dummy items: a green LED for a boolean shutter state, a vertical bar gauge for a pressure Detector, a semicircular analog gauge with a needle for an energy readback, a rotary Dial in enum "mode-select" form showing every choice (standby/ready/running/fault) around the ring, and a horizontal Slider with a numeric readout for a position Adjustable.

See its module docstring for the full gadget list and eco.widgets.indicator_widgets.attach_indicator_menu for how it's wired into eco.widgets.display_qt's property grid. Reach for this one if the simple pair's two approximations (no needle, no rolling line) actually matter for a given panel; otherwise the matched simple pair above is the current default recommendation for new work, on either backend.