GUI: dynamic signals, sample tools, fast shutter row #144

Merged
duan_j merged 23 commits from feat/gui-dynamic-signals-and-sample-tools into main 2026-08-17 18:19:23 +02:00
Owner

Summary

  • Replace Qt method overrides with native mechanisms; install app-level event filters once, after their targets exist
  • Sample table: Unmeasured filter chip, Unflag context-menu action, click-the-# header to select all rows, sample count columns ordered by workflow (crossed wiring fixed), measured threshold now rotation count >= 1
  • Beamline setup panel: live energy/wavelength readout; new fast shutter row (status flag left, Open/Close buttons right) using the same display scheme and DAQ slots as the status bar flag, staff-gated
  • Status bar: ExpHutch shutter placed left of the fast shutter to match beam-path order
  • Busy overlay title wave animation; pending transition target breathes in the state strip
  • Help badge centered at top with right-aligned More link; expanded overlay opens under the badge

Test plan

  • Unit tests cover the changed lines, including the new fast shutter row (placeholder, both states, both button signals)
  • CI pytest runs with -s -vv

Generated with Claude Code

## Summary - Replace Qt method overrides with native mechanisms; install app-level event filters once, after their targets exist - Sample table: Unmeasured filter chip, Unflag context-menu action, click-the-# header to select all rows, sample count columns ordered by workflow (crossed wiring fixed), measured threshold now rotation count >= 1 - Beamline setup panel: live energy/wavelength readout; new fast shutter row (status flag left, Open/Close buttons right) using the same display scheme and DAQ slots as the status bar flag, staff-gated - Status bar: ExpHutch shutter placed left of the fast shutter to match beam-path order - Busy overlay title wave animation; pending transition target breathes in the state strip - Help badge centered at top with right-aligned More link; expanded overlay opens under the badge ## Test plan - Unit tests cover the changed lines, including the new fast shutter row (placeholder, both states, both button signals) - CI pytest runs with -s -vv Generated with [Claude Code](https://claude.com/claude-code)

Coverage report (automated)

Total line + branch coverage: 49%
Diff coverage vs main: 89% (minimum 80%)

Full report: coverage artifact on this run.

