11 KiB
BEC Widgets
A modular PySide6(Qt6) toolkit for BEC (Beamline Experiment Control). Create high-performance, dockable GUIs to move devices, run scans, and stream live or disk data—powered by Redis and a modular plugin system.
Highlights
- No-code first — For ~90% of day-to-day workflows, you can compose, operate, and save workspaces without writing a single line of code. Just launch, drag widgets, and do your experiment.
- Flexible layout composition — Build complex experiment GUIs in seconds with the
BECDockArea: drag‑dock, tab, split, and export profiles/workspaces for reuse. - CLI / scripting — Control your beamline experiment from the command line a robust RPC layer using
BECIPythonClient. - Designer integration — Use Qt Designer plugins to drop BEC widgets next to any Qt control, then launch the
.uiwith the custom BEC loader for a zero‑glue workflow. - Operational integration — Widgets stay in sync with your running BEC/Redis as the single source of truth: Subscribe to events from BEC and create dynamically updating UIs. BECWidgets also grants you easy access the acquisition history.
- Extensible by design — Build new widgets with minimal boilerplate using
BECWidgetandBECDispatcherfor BEC data and messaging. Use the generator command to scaffold RPC interfaces and Designer plugin stubs; beamline plugins can extend or override behavior as needed.
Table of Contents
Installation
Use any of the following setups:
Stable release
Use the package manager pip to install BEC Widgets:
pip install bec_widgets
From source (recommended for development)
For development purposes, you can clone the repository and install the package locally in editable mode:
git clone https://github.com/bec-project/bec_widgets.git
cd bec_widgets
pip install -e .[dev]
Features
1. Dock area interface: build GUIs in seconds
The fastest way to explore BEC Widgets. Launch the BEC IPython client with simply bec in terminal and the BECDockArea opens as the default UI:
drag widgets, dock/tab/split panes, and explore. Everything is live—widgets auto-connect to BEC/Redis, so you can
operate immediately and refine later with RPC or Designer if needed.
2. Qt Designer plugins + BEC Launcher (no glue)
All BEC Widgets ship as Qt Designer plugins with our custom Qt Designer launchable by bec-designer. Design your UI
visually in Designer, save a .ui, then launch it with
the BEC Launcher—no glue code. Widgets auto‑connect to BEC/Redis on startup, so your UI is operational immediately.
3. Robust RPC from CLI & remote scripting
Operate and automate BEC Widgets directly from the BECIPythonClient. Create or attach to GUIs, address any sub-widget
via a simple hierarchical API with tab-completion, and script event-driven behavior that reacts to BEC (scan lifecycle,
active devices, topics)—so your UI can be heavily automated.
- Create & control GUIs: launch, load profiles, open/close panels, tweak properties—all from the shell.
- Hierarchical addressing: navigate widgets and sub-widgets with discoverable paths and tab-completion.
- Event scripting: subscribe to BEC events (e.g., scan start/finish, device readiness, topic updates) and trigger actions,switch profiles, open diagnostic views, or start specific scans.
- Remote & headless: run automation on analysis nodes or from notebooks without a local GUI process.
- Plays with no-code: Use the Dock Area / BEC Designer to set up the layout and add automation with RPC when needed.
Window visibility and display diagnostics
gui.show() shows hidden windows, restores minimized windows, and requests foreground activation
when a window is behind another application. gui.raise_window() uses the same behaviour;
gui.flomni.raise_window() targets one window. Already-visible, active windows are left unchanged.
On XCB (X11/XWayland), BEC retains its GNOME/RHEL window-raising workaround and restores the original window flags afterwards. On native Wayland, BEC briefly hides and shows visible, inactive windows before requesting activation, because GNOME can refuse to activate an existing window. This keeps the window's contents and flags, but may cause flicker or let the compositor reposition it. Each call finishes with the window shown; already-active windows are left untouched. On macOS, BEC requests activation without remapping the window. Activation is still subject to the window manager/compositor's focus policy; a Redis RPC is not a local mouse or keyboard event.
To inspect the running GUI server process, from the BEC IPython client:
from pprint import pprint
pprint(gui.get_display_info())
The report includes the loaded Qt platform (wayland, xcb for X11/XWayland, or cocoa on macOS),
Qt version and plugin search paths, Python executable, selected inherited environment variables,
screens, and window visibility/focus state. XDG_CURRENT_DESKTOP and DESKTOP_SESSION are desktop
hints, not authoritative compositor identification. The session can be Wayland while Qt uses xcb.
Qt settings or plugin paths inherited from a shell or another application's launcher appear in the
environment section. Diagnostics do not start the GUI or change its settings.
After updating this code, restart both the BEC client and its GUI server before testing. On the
affected console, compare the report before and after repeated gui.show() and gui.raise_window()
calls. visible=True with active=False means Qt considers the window shown but without keyboard
focus; it does not prove the window is unobscured or on the current workspace.
For an XWayland comparison on a Linux desktop with XWayland available, start a fresh session with
QT_QPA_PLATFORM=xcb bec --session flomni and verify that qt_platform is xcb. This sets the backend
for that invocation only. For plugin-loading details, start with QT_DEBUG_PLUGINS=1 bec --session flomni.
4. Rapid development (extensible by design)
Build new widgets fast: Inherit from BECWidget, list your RPC methods in USER_ACCESS, and use bec_dispatcher to
bind endpoints. Then run bw-generate-cli --target <your-plugin-repo>. This generates the RPC CLI bindings and a Qt
Designer plugin that are immediately usable with your BEC setup. Widgets
come online with live BEC/Redis wiring out of the box. 
View code: Example Widget
from typing import Literal
from qtpy.QtWidgets import QWidget, QLabel, QPushButton, QHBoxLayout, QVBoxLayout, QApplication
from qtpy.QtCore import Slot
from bec_lib.endpoints import MessageEndpoints
from bec_widgets import BECWidget, SafeSlot
class SimpleMotorWidget(BECWidget, QWidget):
USER_ACCESS = ["move"]
def __init__(self, parent=None, motor_name="samx", step=5.0, **kwargs):
super().__init__(parent=parent, **kwargs)
self.motor_name = motor_name
self.step = float(step)
self.get_bec_shortcuts()
self.value_label = QLabel(f"{self.motor_name}: —")
self.btn_left = QPushButton("◀︎ -5")
self.btn_right = QPushButton("+5 ▶︎")
row = QHBoxLayout()
row.addWidget(self.btn_left)
row.addWidget(self.btn_right)
col = QVBoxLayout(self)
col.addWidget(self.value_label)
col.addLayout(row)
self.btn_left.clicked.connect(lambda: self.move("left", self.step))
self.btn_right.clicked.connect(lambda: self.move("right", self.step))
self.bec_dispatcher.connect_slot(self.on_readback, MessageEndpoints.device_readback(self.motor_name))
@SafeSlot(dict, dict)
def on_readback(self, data: dict, meta: dict):
current_value = data.get("signals").get(self.motor_name).get('value')
self.value_label.setText(f"{self.motor_name}: {current_value:.3f}")
@Slot(str, float)
def move(self, direction: Literal["left", "right"] = "left", step: float = 5.0):
if direction == "left":
self.dev[self.motor_name].move(-step, relative=True)
else:
self.dev[self.motor_name].move(step, relative=True)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
w = SimpleMotorWidget(motor_name="samx", step=5.0)
w.setWindowTitle("MotorJogWidget")
w.resize(280, 90)
w.show()
sys.exit(app.exec_())
Widget Library
A large and growing catalog—plug, configure, run:
Plotting
Waveform, MultiWaveform, and Image/Heatmap widgets deliver responsive plots with crosshairs and ROIs for live and history data.
Scan orchestration and motion control.
Start and stop scans, track progress, reuse parameter presets, and browse history from a focused control surface. Positioner boxes and tweak controls handle precise moves, homing, and calibration for day‑to‑day alignment.
Documentation
The documentation can be found here.