GUI components
Desktop applications and their responsibilities.
The desktop suite is built with PySide6 and shares a common visual language, theme and lifecycle model.
Architecture overview
The Experiment Browser is the main window: FileSelector opens experiment scripts, the central QMdiArea hosts one or more ExperimentWindow panes, and the top dock area manages the experiment queue, preparation station and looper. ExperimentManager is the execution engine that consumes tasks and drives each run through its lifecycle.
flowchart TB
subgraph Browser[Experiment Browser - main window]
FS[FileSelector<br/>experiment file tree]
MDI[QMdiArea<br/>experiment pane container]
EW[ExperimentWindow<br/>single-experiment control]
DOCKS[Top dock area<br/>queue / preparation / looper]
FS --> MDI
MDI --> EW
DOCKS --> MDI
end
EM[ExperimentManager<br/>execution engine]
QUEUE[Experiment queue / scans]
PROC[Process Manager]
EW --> EM
DOCKS --> EM
EM --> QUEUE
EM --> PROCExperimentWindow owns the parameter panel, submission and scan entry points. ExperimentManager does not draw the UI; it consumes tasks from the queue and tracks their run state. The Process Manager starts and supervises the independent GUI, driver and service processes.
| Application | Responsibility |
|---|
| Operations Console | Services, tasks/processes/ports and device health in one window |
| Experiment Browser | Experiment selection, parameters, queue, scans, preparation and looper |
| Dashboard | Instrument defaults and parameter tuning by device group |
| Data Manager | HDF5 browsing, fits, image analysis and export |
| Stream Monitor | Live data and message inspection |
| Image / Live / Scroll viewers | Camera, ROI and real-time curve displays |
| Timeline | FPGA/ADwin sequence visualisation |
| Device Manager | Device database editing, driver catalogue and health checks |
| Property / Subscription / Auto-save editors | Shared property tree and persistence policies |
| Settings Hub | Notifications, licence and low-frequency configuration |
| Task & Process Monitor | Read-only task, process, endpoint and health observability |
Design conventions
- Every window can be opened independently or through the launcher.
- Closing the launcher’s last window prompts for full application exit and stops all services.
- Windows that create background work release it in
closeEvent; hidden windows pause refresh timers rather than stopping data clients. - Help and About pages are per-window and bilingual.
- The interface follows the operating system’s font scaling and light/dark appearance.
Extending the GUI
Reusable building blocks live under quatm/GUI/widgets and quatm/GUI/infra. Project-specific windows belong in the project’s GUI/ directory and are registered in configfile.json. The application identity table controls window names, icons and desktop launchers.
1 - Experiment browser
The Quatm main window: experiment browsing, queue management and a multi-window MDI workspace.
Overview
quatm/GUI/browser/main_window.py provides the experiment browser (Browser): Quatm’s main application window, built on QMainWindow + QMdiArea as a multiple-document interface. The file selector on the left browses experiment scripts, the experiment queue, preparation station and looper are docked at the top, and each experiment occupies its own sub-window in the central MDI area.
Layout
┌───────────────┬───────────────────────────────┐
│ FileSelector │ QMdiArea (subwindow area) │
│ (file tree) │ ┌─────────────────────┐ │
│ │ │ ExperimentWindow │ │
├───────────────┤ └─────────────────────┘ │
│ Queue / Prep │ │
│ / Looper dock │ │
└───────────────┴───────────────────────────────┘
Core components
| Component | Module | Description |
|---|
FileSelector | browser/file_selector.py | Experiment directory tree; double-clicking opens the experiment or simulation window |
ExperimentWindow | browser/experiment_window.py | Control panel for a single experiment inside the central MDI area |
ExperimentQueue | experiment/experiment_queue.py | Tabular task queue |
ExperimentManager | experiment/experiment_manager.py | Takes tasks from the queue and executes them |
PrepStation | experiment/prepstation.py | Staging and editing of tasks before they are sent to the queue |
Looper | looper/ui.py | Conditionally repeats a group of tasks based on measurement results |
Details of each component are given on the corresponding page in this section.
Lifecycle
init_ui / create_dock_widgets: adapts the default geometry to the screen and creates the MDI canvas together with the two dock areas (the file selector on the left; the queue, preparation station and looper at the top).open_experiment: opens an experiment window; if it is already open, restores it from minimised state and raises it.closeEvent: first saves the main-window geometry and the state of all experiment sub-windows, then stops the queue/looper worker threads, and finally hands over to the base class to complete the close.moveEvent / resizeEvent: debounced geometry saving, so that the window position and size are restored on the next start.
Usage
from quatm.GUI.browser.main_window import Browser
browser = Browser()
browser.show()
The experiment browser is the interface through which every experiment other than the helpers is run; it is normally started as the main process by the process manager.
2 - Experiment manager
The core experiment execution engine: takes tasks from the experiment queue and schedules them by measurement/sequence/run hierarchy.
Overview
quatm/GUI/experiment/experiment_manager.py provides the experiment manager (ExperimentManager): the engine that takes tasks from the experiment queue (ExperimentQueue) and executes them. Its queue polling runs in a worker thread, and tasks are scheduled in the hierarchy measurement → repetition → sequence/scan → run.
Execution flow
queue_fn (worker thread polls the queue continuously)
└─→ run (task wrapper: cleanup is guaranteed however the task ends)
├─→ start_measurement (measurement start hook)
├─→ start_sequence (sequence/scan start hook)
│ └─→ … several run calls with varying parameters …
├─→ end_sequence (sequence end hook)
└─→ end_measurement (measurement end hook; clears the task table)
Key methods
| Method | Description |
|---|
queue_fn | Continuously polls the queue for task dictionaries: selects tasks by priority, task number and scheduled time |
run | Task run wrapper: finalises cleanly whether the task ends as Done/Terminated/Failed or is deleted |
pause | Pauses the current experiment; calling it again resumes it |
terminate_experiment | Graceful termination: lets the current run finish naturally before stopping |
start_measurement, etc. | Invokes the experiment’s start/end hooks for each stage |
set_dict | Writes a single parameter to an attribute of the experiment instance |
set_run_nr / set_rep_nr | Synchronises the run/repetition index into the task dictionary and refreshes the table |
due_check | Determines whether a task has reached its scheduled execution time |
start_queue / shutdown | Starts / stops the resident queue worker |
update_table | Notifies the GUI to refresh the displayed task table |
Usage
from quatm.GUI.experiment.experiment_manager import ExperimentManager
manager = ExperimentManager(browser) # browser supplies the queue and the UI
manager.start_queue() # start the resident queue worker
Tasks execute in a dedicated worker thread and do not block the GUI; when the browser closes it calls shutdown to stop the queue worker so that the process exits cleanly.
3 - Experiment queue
The task queue table panel: maintains priority ordering, status tracking and lifecycle management (pause/terminate/sleep/delete) of experiment tasks.
Overview
ExperimentQ (quatm/GUI/experiment/experiment_queue.py) is the “experiment queue” panel of the experiment browser (Browser) main window: a task table driven by the queue table model ScheduleModel (module quatm/GUI/widgets/models.py). Task dictionaries are stored with the task number as primary key; each task is one record containing everything needed to run it, and the table sorts by priority priority (lower value first) and then by task number.
Task status
The experiment manager (ExperimentManager) and the panel jointly maintain the status of each task:
| Status | Meaning |
|---|
Queued | Enqueued and waiting for execution |
Waiting | A due time has been set; not executed until that time is reached |
Sleeping | Asleep; the manager ignores it for the moment (click again to wake it) |
Running | A single run is in progress |
Scanning | A sequence/scan is in progress |
Paused | Paused; can be resumed |
Terminating / Termination Pending | Graceful termination in progress (lets the current run finish) |
Done | Execution completed |
Failed | Execution failed |
Operations and shortcuts
- Button bar (created by
create_buttons): icon buttons for pause/resume, terminate selected tasks, terminate all, and so on. - Context menu and single-key shortcuts (
init_table_actions): e edits parameters, v opens a read-only preview, and delete, sleep/wake and pause are also available. - Deleting a running task: automatically becomes a graceful termination (
delete_clicked). - Terminate (
terminate_clicked / terminate_all): stops gracefully, letting the run currently in progress finish before ending.
Main methods
| Method | Function |
|---|
submit(taskDict) | The only entry point into the queue (runs on the GUI thread): assigns task numbers centrally; the looper (Looper) also reaches it through the submitItem signal |
pause() | Pauses/resumes the experiment currently running |
set_sleeping() | Marks the selected tasks as sleeping or wakes them |
delete_item(k) / delete_clicked() | Deletes by task number, or deletes the selected rows (a running task is terminated gracefully) |
terminate_clicked() / terminate_all() | Gracefully terminates the selected tasks / all tasks |
update_item(k, v) | Updates a model entry by task number and resets its lock |
open_editor() / open_viewer() | Edits / previews the parameters of the selected task read-only (shortcuts e / v) |
Data model
# task dictionary structure (key = task number)
expDict = {
1: {
"expName": "Rabi scan",
"filepath": "experiments/ion/rabi.py",
"status": "Queued",
"priority": 1,
"args": {},
"due": "",
},
}
Sorting rule: priority (lower value first) → task number (first in, first out).
Relation to the looper
Task items of the looper (Looper) are also enqueued through ExperimentQ.submit(), so the desktop interface and loop scheduling share the same “central task-number assignment” entry channel.
4 - Preparation station
The staging and editing area for experiment parameters: preview, reorder and fine-tune tasks before they are pushed into the queue.
Overview
PrepStation is a tab in a top dock of the experiment browser (Browser) that acts as the
“staging area” of the experiment queue: tasks that are not yet ready are kept here and are
pushed into ExperimentQ for execution once confirmed. The task list is persisted in
configuration/browser/prepfile.json and can be viewed and manipulated from the experiment
console on the browser side (Web remote control).
Functions
| Operation | Description |
|---|
| Stage | Clicking the Prep button in the experiment window stages the current parameters (including the scan definition) in the task list |
| Push | Copies the selected task into ExperimentQ (the queue retains the complete task number, priority and scan parameters) |
| Add to loop | Adds the selected task to a Looper loop group (push_to_looper) |
| Reorder | Moves tasks up/down to change their order |
| Sleep | Marks a task as Sleeping so that it takes no part in pushes for the time being |
| Edit/view | Opens the task parameter editor or a read-only view |
| Save/load | Saves a parameter set as JSON (experiment_params/<experiment>/<date>/) |
Task data structure
Every staged task has the same structure as a queue task (generated by the experiment window’s setup_task_dict()):
task = {
'task': 12, # task number
'expName': 'Absorption', # experiment name
'label': 'scan: freq 2:1', # label (includes the scan prefix)
'priority': 0,
'args': {'count': 5}, # experiment parameters
'nRuns': 21, # number of scan points
'scanpars': ['freq'], # scan parameters
'scanvals': [[...]], # scan values
'scansequence': [...], # scan order (after randomisation)
'nReps': 1,
'status': 'Queued', # Queued / Sleeping / Waiting
'filepath': '.../absorption.py',
}
Persistence and sharing
- The task list is kept in
configuration/browser/prepfile.json and is restored automatically when the browser restarts; - The desktop GUI and the Web remote control platform share the same file,
so tasks staged, pushed, reordered or slept on one side are immediately visible on the other.
5 - Looper
The conditional loop executor: decides automatically, from measurement results, whether to repeat, jump or end an experiment loop.
Overview
Looper is a tab in the top dock of the experiment browser (Browser)
(quatm/GUI/looper/). It manages groups of experiment tasks, executes the items of a group
in order, and decides whether to jump or to stop based on the measurement results. Loop
definitions are persisted in configuration/browser/looper/loopfile.json, and the same file
is shared with the experiment console on the browser side
(Web remote control).
Core logic
LoopManager.run_loop(): loop from the start group forever until terminate
└─→ run the items of the group in order
├─ Task: submit to the experiment queue and advance after it finishes
│ (max count / max consecutive count can be set; reaching either ends the loop)
└─ Conditional: read a data-stream field → compare (> >= == != =< <)
├─ true → jump to the given item / end of group / end of loop
└─ false → jump to the else item
Components
| Component | Description |
|---|
LoopGroup | A loop group (one tab), containing the item list and the loop builder |
LoopItem | Item base class (delete/up/down/run counter/indicator LED) |
TaskItem | Task item: points to a staged task, supports max count/max consecutive/skip |
ListItem | Task list item: expands a task list and runs the tasks one by one |
ConditionalItem | Data condition item: compares a field and then jumps (if/else) |
ConditionalReachedItem | Reached-index condition item: jumps once the current time reaches the N-th cycle’s h5 save time |
ConditionalDateTimeItem | Time condition item |
CallerItem / GroupItem | Inter-group jump and end-of-group items |
DataManager | Subscribes to data streams and accumulates the latest measurement values |
LoopManager | The loop executor (run_loop() / terminate(), running in a background thread) |
AutoSaveEditor | Configuration of the automatic HDF5 save schedule |
How to use
Operate interactively through the Looper tab in the experiment browser (Browser):
- Add a group: create a new tab (
New Tab #N), which can be renamed; - Add items: each group can contain Task / List / Conditional / ConditionalDateTime
/ ConditionalReached / Caller and other items;
- Configure items: for Task, select the staged task number and set the maximum count; for
Conditional, select the data field, comparison operator and threshold, and configure the
if/else jump targets (item index /
end box / end loop); - Run: select the starting group and click run;
LoopManager executes in a background thread and
the terminate button stops it at any time.
# core classes (used internally by the GUI)
from quatm.GUI.looper import Looper, LoopManager
looper = Looper(parent=browser)
looper.loopManager.run_loop() # start looping from the start group
looper.loopManager.terminate() # terminate the loop
Persistence and sharing
- Loop definitions are saved automatically to
configuration/browser/looper/loopfile.json
({group name: [item dictionaries, ...]}) and restored automatically when the browser restarts; - The desktop GUI and the web remote-control platform share the same file, so changes made on
either side are immediately visible on the other; the browser side likewise supports group
management, Task/Conditional item editing and starting/stopping loops.
6 - Experiment window
The per-experiment control window: dynamic parameter grid, submission/scanning and parameter persistence.
Overview
quatm/GUI/browser/experiment_window.py provides the experiment window (ExperimentWindow):
each experiment script corresponds to a control window carried by an ExperimentSubWindow in
the browser’s MDI area. It generates a parameter grid automatically from the parameter objects
declared by the experiment, and is responsible for submitting to the queue/preparer, building
scans and loading/saving JSON parameters.
Components
| Component | Description |
|---|
ExperimentSubWindow | Graphical container: stores the window geometry and clamps the window inside the visible area |
ExperimentWindow | Single-experiment control panel: parameter grid + submit/scan/save controls |
SequenceEditor | Scan sequence dialog: sets start, stop and step count per dimension to generate an nD scan |
Key methods (ExperimentWindow)
| Method | Description |
|---|
submit_to_queue | Submits the current experiment to the experiment queue for execution (the unified entry point for all submission buttons) |
submit_next | Submits with the highest priority, ahead of all existing tasks |
submit_to_prepper | Submits to the preparation station (PrepStation) for staging |
setup_scan | Builds the scan sequence (parameter values and randomised run order) from the scan definition |
setup_task_dict | Builds the task dictionary stored in the queue/preparation station |
build_argument_dict | Collects values from the parameter grid controls into a complete parameter dictionary |
save_params / load_params | Saves parameters to JSON (named by date/time, optionally backed up) and loads them back |
adaptive_columns | Re-flows the parameter grid columns to the window width (roughly one extra column per 200 px) |
edit_sequence | Opens the scan sequence editor and applies the new sequence |
Usage
from quatm.GUI.browser.experiment_window import ExperimentWindow
# normally opened by Browser.open_experiment; may also be constructed directly
win = ExperimentWindow(filepath=..., props=props, parent=subwindow, browser=browser)
The parameter objects declared by the experiment script (such as NumberValue / BoolValue /
StringCombo) automatically produce editing controls with a label, unit and range; parameter
sets can be saved as json and loaded again later.
7 - Parameter system
The BasicManager parameter-manager system: maps experiment properties to numeric, boolean, combo and frequency controls.
Overview
quatm/GUI/entries/dashboard.py provides a family of “parameter manager” controls: each
manager maps one experiment parameter (stored in the property database, always in SI units
internally) to a labelled input control, and synchronises bidirectionally between the
interface and the property database.
Control class hierarchy
BasicManager (base class of numeric parameter entries)
├── IntManager → integer spin box
├── FloatManager → float spin box (optional unit/step/decimals)
├── BoolManager → check box
├── ComboManager → drop-down box (string list)
└── FrequencyManager → DDS frequency spin box (stores Hz, shows MHz)
Usage (schematic)
from quatm.GUI.entries.dashboard import IntManager, FloatManager, BoolManager
# each manager binds the property store props and the experiment exp, reading/writing by parameter name
mgr = IntManager(props=props, exp=exp, parname="my_int")
fmgr = FloatManager(props=props, exp=exp, parName="cooling_time", unit="µs")
bmgr = BoolManager(props=props, exp=exp, parName="enable")
Changes made in the interface are converted to SI and written back to the property database;
changes in the property database are likewise synchronised to the displayed values periodically
by each control (updateValue / updateSpin).
See also “Experiment parameter panel”: on this basis the Dashboard main panel arranges the
parameters of a whole device into an adjustable control panel.
8 - Property editor
A ParameterTree-based tool for viewing and editing properties.
Overview
quatm/GUI/entries/property_editor.py provides a property editing tool based on
parametertree: it presents the key/value tree of the property database as an expandable,
editable parameter tree with search filtering, and writes changes back immediately.
Components
| Component | Description |
|---|
TreeEdit | Extracts information from Properties and arranges it into a tree-view structure |
PropEdit | Main property editing window (ParameterTree): subtree browsing, search, saving/deleting the selected node |
Usage
from quatm.GUI.entries.property_editor import PropEdit
editor = PropEdit(name="device_status", props=props, subtree="/", parent=parent)
Note: subtree restricts the view to the properties under a given path; TreeEdit is responsible for converting the property dictionary into a tree of Parameter nodes.
9 - Live plotting
A live plot item that subscribes to data streams and keeps updating, together with its widget container.
Overview
quatm/GUI/entries/updating_plot.py provides live plotting: UpdatingPlot is a
PlotItem subclass that subscribes to a data stream, periodically pulls new data and
refreshes the curve; LivePlot is a widget container that embeds one UpdatingPlot, which
makes it easy to dock into other windows as a panel.
Components
| Component | Description |
|---|
UpdatingPlot | Live plot item: subscribes to a data stream and keeps the curve updated |
LivePlot | Widget container: embeds one UpdatingPlot and may carry an image stream name |
Key methods (UpdatingPlot)
| Method | Description |
|---|
setnewData | Periodically pulls new data; a one-dimensional array gets an x axis automatically, then the curve is updated according to the rotation flag |
updateConfiguration | Cancels the old subscriptions, then resubscribes and rebuilds the curves from the stream list configured in the properties |
subscribe_window | Opens the subscription editor to select data streams and reapplies the subscription configuration on close |
configureWindow | Opens the property editor dialog to adjust settings and reapply the display configuration |
Usage
from quatm.GUI.entries.updating_plot import UpdatingPlot, LivePlot
plot = UpdatingPlot(name="PMT signal") # use directly as a plot item
host = LivePlot(name="PMT signal", parent=parent) # or embed as a container in the UI
Subscriptions are made in the context menu of the plot item; LivePlot is also used as the
container for the row/column profile plots in the image viewer.
10 - LED indicator
A status LED: green/red/cyan color schemes, radial gradients and lit/unlit appearances.
Overview
LedIndicator (quatm/GUI/widgets/qled.py) is an LED indicator button whose lit/unlit
appearance can be toggled: a two-layer circular shell with a gradient centre, using highlighted
colors when checked (lit) and dark colors when unchecked (dark). It is commonly used to show
device connection status, experiment run status and the execution feedback of loop items.
Color modes
| Method | Description |
|---|
set_green() | Green scheme: bright green when checked, dark green when unchecked; usually indicates normal/ready |
set_red() | Red scheme: bright red when checked, dark red when unchecked; usually indicates an error/warning |
set_cyan() | Cyan scheme: used by looper condition items while they wait for data |
The lit/unlit (checked) state is controlled with setChecked(True/False); the onColor1/onColor2
and offColor1/offColor2 properties customise the primary/secondary colors of the on and off
states.
Usage
from quatm.GUI.widgets.qled import LedIndicator
led = LedIndicator(parent=parent)
led.set_green() # device ready
led.setChecked(True) # switch on
# ...
led.set_red() # device fault
11 - GUI base classes
BWidget, BMainWindow and BFrame: Qt base classes with geometry persistence and property communication.
Overview
quatm/GUI/widgets/quatmQt.py defines the base classes and common controls of the Quatm GUI
components: it provides window geometry persistence, the Properties property namespace, and a
combo box with completion.
Class hierarchy
QWidget → BWidget
QFrame → BFrame
QMainWindow → BMainWindow
Base classes and mixins
| Component | Description |
|---|
GeometrySaveMixin | Persists window geometry to QSettings (keyed by window name); saves on close, move and resize (debounced) |
BWidget | Quatm widget base class: carries the Properties namespace and geometry persistence |
BFrame | Base class corresponding to QFrame, with the same behaviour as BWidget |
BMainWindow | Main-window base class with geometry persistence and a property handle |
from quatm.GUI.widgets.quatmQt import BWidget, BMainWindow
widget = BWidget(name="my_widget", parent=None)
window = BMainWindow(name="MainWindow", parent=None)
Search and completion controls
| Component | Description |
|---|
CustomCompleter | Completer with match modes (contains = substring anywhere / begins = prefix) |
SearchComboBox | Combo box with autocompletion and type-ahead search; add_if_new() / find_or_add() maintain entries conveniently |
SearchLineEdit | Line edit for search boxes: selects all on focus, records on focus-out/Enter, restores on Escape |
from quatm.GUI.widgets.quatmQt import SearchComboBox
box = SearchComboBox(parent=parent)
box.add_if_new("my_stream")
12 - Image monitor
A self-updating image display control: image/mask stream subscription, denoising and spot detection, level and ROI configuration.
Overview
quatm/GUI/entries/image_monitor.py provides the automatically updating image display window
(ImageDisplay): it periodically pulls the latest frame from the image stream subscribed in the
properties, transforms it by resolution and offset and refreshes the display, optionally
overlaying a mask stream in sync. It has built-in denoising, spot detection, saving of the
current frame and property configuration (display levels, ROI, etc.).
Methods
| Method | Description |
|---|
update_image | Periodically pulls the image stream and refreshes the display frame by frame after resolution/offset transforms |
update_mask | Pulls the mask stream and updates the overlay geometry from the mask resolution/offset |
noise_filter | Denoises with a bilateral filter plus a median filter according to the property parameters |
spot_detector | Binarises the image by threshold to detect bright spots |
saveCurrentImage | Saves the currently displayed frame as PNG (named by time) |
subscribe_window | Opens the subscription dialog to select image streams and resubscribes |
subscribe_mask | Opens the subscription dialog to select mask streams and resubscribes |
configureWindow | Opens the property editing dialog to adjust display levels, ROI and other parameters |
Usage
from quatm.GUI.entries.image_monitor import ImageDisplay
view = ImageDisplay(imagestreams=["camera/image"], parent=parent)
view.configureWindow() # adjust display levels, ROI, etc.
Note: ImageDisplay is both the central display of ImageWindow and can be embedded on its
own in other interfaces. The image and mask stream lists are stored in the properties, and the
context menu provides entries for subscription, mask subscription and configuration.
13 - Analysis manager
A tree-view panel: manages the start, stop, status monitoring and automatic restart of analysis scripts.
Overview
quatm/GUI/entries/analysismanager.py provides the analysis manager: it lists the analysis
scripts in a checkable tree table (name / script / category / input stream) and is responsible
for starting and stopping subprocesses, monitoring and automatic restart, as well as adding,
deleting and configuring analysis entries.
Components
| Component | Description |
|---|
AnalysisManager | Main panel: loads and organises the analysis entries, and provides delete/configure entry points |
TreeViewWidget | Analysis script management tree: checking starts and stops, monitoring restarts automatically, add/delete/edit |
addAnalysisWidget | Toolbar for adding an analysis entry (name / category / script / input stream) |
Functions
| Operation | Description |
|---|
| Check to start | Checking an entry starts the corresponding analysis subprocess; unchecking stops it |
| Stop | endProcess: terminates first and waits briefly for exit, then forces the end on timeout |
| Status refresh | updateStatus: periodically checks whether the process is still running |
| Clean up stray processes | killVagabonding: terminates leftover processes of the same script from the previous round |
| Add / configure / delete | The toolbar registers new entries; a selected row can be configured or deleted |
Usage
from quatm.GUI.entries.analysismanager import AnalysisManager
mgr = AnalysisManager(name="AnalysisManager", parent=parent)
Analysis scripts are independent processes: once an entry is checked, the manager launches the
script with the current interpreter and injects the project environment variables into the child
process, so analysis code and the main program are isolated from each other and a script crash
cannot affect experiments.
14 - Stream monitor
Stream monitoring: multi-tab data/image/command/message monitoring and single-stream panels.
Overview
quatm/GUI/streammonitor/ provides the stream monitor: MainWindow uses tabs to hold
monitors for data streams (Data), image streams (Image), commands (Command), message logs
(Message) and history (History), among others; each monitor tab is a StreamMonitor that
subscribes to a single data stream and displays it in real time.
Components
| Component | Description |
|---|
MainWindow | Monitoring main window: multi-tab container + menu |
StreamMonitor | Single-stream monitoring panel: subscribes to one data stream and displays it in real time |
MessageTableModel | Message log table model (timestamp/source/level/content, colored by level) |
ImageTableModel | Image stream metadata table (fixed time/source columns + dynamic key columns) |
AutoSortProxyModel | Auto-sorting proxy model ordered by descending timestamp |
Monitoring types
| Tab | Content |
|---|
| Data | Real-time curves/tables of numeric data streams |
| Image | Scientific image streams and their metadata |
| Command | Command / instruction streams |
| Message | Message log (timestamp/source/level/content) |
| History | Historical records |
Usage
from quatm.GUI.streammonitor import MainWindow, StreamMonitor
win = MainWindow(parent=parent) # multi-tab monitoring main window
panel = StreamMonitor(name="PMT", stream_type="Data", parent=parent) # single-stream panel
This monitoring interface is normally started as a standalone process; the single-stream
StreamMonitor panel can also be embedded in other windows.
15 - Sequence editor
Experiment code editing and sequence simulation: syntax highlighting, parsing and output-channel timing visualisation.
Overview
quatm/GUI/editors/editor_sequencer.py provides the edit and simulate tool (formerly Combi /
“open simulation window”): the CodeEditorParser composite main window combines the CodeEditor
on the left with the sequence visualisation on the right: after editing an experiment script you
can simulate a run with a single click, drawing the timing of each output channel as a step plot,
which makes experiment sequences easier to debug.
Components
| Component | Description |
|---|
CodeEditor | Simple code editor: line numbers, Python highlighting, automatic indentation and completion |
PythonHighlighter | Lightweight Python syntax highlighter |
LineNumberArea | Line-number margin widget on the left of the editor |
ExperimentParser | Parses an experiment: buildExperiment / simulateExperiment return the command stack |
ExperimentSequencer | Draws a sequence as a step plot of each output channel against time |
CodeEditorParser | Composite main window: Save / Simulate / Clear toolbar + dock layout |
Key methods
| Method | Description |
|---|
saveFile (CodeEditor) | Saves the current text to a file (returns True on success) |
simulateExperiment (CodeEditorParser) | Simulates the current version of the experiment and hands the command stack to the sequence visualisation |
buildExperiment / simulateExperiment (ExperimentParser) | Builds / simulates the experiment and returns the command stack |
plotSequence (ExperimentSequencer) | Draws the sequence dictionary as a per-channel timing step plot |
Usage
from quatm.GUI.editors.editor_sequencer import CodeEditorParser
win = CodeEditorParser(parent=parent, filename="experiments/my_experiment.py")
Note: parameter scans (nD scans, randomisation, etc.) are edited through the Edit button of
the experiment window (SequenceEditor, see Experiment window); the scan
definition is stored in the experiment property tree and shared with the remote-control platform.
16 - Process manager
A distributed process panel: starts and stops background processes grouped as Servers/Driver/GUI/Viewer/Monitor.
Overview
quatm/GUI/entries/processmanager.py provides the process manager: it reads the configuration by category (Servers / Driver / GUI / Viewer / Monitor) and generates a SingleProcess start/stop button panel for each script entry, so the whole experiment software suite is managed from one place. Processes are launched with the current interpreter, either as a script or as a module (python -m), carrying the project root and the parent-process watchdog environment.
Process categories
| Category | Description | Typical processes |
|---|
| Servers | Communication / property / data services | Data centre, property service, etc. |
| Driver | Hardware driver processes | FPGA and other device drivers |
| GUI | Graphical interface processes | Experiment browser, data manager, etc. |
| Viewer | Data viewing processes | Live plotting, scroll window, image viewer, etc. |
| Monitor | Monitoring / checking processes | Property checker, message monitor, etc. |
Components
| Component | Description |
|---|
ProcessManager | Category panel: reads the configuration and generates start/stop buttons for each entry |
SingleProcess | Single-entry panel: start / asynchronous termination / status polling with coloring |
resolve_script | Resolves the script entry from the configuration into a launch target (file path or module) |
Key methods (SingleProcess)
| Method | Description |
|---|
startProcess | Stops the old instance and then starts the subprocess with the current interpreter |
stopProcess | Terminates the subprocess asynchronously without blocking the GUI thread |
updateStatus | Periodically polls the exit status and colors it (running / exited normally / crashed) |
Usage
from quatm.GUI.entries.processmanager import ProcessManager
pm = ProcessManager(parent=parent)
pm.show()
The entries to manage are defined by the scripts in the configuration file; each entry is started
and stopped through its own button panel.
17 - Device manager
PyVISA device discovery and driver binding: enumerate, query and select instruments and associate driver files.
Overview
quatm/GUI/entries/device_manager.py provides the device manager (VISADeviceLister): it scans
the VISA instruments on the system through PyVISA, queries the device identity (*IDN?) and
selects a driver file to bind, then adds the device to the selected list as a DeviceInfo record
for use in device configuration.
Components
| Component | Description |
|---|
VISADeviceLister | VISA device list tool: two columns (enumeration/query + selected devices/driver files) |
DeviceInfo | Device description: name, type, driver file and additional parameters |
Workflow
1. refresh_devices() → scan the VISA bus and fill the device list
2. query_device_info() → send an *IDN? query to the selected device
3. select_driver_file() → choose and bind a driver file
4. add_device_to_selected() → add to the selected device list (de-duplicated)
Usage
from quatm.GUI.entries.device_manager import VISADeviceLister
dl = VISADeviceLister(parent=parent)
dl.refresh_devices() # list the connected USB/GPIB/LAN and other VISA devices
18 - Data storage
HDF5 data management: subscription, scheduled/manual saving and history read-back, with integrated plotting panels.
Overview
quatm/GUI/h5storage/ provides the HDF5 data storage GUI (a package split out of the former
single file, with its public symbols re-exported at package level): three kinds of data manager —
aggregate, single-stream and image — subscribe to data and save it automatically at a scheduled
time, periodically, or on a Looper command; the current snapshot can also be written to an .h5
file in the background, and historical measurements read back from a file.
Components
| Component | Module | Description |
|---|
H5StorageGui | main_window.py | Main window: data tabs, save/scheduled-save controls and plot management |
H5StorageDataMgr | data_manager.py | Host for the three data managers: subscription management, save/read-back, clearing |
H5SaveWorker | workers.py | Worker thread that writes the .h5 file, keeping disk I/O off the Qt event loop |
H5DataPlotter / EventPlotter | plotters.py | Plotting tabs for measurement data / event data |
XAxisWidget / YAxisWidget | axes.py | Data selection and filter-chain configuration for the x axis and each y axis |
BinningWidget / FilterWidget / FittingWidget | widgets.py / fitting.py | binning, filtering and ODR fitting processing chain |
Saving modes (H5StorageDataMgr)
| Method | Description |
|---|
toggle_scheduled_save | Toggles saving at a scheduled time (saves automatically and notifies when the time arrives) |
toggle_regular_save | Toggles periodic automatic saving every N hours |
scheduled_save_by_looper | Responds to save commands from the Looper (final / intermittent) |
savetoFile | Writes the current data snapshot to an .h5 file in the background |
loadFromFile | Loads historical measurements from an existing .h5 file and refreshes the interface |
selectDatadir | Chooses the target h5 file manually (to avoid falling back to the daily default name) |
clear | Clears the entire cache of the three data managers |
Usage
from quatm.GUI.h5storage import H5StorageGui # package-level re-export
win = H5StorageGui(parent=parent)
win.show()
Data saving is performed by the background H5SaveWorker: the GUI thread copies the data snapshot
first and then hands it to the worker, so writing to HDF5 on disk never blocks the interface.
19 - Live scroll plot
Live scrolling plots: ring buffers, running averages and data-stream subscription.
Overview
quatm/GUI/entries/live_plot.py provides live scrolling plots: ScrollWindow is a
multi-subplot main window in which each ScrollPlot subplot subscribes to several channels of
one data stream, maintains a ring buffer and scrolls scatter/curve data and a running average;
PlotDataEditor provides the data-source drop-down panel.
Components
| Component | Description |
|---|
ScrollWindow | Multi-subplot scrolling main window: restores or creates subplots from the properties; the toolbar can append subplots |
ScrollPlot | Scrolling subplot: ring buffer, scatter and running-average curves, context-menu operations |
PlotDataEditor | Data-source selection panel: drop-down candidates written back to the properties/parent plot |
| Method | Description |
|---|
feedData | Pushes a new data block into the buffer array |
setnewData | Periodically pulls new data into the buffer and refreshes the curves |
updatePlot | Flushes the valid data in the buffer to the scatter and running-average curves |
running_mean | Computes an N-point running average efficiently from cumulative sums and differences |
dataSelectDialog | Selects the data channels displayed in this plot |
subscribe_window | Opens the subscription editor to resubscribe the data stream |
configureWindow | Pauses refreshing and edits this plot’s properties inline |
deletePlot | Removes this plot from the parent window and clears its properties |
Usage
from quatm.GUI.entries.live_plot import ScrollWindow
host = ScrollWindow(name="PMT scroll monitor", parent=parent)
host.addPlot() # append a scrolling subplot
Afterwards, subscribe to data streams, select channels and adjust the display from the context
menu of a subplot. Configurations such as the buffer length, the running-average switch and the
window size are stored in the properties and restored automatically after a restart.
20 - Auto-save editor
Editor for the list of automatic save times: configures a set of periodic save instants (checkbox + date-time).
Overview
AutoSaveEditor (quatm/GUI/editors/auto_save_editor.py) is a pop-up editor used to maintain
the “time list” of periodic h5 automatic saves: each row = one (checkbox + date-time), and a
checked row triggers one automatic save at the corresponding time. It is invoked by the
data-storage panels.
Components
| Component | Description |
|---|
AutoSaveEditor | Dialog for editing the list: loads/writes back the properties, adds, deletes and edits rows, and confirms (Accept) |
DateTimeWidget | Editing row for a single automatic save time: index + checkbox + date-time picker |
Main methods
| Method | Function |
|---|
load_AutoSaveEditor_props(props, prop_name) | Reads the saved date-times and check marks from the properties, padding them up to the maximum number of items |
update_datetime(idx, datetime_save) | Saves a new date-time for a row |
update_ticked(idx, ticked) | Saves the checked state of a row |
accept() | Confirms: writes the list back to the properties and closes |
Usage
from quatm.GUI.editors.auto_save_editor import AutoSaveEditor
editor = AutoSaveEditor(props=props, max_items=24, parent=parent)
editor.load_AutoSaveEditor_props(props, "H5Storage/auto_save_times")
21 - Experiment helpers
Optical-tweezer alignment and stray-field compensation: a toolkit for automated calibration and optimisation.
Overview
Quatm wraps routine experiment calibration tasks as alignment/helper tools. The modules live in
quatm/GUI/alignment/, together with a number of standalone entry-point widgets, and provide
one-click runs and automatic optimisation.
ODT alignment helper — OdtAlignmentHelper
quatm/GUI/alignment/odt_helper.py — builds a single-axis control block for each piezo mirror
axis of xODT/cODT, and provides overall run/terminate together with compensation voltage and BA
laser parameter settings. The complete alignment sequence for each axis is: full-range scan with
the laser off → fine scan near the ion → publish the fit result → move to the target position.
from quatm.GUI.alignment.odt_helper import OdtAlignmentHelper
helper = OdtAlignmentHelper(parent=parent)
helper.run_all() # run the full alignment sequence (thread pool keeps axes sequential)
helper.terminate_all() # safely stop all axes
Stray-field compensation — CompensationHelper
quatm/GUI/alignment/comp_helper.py — obtains the compensation voltage from a voltage scan along
each trap axis as the intersection of two fitted lines, minimising the stray field automatically.
| Component | Description |
|---|
PlotWidget | Plot widget with one tab per trap axis |
ControlWidget | Control area for each axis plus a shared parameter area |
SingleAxisWidget | Scan parameters and “find compensation voltage” (intersection of the two fit lines) |
CompVoltageScan | Performs the voltage scan at the high and low rf confinement |
from quatm.GUI.alignment.comp_helper import CompensationHelper
comp = CompensationHelper(parent=parent)
Piezo mirror control — PiezoMirrorControl
quatm/GUI/entries/piezo_mirror_controler.py — a simple panel that steers the direction of the
mirror driven by a linear actuator using arrow keys/buttons; W/A/S/D correspond to up/left/down/right.
from quatm.GUI.entries.piezo_mirror_controler import PiezoMirrorControl
mirror = PiezoMirrorControl() # standalone window; control with WASD or the arrow keys
mirror.show()
22 - Data models
DictSyncModel/ListSyncModel: maps Python dictionaries/lists onto Qt tables automatically.
Overview
quatm/GUI/widgets/models.py provides the base classes that map Python dictionaries / lists onto Qt table models (derived from ARTIQ’s models). The table interfaces of the experiment queue, preparation station and looper are all built on them.
Core classes
| Class | Description |
|---|
_SyncSubstruct | Helper wrapping dictionary operations on the underlying container: triggers the refresh callback after append/insert/pop |
DictSyncModel | Dictionary→table model base class: maps backing_store through the headers/dataNames columns |
ListSyncModel | List→table model base class (the backing store is a list) |
ScheduleModel | Model for the experiment queue (ExperimentQueue): tasks sorted by priority and then by task number |
PrepModel | Model for the preparation station / looper: the backing store is a list whose order can be changed freely |
Usage
from quatm.GUI.widgets.models import DictSyncModel
model = DictSyncModel(
headers=["Name", "Value", "Unit"],
dataNames=["name", "value", "unit"],
init={"name": "param1", "value": 100, "unit": "µs"},
)
Concrete business models customise their behaviour by implementing three hooks in a subclass: convert turns a key/value/column into cell content, sort_key returns the sort key, and flags decides per-cell flags such as whether a cell is editable.
23 - Collapsible components
CollapsibleDialog: a collapsible/expandable tree dialog.
Overview
quatm/GUI/widgets/theexpandables.py provides the collapsible/expandable tree dialog
CollapsibleDialog: each “section” consists of a tree item with an expand/collapse button and a
content widget, which suits configuration interfaces and advanced parameter settings.
Components
| Component | Description |
|---|
SectionExpandButton | Button that expands/collapses a section |
CollapsibleDialog | Dialog container; subclasses override define_sections() to define all panels |
CollapsibleDialog provides add_section(title, widget) to add a panel directly; subclasses may
also override define_sections() to organise the tree structure in detail with add_button /
add_widget.
Usage
from quatm.GUI.widgets.theexpandables import CollapsibleDialog
class SetupDialog(CollapsibleDialog):
def define_sections(self):
self.add_section("Camera settings", some_widget)
self.add_section("Trigger settings", trigger_widget)
24 - Parameter boxes
Input controls bound to experiment parameters: float, boolean and combo parameter boxes that write changes back immediately.
Overview
quatm/GUI/widgets/arg_boxes.py provides widgets that bind experiment parameter classes to Qt
input controls: when the user changes a value it is written back to the experiment parameter
(stored internally in SI units, converted for display as needed). They are usually created
automatically by the “experiment parameter panel / parameter system” according to the parameter
type.
Controls
| Control | Parameter type | Description |
|---|
FloatBox | Numeric parameter | Floating-point input; converts units via unit and display_multiplier before writing back |
BoolBox | Boolean parameter | Checkbox; the value is written back as a boolean |
ComboBox | List of strings | Combo box; takes the value from the string list by the selected index |
Usage (schematic)
from quatm.GUI.widgets.arg_boxes import FloatBox, BoolBox, ComboBox
# numeric parameter: shown in kHz in the UI, stored in SI (Hz) underneath
fbox = FloatBox(argument=param_freq, unit="kHz", display_multiplier=1e3)
bcheck = BoolBox(parName="enable", value=param_enable)
cbox = ComboBox(parName="source", argument=param_source) # the parameter class holds a stringlist
These controls are normally generated automatically by the parameter system from the experiment’s
declarations, so there is no need to instantiate them by hand.
25 - Experiment parameter panel
DefaultExp + Dashboard: a whole-device parameter control panel built automatically from the device database.
Overview
The Dashboard (quatm/GUI/entries/dashboard.py) is the “control panel” main widget driven by the
default experiment (DefaultExp): DefaultExp assembles the parameters of the whole machine
from the device database automatically, and Dashboard then arranges these parameters in sections
by integer/float/boolean and as a grouped grid, forming an adjustable parameter control interface.
Class hierarchy
DefaultExp (experiment base class: auto-assembles device properties and groups them)
└─ Dashboard (control panel: sections + grouped grid + adaptive column count)
└─ manager widgets (IntManager / FloatManager / BoolManager / ComboManager …)
Key features
- Automatic assembly:
DefaultExp.build() walks the device library and creates a parameter
object for every attr/generic_attr device, classifying it by its group. - Sections by type: the interface is split into integer / float / boolean sections, and the
parameters are grouped within each section.
- Adaptive column count:
adaptive_columns() reflows according to the window width (starting at
4 columns, adding one column per ~200 px), so that narrow windows are not squeezed. - Immediate effect: changing a spin box writes back to the properties and straight through to the
hardware (the run body
run() is empty, which suits purely manual tuning).
Usage (schematic)
from quatm.GUI.entries.dashboard import Dashboard, DefaultExp
# DefaultExp auto-assembles the device parameters; Dashboard handles layout and grouping
page = Dashboard(parent=parent)
The “Parameter system” page describes the manager control classes behind it.
26 - FPGA DAC control
A simple manual control interface for the FPGA: 16 DAC voltages and 32 TTL bitmask settings.
Overview
quatm/GUI/entries/fpga_controller.py provides simple manual FPGA control for debugging and
calibration: each DAC voltage is set with a labelled -10 … +10 V spin box, and the 32 TTL outputs
are set with a grid of checkboxes.
Components
| Component | Description |
|---|
dacv(v) | Converts -10..+10 V into the 16-bit raw DAC code used by the FPGA |
DACsingle | A single DAC channel: a labelled -10..+10 V spin box, effective as soon as it is written |
DACvalues | All 16 DAC channels stacked vertically |
TTLQW | The 32 TTL outputs: an 8×4 grid of checkboxes, written as a single 32-bit mask |
simple_page | The DAC + TTL manual control page |
centralWidget / mainQMW | Tab container and simple main window (with a File/quit menu) |
Usage
from quatm.GUI.entries.fpga_controller import DACvalues, TTLQW
# obtain the low-level FPGA driver handle (controller) from the device database
page = DACvalues(controller)
# TTL: checkbox states are combined into a mask by the "Apply/state change" callback and written to the FPGA
ttl = TTLQW(controller)
If the hardware is missing or a write fails, it only prints an error instead of crashing, which
makes the interface easy to debug without hardware.
27 - Background worker threads
Worker/WorkerSignals: a generic QRunnable background-task wrapper.
Overview
quatm/GUI/widgets/browser_workers.py and helper_workers.py provide a generic background-thread
wrapper based on QRunnable: time-consuming tasks are executed in a thread pool, and
results/exceptions/progress are passed back to the GUI thread through signals.
Signals
| Signal | Description |
|---|
finished | Task completed (no data) |
error(tuple) | (exception type, value, traceback string) |
result(object) | The result returned by processing, of any type |
progress(int) | Progress percentage |
Usage
from quatm.GUI.widgets.browser_workers import Worker
worker = Worker(callback=my_long_task, arg1, kw=...) # passed through to the callback
worker.signals.finished.connect(on_done)
worker.signals.error.connect(on_error)
QThreadPool.globalInstance().start(worker)
The two implementations are largely the same: helper_workers.py is reused for helper/analysis
background tasks, while browser_workers.py is reused for browser-style panels.
28 - Experiment timeline
FPGA experiment timing visualisation: a chart viewer for DAC and TTL output channels against time.
Overview
quatm/GUI/entries/experiment_timeline.py provides the experiment timeline (Timeline): it
loads an experiment timing JSON file (experiment.json), draws the variation of each DAC/TTL
output channel over time as step subplots, and provides a checkbox panel of output channels to
decide which curves are visible, which makes experiment timing logic easier to debug and verify.
Components
| Component | Description |
|---|
Timeline | Timeline viewer main window: file loading, plotting and channel control |
TimelinePlotter | Timing visualisation widget: plots the state of each active output against time |
SelectorWidget | Output-channel checkbox panel: select all / select none decides the visible curves |
Key methods (Timeline)
| Method | Description |
|---|
load_file | Loads and parses the timing JSON, constructs the time series and refreshes the channel selector |
plot_experimental_sequence | Creates a separate subplot for each output according to the channel selection and draws a step plot |
decode_ttl_bitmask | Splits the 32-bit TTL bitmask into set/clear commands for the individual channels |
construct_time_sequences | Converts the step-wise timing into times/vals sequences organised per channel |
reverse_device_db_transformation | Device-database reverse transformation: maps internal names back to readable names (including the gauge calibration table) |
Usage
from quatm.GUI.entries.experiment_timeline import Timeline
tl = Timeline()
tl.load_file("experiment.json") # load a timeline file and plot it
tl.show()
29 - Scroll window
A multi-channel real-time scrolling data viewer window: subplot buffers, data-stream subscription and dynamic refreshing.
Overview
quatm/GUI/entries/scrollwindow.py provides the scroll window (ScrollWindow): a BMainWindow
subclass that acts as a multi-channel real-time scrolling data viewer main window. It lays out
ScrollPlot subplots in the requested number (two per row), distributes data blocks to the
corresponding subplots and refreshes them uniformly; PlotDataEditor provides the data/image stream
selection drop-down.
Components
| Component | Description |
|---|
ScrollWindow | Main window: creates subplots by count, distributes data and refreshes |
ScrollPlot | Scrolling subplot: subscribes to a data stream, ring buffer + periodic refresh |
PlotDataEditor | Data-source selection panel: drop-down candidates written back to the property ydata |
Key methods
| Method | Description |
|---|
initPlots | Creates the scrolling subplots by count and places them in the layout (two per row) |
feedData | Feeds a new data block into the buffer data array |
updatePlot | Batch-refreshes the filled interval of the buffer into the curves of each subplot |
setnewData (ScrollPlot) | Timer-driven data pull and curve refresh |
dataSelectDialog (ScrollPlot) | Opens a dialog to select the data channels to plot |
subscribe_window (ScrollPlot) | Opens the subscription editor to change the data stream |
configureWindow (ScrollPlot) | Pauses refreshing and edits this plot’s configuration inline |
Usage
from quatm.GUI.entries.scrollwindow import ScrollWindow
win = ScrollWindow(name="PMT scroll monitor", parent=parent, n_plots=2)
Data-stream subscription, data-channel selection and display configuration are performed on each
subplot through its context menu; the window can also be started as a standalone process.
30 - Subscription editor
Manages the subscription list of data/image streams: adds, deletes and hot-updates subscribed sources.
Overview
SubscriptionEditor (quatm/GUI/editors/subscription_editor.py) manages the program’s
subscriptions to data streams/image streams: it loads the stored subscriptions from the property
database, lists the available streams for selection, supports adding/deleting, and hot-updates the
list when properties change. It is commonly used as the data-source selection component of live
plotting/monitoring panels.
Main methods
| Method | Function |
|---|
loadSubscriptions() | Reads the saved subscriptions from the property database and loads them into the list |
update_streamlist() | Rebuilds the drop-down candidates from the currently available data streams |
add() | Adds the stream selected in the combo box to the subscription list and synchronises the properties |
deletemarked() | Removes the subscription items marked for deletion and synchronises the properties |
handle_property_changes(changes) | Listens for property changes, refreshes the list and broadcasts the change |
Usage
from quatm.GUI.editors.subscription_editor import SubscriptionEditor
editor = SubscriptionEditor(props=props, category="Data", parent=parent)
editor.loadSubscriptions()
31 - Table parameter
A QTableWidget-based editor for numpy structured arrays, used in parameter trees.
Overview
quatm/GUI/widgets/table_parameter.py provides a table editing node for ParameterTree: the
table_parameter parameter type works together with the table_parameterItem widget item to edit
numpy structured arrays (such as calibration tables and scan sequences) directly in the parameter
tree.
Components
| Component | Description |
|---|
table_parameter | Parameter type: accepts a numpy structured array as its value |
table_parameterItem | Corresponding widget item: creates a QTableWidget as the editor from the array dtype |
table_parameterItem creates columns from the array dtype (including field name/type), and cell
edits are written back to the array immediately; valueIsDefault() is used to decide whether the
value needs to be persisted separately.
Usage
from quatm.GUI.widgets.table_parameter import table_parameter
import numpy as np
dt = np.dtype([("freq", float), ("amp", float)])
param = table_parameter(name="scan_table", value=np.zeros(10, dtype=dt))
32 - Remote ROI
An ROI component that publishes color/position/size through the central property database and can be synchronised remotely.
Overview
quatm/GUI/widgets/ROI.py provides ROI: it inherits ROI and communicates its color, position
and size through the central property database under a unique name, so that remote clients can view
and adjust the ROI region in sync.
Methods
| Method | Description |
|---|
update_from_properties | Refreshes the ROI pen color and geometry from the color/position/size stored in the properties |
Usage
from quatm.GUI.widgets.ROI import ROI
roi = ROI(name="analysis_roi", parent=view) # name must be unique; duplicate names share properties
roi.update_from_properties()
Note: name is the key of this ROI in the central property database, so make sure it does not clash
with any name in the property dictionary; after a property change, calling update_from_properties
refreshes the ROI appearance and position in sync.
33 - Image viewer
The scientific image viewing main window: a central image display with row/column profile and histogram LUT dock panels.
Overview
quatm/GUI/entries/image_group.py provides the image viewing window (ImageWindow): the central
ImageDisplay is the main widget, with panels such as the row/column profiles and the
histogram LUT docked around it, for real-time monitoring and analysis of imaging experiments.
Layout
| Dock area | Widget | Purpose |
|---|
| Central | ImageDisplay | Subscribes to and automatically refreshes an image stream; can overlay a mask |
| Top | LivePlot (row profile) | Intensity profile of an image row |
| Right | LivePlot (column profile) | Intensity profile of an image column |
| Left | HistogramLUTWidget | Histogram color scale: level range and false-color mapping |
Each profile plot is a LivePlot container in which an UpdatingPlot subscribes to the corresponding
data stream and refreshes in real time (see Live plotting for details).
Display parameter persistence
Display parameters are stored in the Viewer/ namespace of the property database; they are
restored from the properties at construction time and saved automatically on close:
| Property | Meaning |
|---|
Viewer/minlevels / Viewer/maxlevels | Histogram display level range |
Viewer/gradient | False-color gradient preset (such as magma) |
Viewer/tick_position | Color scale tick positions (0–1 normalised list) |
After dragging the histogram levels or the color scale ticks, the current values are written back to
the properties through update_min_max and get_tick_position respectively, and closeEvent saves
the view range and ticks uniformly before the window closes.
Methods
| Method | Description |
|---|
update_min_max | Writes the current histogram level range back to the properties (min/max levels) |
get_tick_position | Walks the color scale ticks and returns them normalised to the 0–1 range |
closeEvent | Saves the view range and color scale ticks to the properties before closing |
Usage
from quatm.GUI.entries.image_group import ImageWindow
win = ImageWindow(name="absorption_imaging") # standalone top-level window; parent may be omitted
win.show()
This window is often started as a standalone process, but can also be embedded in other interfaces
as a BMainWindow instance. The image stream and the row/column profile streams are subscribed from
the context menus of the individual sub-panels (image display / profile plots).
34 - Property checker
A property-distribution watchdog: verifies that the Properties hub is really broadcasting updates.
Overview
PropertiesChecker (quatm/GUI/entries/properties_checker.py) is a property-distribution
watchdog: it sets a marker through one property handle and reads it back through a second handle;
if the hub has not propagated the change, it raises an alert indicating that the property service may
have stalled. The check runs periodically on a timer, so the window is displayed normally and the GUI
event loop stays responsive.
Usage
from quatm.GUI.entries.properties_checker import PropertiesChecker
checker = PropertiesChecker(parent=parent)
checker.check_once() # run one watchdog check manually
The panel itself calls check_once() periodically (each time setting the marker true and then false
and reading it back to verify).
35 - Data plotter
A general-purpose measurement data plotting component: data source/multiple y axes, filtering, binning and fitting.
Overview
quatm/GUI/alignment/plotter.py provides the data plotter (DataPlotterWidget): it subscribes to
and plots data acquired during a measurement. It supports switching the data source, an x-axis
selector and multiple y-axis selectors; each y axis is processed through a configurable data chain
(scaling factor → filter → binning/fitting) before being drawn in the plot area, and the result can
be exported to PDF.
Components
| Component | Description |
|---|
DataPlotterWidget | Main plot: data source drop-down, x-axis selector and several y-axis selectors (right-click toggles the configuration panel) |
YAxisWidgetHelper | y-axis data chain: scaling factor, filter, binning and fitting |
Key methods
| Method | Description |
|---|
setDataSource | Sets or changes the data source |
checkForNewData / updateDatasources | Checks for new data/new data sources and refreshes |
plotData | Adds a set of x/y data to the plot area as a new curve |
toggleConfig | Toggles the data source and y-axis configuration panels |
exportpdf | Exports the current plot as a PDF suitable for archiving |
clear | Clears the data summary and all curves in the plot area |
Y-axis chain (YAxisWidgetHelper): updateYscale sets the scaling factor, the context menu offers
addBinning for binning and addFitting for fitting (such as Gaussian/linear), and reevaluateData
re-runs the whole data chain after a filter or similar change.
Usage
from quatm.GUI.alignment.plotter import DataPlotterWidget
plotter = DataPlotterWidget(
axis_name="myaxis",
prop_root="...",
datastream_names=[".../data"],
n_yaxis=1,
parent=parent,
)
plotter.toggleConfig() # expand the data-source / x, y axis selection config