GUI: busy-state UX, role login defaults, control panel fixes #140

Merged
duan_j merged 27 commits from fix/graphics-and-recovery into main 2026-08-17 13:42:00 +02:00
Owner

Batch of GUI improvements from beamline operation feedback.

Safety principle: opening the GUI never moves hardware

  • A login-default state POST (staff -> Beam location, users -> Sample
    alignment) was added during this branch and then deliberately REMOVED:
    a state transition can move motors, and merely starting or logging
    into the GUI must never move anything. The GUI now only adopts the
    state the server reports and posts no transition on startup. A comment
    in update_daq_status pins this decision so it does not come back.
  • Same principle applied to input paths: typing into motor boxes stages
    values only; nothing moves until the Move button is clicked. Wheel
    events on value widgets are guarded, and a left click on the state
    strip never starts a transition (right-click menu only).

Busy-state UX

  • BEAMLINE BUSY flag in Gonio camera, Beamline view and combined view is
    now plain shadowed status text (same look as the sample camera
    warnings), not the badge pill that read as a button.
  • The state strip greys out completely while the beamline is busy or
    Moving, so no transition looks clickable mid-operation.
  • While busy, the video tab switches to the Beamline combined view so
    the motion can be observed, and returns to the Sample camera when done
    (generalizes the old robot-mount auto-switch).

Role-dependent behavior

  • Non-staff now see the Beamline setup / ABR meas. pos. / Beam
    configuration banners as locked stand-ins instead of nothing; clicking
    one explains the staff gate and points to the local contact.
  • A click on the pgroup-gated (disabled) Beamline tab is no longer eaten
    silently: staff get the visitor-pgroup explanation, users the
    staff-only message (same pattern as the Auxiliary-puck tab).

Motor and panel work

  • Motor entries stage values and apply moves in one click; new
    MotorMoveGroup widget with unit tests.
  • Light slider changes emit from every input path, readback guarded.
  • Smargon Chi/Phi row unsqueezed, GM labels reserve sign width.
  • Change Energy row in experiment configuration; Monochromator panel
    renamed to Beamline setup (case-consistent).

Stability and CI

  • Prediction subscriber no longer aborts libzmq or logs per-frame decode
    errors.
  • Exposure wheel guard, banner PNG fallback for RHEL9 consoles, misc
    naming and comment cleanups.
  • All four CI gates green: ruff format and lint, basedpyright diff at
    100 percent, diff coverage vs main at 100 percent (was 62).