Coverage report (automated) Total line + branch coverage: 49% Diff coverage vs main: 89% (minimum 80%) Full report: coverage artifact on this run.
duan_j added 16 commits 2026-08-17 15:34:59 +02:00
Per-window installs of the two QApplication-level filters (cursor,
wheel guard) stacked one stale copy per MainWindow; now installed once
per process, parented to the app. closeEvent overrides in the baton
dialogs replaced by finished.connect(timer.stop); keyPressEvent in
VideoGraphicsView replaced by WidgetShortcut QShortcuts; hover
enter/leave overrides in PlayPauseButton replaced by underMouse();
ValueLabel inherits ClickableLabel instead of duplicating its
mousePressEvent. Dead raster-timer branch and a PyQt5-era
event-position fallback deleted; redundant try/except and getattr
guards dropped now that class-level defaults cover pre-init reads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
While Moving, the target state sat solid blue and read as already
reached. An opacity pulse (QGraphicsOpacityEffect + QPropertyAnimation)
marks it as in progress; a stylesheet animation would repolish the
button every frame, which the guarded updates in _apply_highlight
exist to avoid.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BEAMLINE BUSY and the robot warnings now hop per letter (clipped sine)
so they read as an in-progress signal, not a frozen label. Gated by a
new BusyOverlayStyle.animate flag: 'In viewing mode' stays static
because it is a passive notice. No timer - the phase rides the wall
clock and repaints ride the ~20 fps camera frames, so the wave freezes
with a stalled feed (which has its own error surface).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both bottom-dock tables now list mount -> raster -> screening ->
rotation, the order the work actually happens in. In the Auxiliary
table the swap also fixes a real bug: data() feeds get_entry
header-index-minus-1, but rotation/raster were crossed against the
header, so 'Raster count' displayed rotation counts and vice versa.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Complement of Measured (rotation count <= 1): one click shows
everything still to be done, so it can be selected wholesale and
queued. Plain chip like All - unmeasured rows carry no tint, so
there is no legend color to wear; cross-status marks (queued/
flagged) show like in the Measured view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Below Mount/Unmount, acting on the selection like the queue actions.
Flagging happens by dropping rows on the Flagged chip; until now
there was no way back. Disabled when nothing in the selection is
flagged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New 'Current energy / lambda' row above the setpoint, fed per DAQ
tick from diffraction.energy_keV; the setpoint row is renamed
'Set Energy (keV)' so 'what is' and 'what to set' are distinct.
Guards energy 0.0 (server's detector-unavailable fallback) because
the wavelength property divides by it. The copied energy row in the
experiment configuration is renamed to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beam-path order in the status bar: the hutch shutter sits upstream
of the fast shutter, so it reads left to right along the beam.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unmeasured and All are both 'what is left' views; the hand-applied
labels (Queued/Flagged/Measured) follow. The 1px spacing keeps the
chips readable as separate buttons instead of one solid bar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The '?' badge sat in a corner, which drifts oddly when the view is
scaled to fit; the top middle stays put and out of the sample's way.
Transparency unchanged - visible enough to find, not disturbing.
'More... (F1)' moves to the overlay's right edge, reading like a
dialog's action button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Confirmed by Dawn: any rotation data counts as measured; unmeasured
is exactly rotation count 0. The old > 1 rule left single-rotation
samples looking like still-to-do work in the Unmeasured view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The status column holds no sortable data, so its header click slot
was free: it now does Ctrl+A, pairing with the Unmeasured chip to
grab everything still to be done in one motion. The sort indicator
is put back afterwards - the click drags it onto '#' even though
the model ignores sorting there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The '?' badge moved to the top middle but the cheat sheet still
opened bottom-left, sending the mouse across the view to close it.
The overlay now drops in where the badge was.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Status flag on the left (same rich-text scheme as the status bar,
theme-aware via set_theme), Open/Close buttons on the right, wired
to the same daq.open_shutter/close_shutter slots staff-gated in
main_window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: pyright-style ignore for setGraphicsEffect(None)
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 58s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m3s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m16s
CI / test (3.13) (pull_request) Successful in 1m25s
CI / lint (pull_request) Successful in 2m6s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m56s
CI / test (3.12) (pull_request) Successful in 2m7s
CI / test-with-coverage (pull_request) Successful in 2m34s
CI / coverage-analysis (pull_request) Successful in 4s
03ae77fa74
basedpyright disables mypy-style 'type: ignore' comments by default, so
the diff-quality gate still counted the violation; match the existing
'pyright: ignore[reportArgumentType]' convention from main_window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j force-pushed feat/gui-dynamic-signals-and-sample-tools from 2b660e31c7 to 03ae77fa74 2026-08-17 15:34:59 +02:00 Compare
duan_j marked the pull request as work in progress 2026-08-17 15:50:31 +02:00
duan_j added 3 commits 2026-08-17 16:24:12 +02:00
Mirrors the Beamline setup live readout above Set Energy, fed by the
same DAQ tick with the zero-energy guard and repaint skip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The queue engine keeps insertion order and Run pops its head, but the
Queued view sorted by the dewar table's header sort - the top row was
not what runs next. set_queued_ids now keeps the order it is fed (the
queue model's order) and the Queued view displays exactly that; header
clicks no longer reorder it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat: queue controls live only in the Queued view
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 1m6s
CI / test (3.12) (pull_request) Successful in 1m7s
CI / lint (pull_request) Successful in 1m15s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m24s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m24s
CI / test (3.13) (pull_request) Successful in 1m32s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m28s
CI / test-with-coverage (pull_request) Successful in 1m48s
CI / coverage-analysis (pull_request) Successful in 4s
6ace1fcbcf
Run/Remove selected/Clear list act on the queue, whose order only the
Queued chip view shows - in other views they are greyed out with a
tooltip pointing at the chip. Run stays enabled while automation runs
so pausing is always possible. Applies to the docked trio and the
pop-out clones alike.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j added 1 commit 2026-08-17 16:49:31 +02:00
feat: sample camera starts in scale-to-fit
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 1m8s
CI / test (3.12) (pull_request) Successful in 1m11s
CI / test (3.13) (pull_request) Successful in 1m9s
CI / lint (pull_request) Canceled after 1m20s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Canceled after 1m10s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Canceled after 1m7s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Canceled after 1m5s
CI / test-with-coverage (pull_request) Canceled after 1m2s
CI / coverage-analysis (pull_request) Canceled after 0s
b58c23d0f6
_autoscale now defaults on, so the view fits from the first layout
(the constructor's placeholder already carries the camera size and
resizeEvent refits on every resize). update_pixmap additionally
refits when the frame size changes - the real stream resolution can
differ from the placeholder, and only a view resize refit before.
Right-click 'Scale to fit' still toggles back to 1:1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j added 1 commit 2026-08-17 16:50:52 +02:00
fix: no auto-switch to beamline view during Sample alignment
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 1m3s
CI / test (3.13) (pull_request) Successful in 1m9s
CI / test (3.12) (pull_request) Successful in 1m16s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m13s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m17s
CI / lint (pull_request) Successful in 1m33s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m32s
CI / test-with-coverage (pull_request) Successful in 1m42s
CI / coverage-analysis (pull_request) Successful in 3s
cb5837d90f
Busy in Sample alignment IS the alignment (centring moves) - yanking
the view away to the combined beamline tab hid exactly what the user
was doing. The motion-watch switch now skips that state; robot
exchange and other busy transitions still switch as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j added 2 commits 2026-08-17 17:00:28 +02:00
test: pin the Sample-alignment motion-watch exception
CI / test (3.11) (pull_request) Successful in 1m12s
CI / test (3.12) (pull_request) Successful in 1m13s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m12s
CI / lint (pull_request) Successful in 1m30s
CI / test (3.13) (pull_request) Successful in 1m25s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m21s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m27s
CI / test-with-coverage (pull_request) Successful in 1m43s
CI / coverage-analysis (pull_request) Successful in 4s
CI / lint (push) Successful in 28s
Docs build and publish / docker (push) Successful in 8s
CI / test (3.11) (push) Canceled after 35s
CI / test (3.12) (push) Canceled after 31s
CI / test (3.13) (push) Canceled after 30s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 26s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 25s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 21s
CI / test-with-coverage (push) Canceled after 20s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 24s
e9428ca64d
Robot motion switches to the combined view; alignment beginning
switches straight back while busy is still set, and staying busy in
alignment never re-triggers. Piggybacks on the init test's window -
extra MainWindow constructions feed the PySide teardown flake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j marked the pull request as ready for review 2026-08-17 18:19:02 +02:00
duan_j merged commit e9428ca64d into main 2026-08-17 18:19:23 +02:00
Sign in to join this conversation.