Generated with Claude Code (https://claude.com/claude-code)

Batch of GUI improvements from beamline operation feedback. Safety principle: opening the GUI never moves hardware - A login-default state POST (staff -> Beam location, users -> Sample alignment) was added during this branch and then deliberately REMOVED: a state transition can move motors, and merely starting or logging into the GUI must never move anything. The GUI now only adopts the state the server reports and posts no transition on startup. A comment in update_daq_status pins this decision so it does not come back. - Same principle applied to input paths: typing into motor boxes stages values only; nothing moves until the Move button is clicked. Wheel events on value widgets are guarded, and a left click on the state strip never starts a transition (right-click menu only). Busy-state UX - BEAMLINE BUSY flag in Gonio camera, Beamline view and combined view is now plain shadowed status text (same look as the sample camera warnings), not the badge pill that read as a button. - The state strip greys out completely while the beamline is busy or Moving, so no transition looks clickable mid-operation. - While busy, the video tab switches to the Beamline combined view so the motion can be observed, and returns to the Sample camera when done (generalizes the old robot-mount auto-switch). Role-dependent behavior - Non-staff now see the Beamline setup / ABR meas. pos. / Beam configuration banners as locked stand-ins instead of nothing; clicking one explains the staff gate and points to the local contact. - A click on the pgroup-gated (disabled) Beamline tab is no longer eaten silently: staff get the visitor-pgroup explanation, users the staff-only message (same pattern as the Auxiliary-puck tab). Motor and panel work - Motor entries stage values and apply moves in one click; new MotorMoveGroup widget with unit tests. - Light slider changes emit from every input path, readback guarded. - Smargon Chi/Phi row unsqueezed, GM labels reserve sign width. - Change Energy row in experiment configuration; Monochromator panel renamed to Beamline setup (case-consistent). Stability and CI - Prediction subscriber no longer aborts libzmq or logs per-frame decode errors. - Exposure wheel guard, banner PNG fallback for RHEL9 consoles, misc naming and comment cleanups. - All four CI gates green: ruff format and lint, basedpyright diff at 100 percent, diff coverage vs main at 100 percent (was 62). Generated with Claude Code (https://claude.com/claude-code)
duan_j added 20 commits 2026-08-13 16:10:09 +02:00
fix: prevent libzmq abort and per-frame decode errors in prediction subscriber
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
69178945e5
Sniff for '{' before JSON-parsing zmq parts: JPEG frames start with 0xff
and raised UnicodeDecodeError on every frame. Stop closing the socket
from the GUI thread in stop(): zmq sockets are not thread-safe and the
concurrent close/recv tripped the signaler.cpp POLLIN assertion (core
dump); run() closes it in its own thread via the 500ms RCVTIMEO.

uv.lock: sync version to 0.8.3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: exposure wheel only increased when Alt held
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
ac5936f7be
Qt xcb/windows swap wheel axes while Alt is held, so angleDelta().y() is 0
and copysign(_, 0) is always positive. Fall back to angleDelta().x() and
ignore genuine zero deltas.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: stage motor entry values, apply moves in one click
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
e18c7feded
Chi/Phi boxes in the Smargon tab no longer move on Enter: edits stage
the target (orange), the Move button sends all staged axes at once
(green), and boxes return to neutral when the motor reports arrival.
Out-of-range entries stay red and are never sent; limit tips pop as
tooltips. MotorMoveGroup is opt-in per box so other panels can adopt
the same logic later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Underlined 'More… (F1)' line at the bottom of the painted help box emits
open_full_help, wired to the existing Mouse / Keyboard Controls dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: reserve sign width for GM labels; annotate red-highlight mismatch
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
c837e6c44d
Value column was content-sized, so rows shifted whenever a value flipped
negative. Labels now reserve '-88.888' width; right alignment keeps digits
anchored. Also TODO: red colors on live aerotech while labels display
aerotech_meas - intent unconfirmed, do not change condition until decided.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: unsqueeze Chi/Phi row and widen Step box in smargon panel
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
70107e6219
Move button gets its own full-width row instead of sharing row 1 with the
Chi/Phi entries; Step box spans columns 1-4 instead of the narrow Chi
column.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copy of the Beamline Setup energy row (keV display, eV emit), placed
below the ML Loop Centring / Make Raster Grid row and always visible.
Wired to the same daq.change_energy; not staff-gated, server enforces
write permission.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: rename Monochromator panel to Beamline Setup, add WIP beam-to-box row
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
bb447b1567
Display title only; class and file keep their names. New full-width
'Move Beam to Box (center) (WIP)' button under the Energy row, disabled
with a coming-soon tooltip until a DAQ endpoint exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: send light slider changes from every input path, guard readback
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
869a66ff4b
Sliders only emitted on sliderReleased, so right-click+wheel, arrow keys
and groove clicks changed the value locally without a PUT and the 500 ms
status poll snapped it back. setTracking(False) + valueChanged now sends
one PUT per deliberate change; readback setValue is wrapped in
blockSignals to avoid echoing PUTs, and a 1.5 s grace window stops
in-flight stale status responses from bouncing the slider.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: disable Beamline tab when active pgroup is outside token pgroups
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
c5a6a175bd
Switching to a visitor pgroup means running a user experiment: grey out
the Beamline tab (tooltip: beamline scientists only), bring the
Experiment tab forward with Dataset path and Exp. Config. opened.
Gate applies on transitions only so manual tab choices survive the
1 Hz status ticks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Staff land in BeamLocation, users in SampleAlignment. One-shot on the
first status tick where the GUI owns the session and the beamline is
idle, so a running operation or a watch-only GUI is never disturbed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beamline setup, ABR meas. pos. and Beam configuration banners stay
visible for non-staff instead of vanishing, but are locked stand-ins:
clicking one pops the staff-only notice (same pattern as the
Auxiliary-puck tab) telling the user to log in as staff or contact
their local contact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore: rename Beamline Setup to Beamline setup, comment tweaks
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
75d7bed380
Panel title case now matches the other banner names. Also clarify the
splash banner comment in gui.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: busy flag in Axis views as plain status text, not a badge
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
69d552c068
Gonio camera, Beamline view and the combined view showed BEAMLINE BUSY
in the rounded badge pill, which reads as a clickable button. These
views are passive, so draw the same shadowed colored text the sample
camera uses for its warnings; the pill stays only on the sample-camera
session badges, which really are click targets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Busy now empties the available-target set exactly like Moving, so no
entry looks clickable and no transition can be posted mid-operation
(same guard the status-bar menu already had). The current/pending
entry keeps its highlight as orientation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: switch to Beamline combined view while busy
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / test (3.11) (pull_request) Successful in 1m27s
CI / test (3.12) (pull_request) Successful in 1m27s
CI / lint (pull_request) Failing after 1m31s
CI / test (3.13) (pull_request) Successful in 1m27s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 2m1s
CI / test-with-coverage (pull_request) Successful in 2m12s
CI / coverage-analysis (pull_request) Failing after 3s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 2m48s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 2m44s
58bde9379b
Generalize the robot-mount auto-switch: any busy period now brings up
the combined view so the motion can be watched, and the sample camera
returns when busy clears. Edge-triggered, so manual tab choices
survive between transitions; _mounting renamed to _watching_motion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Coverage report (automated)

Total line + branch coverage: 48%
Diff coverage vs main: 99% (minimum 80%)

Full report: coverage artifact on this run.

Coverage report (automated) Total line + branch coverage: 48% Diff coverage vs main: 99% (minimum 80%) Full report: coverage artifact on this run.
duan_j added 1 commit 2026-08-14 07:23:23 +02:00
chore: satisfy ruff format and SIM201 lint
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / lint (pull_request) Failing after 1m3s
CI / test (3.11) (pull_request) Successful in 1m29s
CI / test (3.12) (pull_request) Successful in 1m28s
CI / test (3.13) (pull_request) Successful in 1m27s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m30s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m43s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m41s
CI / test-with-coverage (pull_request) Successful in 2m6s
CI / coverage-analysis (pull_request) Failing after 3s
b4896105ab
ruff format collapsed lines the Beamline setup rename and other edits
left splittable; SIM201 wanted != over 'not ... ==' in the prediction
subscriber's JSON sniff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j added 2 commits 2026-08-14 08:52:04 +02:00
Rename NumberLineEdit.validator to range_validator: the instance attr
shadowed QLineEdit.validator() and broke type checking at call sites.
Annotate the smargon _move_axes dict and cast the stand-in self in the
prediction subscriber test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test: lift diff coverage over the 80 percent gate
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / lint (pull_request) Successful in 1m3s
CI / test (3.11) (pull_request) Successful in 1m25s
CI / test (3.12) (pull_request) Successful in 1m25s
CI / test (3.13) (pull_request) Successful in 1m24s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m32s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m37s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m41s
CI / test-with-coverage (pull_request) Successful in 2m4s
CI / coverage-analysis (pull_request) Successful in 3s
16e112f44a
Promote the illumination panel's __main__ self-check to a real pytest
file so CI counts it, add busy-overlay render checks (status text
paints, video view routes through it), and cover the motor group's
incomplete-entry and below-min Enter paths. Local diff coverage from
the gui suite alone: 82 percent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j marked the pull request as work in progress 2026-08-14 09:05:25 +02:00
duan_j added 2 commits 2026-08-14 09:20:46 +02:00
Two changes folded together because they touch the same status path:

- Revert the login-default state POST: a state transition can move
  motors, and merely opening the GUI must never move hardware. The GUI
  now adopts whatever state the server reports.
- A click on the pgroup-gated (disabled) Beamline tab used to be eaten
  silently by the tab bar. An event filter now pops the explanation:
  visitor-pgroup wording for staff, the staff-only message for users
  (same pattern as the Auxiliary-puck tab).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test: cover every changed line in the branch diff
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / lint (pull_request) Successful in 1m17s
CI / test (3.11) (pull_request) Successful in 1m44s
CI / test (3.12) (pull_request) Successful in 1m41s
CI / test (3.13) (pull_request) Successful in 2m1s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m57s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 2m0s
CI / test-with-coverage (pull_request) Successful in 2m26s
CI / coverage-analysis (pull_request) Successful in 3s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Failing after 18m13s
aa6c226103
Diff coverage vs main now 100 percent from the gui suite alone:
non-staff locked banners and both gated-tab popups, remote-close
bookkeeping in update_daq_status, smargon staged moves, camera More
link and Alt-wheel axis fallback, number box relimits, energy row
emit, and the gui.main() banner/graceful-exit path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j marked the pull request as ready for review 2026-08-14 09:26:22 +02:00
duan_j added 2 commits 2026-08-14 10:54:31 +02:00
Per-test lines and live (uncaptured) output: the pxiii_bec job sat
silent at 81 percent for hours, and file-level dots could not say which
test wedged. With -s -vv a hang names its test and streams the last
log lines before it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: install gate event filters after eventFilter's targets exist
CI / lint (pull_request) Successful in 1m13s
CI / test (3.12) (pull_request) Successful in 1m30s
CI / test (3.11) (pull_request) Successful in 1m38s
CI / test (3.13) (pull_request) Successful in 1m40s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m38s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m41s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m51s
CI / test-with-coverage (pull_request) Successful in 2m6s
CI / coverage-analysis (pull_request) Successful in 3s
CI / lint (push) Successful in 31s
Docs build and publish / docker (push) Successful in 15s
CI / test (3.11) (push) Canceled after 37s
CI / test (3.12) (push) Canceled after 33s
CI / test (3.13) (push) Canceled after 32s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 28s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 27s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 23s
CI / test-with-coverage (push) Canceled after 22s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 21s
190e728734
The locked-banner and Beamline-tab-bar filters were installed mid
__init__, but MainWindow.eventFilter reads sample_lists_tabs, which is
created later - every event delivered in between raised AttributeError
inside the filter ('Error calling Python override of eventFilter()'
spam) and broke widget teardown, cascading errors across CI tests and
wedging the pxiii_bec job. Filters now install late, next to
installEventFilter(self), and the filter guards its attribute reads so
construction/teardown-time events can never raise.

Also fold the staff-gate assertions into test_main_window_init: every
extra MainWindow construction raises the odds of the pre-existing
PySide SystemError flake, so don't build a window just for them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j merged commit 190e728734 into main 2026-08-17 13:42:00 +02:00
Sign in to join this conversation.