Commit Graph
99 Commits
Author SHA1 Message Date
duan_jandClaude Fable 5.1 bd2b25e156 fix(gui): reach the file-exists guard from every tab, test exact names
The tabs are handed parent=DataCollectionSettings but
QStackedWidget.addWidget() reparents them to the stack, so the guard's
parent() lookup never found file_path_panel and every Run (rotation,
screening, raster, X-ray centering, Simple) skipped the check. Lookup
now walks up the widget tree; regression test drives all five runs
through the real DataCollectionSettings.

The "taken" test is exact again: <run>_master.h5 plus the DAQ's
derived <run>_raster2d/_raster1d_master.h5. The directory test and the
_*_master.h5 glob are gone: neither is a file a run writes, and they
painted run numbers red that nothing would ever produce.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5.1 19361974d3 fix(gui): make the file-exists guard literal: popup, run +1, no scan
update_filename() still skipped to the next free run number on every
edit and refresh, so the name was always free by the time Run was
clicked and the guard never fired. The name is now exactly what the
fields say (label turns red on a clash); on Run, an existing target
pops "File already exists", bumps the run number by one and does
nothing else.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5.1 50277edda2 fix(gui): pop up "File already exists" instead of silently bumping the run
The Run guard called set_scan_kind(), which runs update_filename() and
skips to the next free run number before the existence check, so the
check never fired and the scan started under a new number without a
word. Now the check runs first, then a warning box names the existing
master file and the run number it moved to, and the run is blocked.

- Simple tab "Run rotation" had no guard at all; same guard added
- "Taken" also matches <run>_*_master.h5: X-ray Centering writes
  <run>_raster2d_master.h5, which the exact name missed
- Camera context-menu "Evaluate grid" presses the panel button, so it
  gets the guard and stays inert while the button is disabled
- Dead next_free_run_from() removed

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5.1 27649375b1 fix(gui): zoom-proof the Dewar table and control heights, theme the mounted HUD ink
Dewar "#" column sat 1px above the other rows at some zoom steps: the
frozen-column overlay sizes its own header from "#" alone while the main
header also sees the symbol columns, whose fallback-font glyphs have a
taller line box. The overlay header is now pinned to the main header
height on resize and (deferred, receiver-bound) on font/style change.

Zoom also left columns at their startup widths (headers truncated) and
clipped descenders in every button/combo/entry box: the 16px height pin
in both sheets was raw px while the body font grew to 18/21px. Columns
re-autosize on FontChange and the pin scales with the font ladder.

Camera "Currently mounted" HUD: black ink on a white halo in the light
themes, white on black in Sunset, instead of white-on-black everywhere.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5.1 4735b50d2c fix(gui): keep queue head until /status confirms unmount on Mount next
Mount next popped the mounted head before posting the exchange, so a
failed unmount dropped a sample that was still on the gonio. Now the head
stays queued and is removed only when /status shows a different (or no)
sample mounted; a failed exchange leaves the queue intact for a retry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5 8bd7e30e0b feat(gui): pin status-bar operables right, stop readout width jitter
- Fast Shutter / State / p-group / Session are addPermanentWidget now:
  QStatusBar itself pins them to the right corner, so wrapping of the
  other readouts never moves them
- passive readouts keep wrapping in the FlowHost on narrow windows
- grow-only min-width ratchet on every readout: changing number widths
  used to re-flow the row each DAQ tick and jitter every neighbour

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 14:27:19 +02:00
duan_jandClaude Fable 5 5cb57f4737 feat: status-bar readouts wrap on narrow windows instead of clipping
CI / lint (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 59s
CI / test (3.12) (pull_request) Successful in 1m4s
CI / test (3.14) (pull_request) Successful in 1m5s
CI / test (3.13) (pull_request) Successful in 1m10s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m12s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m14s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m25s
CI / test-with-coverage (pull_request) Successful in 1m38s
CI / coverage-analysis (pull_request) Successful in 3s
All readouts (sharpness ... Cryo | Fast Shutter | State | p-group |
Session) now live in one FlowLayout host: when the window gets
narrow the bar grows extra rows and every value stays readable,
where QStatusBar used to clip the permanent group and hide the rest.
FlowLayout is the canonical Qt example trimmed down (Qt ships no
built-in); the host pins its minimum height to the wrapped height
because QStatusBar ignores heightForWidth. The old left/right
addWidget vs addPermanentWidget split is gone with the wrap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 14:37:19 +02:00
duan_jandClaude Fable 5 33239bf51f refactor: drop the camera-region splitter, side columns fixed again
User decision: the drag-resize bars beside the Sample camera view are
not wanted - the side panels don't need resizing. Back to the plain
HBox with zoom-scaled fixed column widths; the View-menu collapse
toggles and the camera minimum-width clamp stay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 14:37:19 +02:00
duan_jandClaude Fable 5 26fbe2ca42 fix: give deferred singleShot callbacks a receiver context
CI / lint (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 1m2s
CI / test (3.12) (pull_request) Successful in 1m8s
CI / test (3.14) (pull_request) Successful in 1m7s
CI / test (3.13) (pull_request) Successful in 1m16s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m14s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m21s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m32s
CI / test-with-coverage (pull_request) Successful in 1m37s
CI / coverage-analysis (pull_request) Successful in 3s
Root cause of the long-standing cross-test SystemError flake
("QPushButton returned NULL", CI failing test_title_label after an
unrelated file): TitleLabel schedules
QTimer.singleShot(0, self._apply_collapsed) at construction; when the
banner is deleted before the event loop spins (what every panel test
does), the pending timer fires into the dead C++ object and poisons
whatever test runs next. Deterministic repro added as a regression
test. The receiver-context overload singleShot(0, self, ...) auto-
cancels the callback when the widget dies. Same fix for the two
deferred main-window callbacks (dock split, floating-dock enlarge).

Note for the record: the LOCAL segfault storm this afternoon was the
dev machine's disk hitting 100% full (uv cache at 15G) — Bus errors
from mmap on a full disk, unrelated to any code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 13:29:07 +02:00
duan_jandClaude Fable 5 e31b0e9a6f fix: cast the mocked daq.unmount for basedpyright on CI
CI's basedpyright (latest from the index, newer than the local venv)
types win.daq as the real DAQWorker, so assert_not_called on the
patched-in MagicMock flagged the two new gate-test lines.

Note: the pytest job segfaults are NOT from this branch — plain
origin/main crashes 2/4 runs under PySide6 6.11.2 (what CI installs;
uv.lock pins 6.9.0 which is stable). Needs a separate dependency fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 13:28:16 +02:00
duan_jandClaude Fable 5 809959df33 feat!: remove Playlist Mode and Automation View, unpin window minimum
Why the width shrink only worked after a Playlist Mode round-trip:
Qt auto-pins a top-level window's minimum size to its layout minimum,
and returning from Playlist Mode hid the Information dock (and every
side dock), which happened to drop that layout minimum. The main view
now gets an explicit small minimum (320x240) which disables the
auto-pin outright, so the window shrinks directly and the dock
contents clip or scroll instead.

The Playlist Mode (Ctrl+6) and Automation View (Ctrl+5) prototypes
are deleted per user decision: panels, pages, the content stack (the
standard page is the only page left), menu actions, camera fan-out to
their view copies, portrait alert interceptors (now plain runtime
notification routers), and their styles (portraitRoot, compact*
cards, COMPACT_* and FONT_HERO knobs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 13:28:16 +02:00
duan_jandClaude Fable 5 fd0edffdc9 WIP: resizable camera region, menu panel collapse, zoom ladder
- center splitter: left controls | camera tabs | beamline column;
  drag resizes the camera width, View menu check actions collapse
  the fixed-width side panels (no on-screen buttons)
- font zoom restricted to a 100/125/150 percent ladder; the
  fixed-width side boxes scale with it so zoomed text does not clip
- camera tab widget min width clamped to 1/3 of its natural minimum
- resize bars: short centered grip PNGs shared by QMainWindow
  separators and QSplitter handles, single style source in styles.py
- window min width 1244 -> ~376: automation row and sample-list
  chip row stop propagating their natural minimums (Ignored-width
  hosts), portrait page fixed width removed (portrait mode pins the
  window width itself)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-16 13:28:16 +02:00
duan_jandClaude Fable 5 f1f932bc7c feat: full-name tooltips on the glyph count column headers
CI / lint (push) Successful in 32s
Docs build and publish / docker (push) Successful in 15s
CI / test (3.12) (push) Canceled after 36s
CI / test (3.13) (push) Canceled after 32s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 22s
CI / test (3.14) (push) Canceled after 31s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 27s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 26s
CI / test-with-coverage (push) Canceled after 21s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 22s
The sample list's Mount/Gridscan/Screening/Rotation count columns show
only a glyph in the header to save width; hovering now shows the full
name via ToolTipRole. Text headers stay tooltip-free.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 13:23:32 +02:00
duan_jandClaude Fable 5 1fe3c7f4e5 refactor: transmission is a per-mode setting, placed in its section
The shared "Rotation transmission" row sat in the top rows of BOTH the
Gridscan and Rotation tabs, where it read as one setting for everything
(and as nonsense in the Gridscan tab). Now each tab places its own row:
Gridscan keeps "Transmission" with the shared resolution/distance rows,
and the Rotation tab moves "Rotation transmission" below the Screening
block and its Run button, as the first row of the Rotation section -
next to the fields it actually applies to. Screening keeps its separate
"Screening transmission". No behavior change: same field, same
spreadsheet/user-value handling, only the label and the placement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 13:23:32 +02:00
duan_jandClaude Fable 5 6d09ae4fe3 feat: show the mounted sample name on the camera view
A "Currently mounted: <sample_name>" HUD line in the top-left corner of
the sample camera, driven by the DAQ status (clears when nothing is
mounted). Painted like the other camera HUDs and sized through the text
zoom, which QSS cannot reach here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 13:23:32 +02:00
duan_jandClaude Fable 5 1fc72649b0 feat: text zoom shortcuts and high-contrast resize lines
- Ctrl+plus / Ctrl+minus / Ctrl+0 (and View menu entries) scale the QSS
  FONT_* ladder and the application default font together; the scale is
  clamped to 0.8-1.6 and persisted in QSettings appearance/font_scale.
- Resize gutters keep a 5px hover/drag region but paint only a 2px line
  with a 1px shadow (hard-stop gradients per orientation).
- WCAG contrast fixes: separators were 1.0-1.2:1 against every theme
  background, now >=3.25:1 (dark theme gold @70%); scrollbar handle
  1.17:1 -> 3.38:1; unselected tab text on the sky gradient 2.07:1 ->
  4.71:1 via TAB_IDLE_TEXT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 13:23:32 +02:00
duan_jandClaude Fable 5.1 d5a7074ec1 feat: compact sample list headers, move Comment column
CI / lint (pull_request) Successful in 42s
CI / test (3.12) (pull_request) Successful in 1m4s
CI / test (3.14) (pull_request) Successful in 1m3s
CI / test (3.13) (pull_request) Successful in 1m8s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m17s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m25s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m12s
CI / test-with-coverage (pull_request) Successful in 1m26s
CI / lint (push) Successful in 30s
Docs build and publish / docker (push) Successful in 11s
CI / test (3.12) (push) Canceled after 35s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 25s
CI / test (3.13) (push) Canceled after 32s
CI / test (3.14) (push) Canceled after 30s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 27s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 22s
CI / test-with-coverage (push) Canceled after 20s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 22s
CI / coverage-analysis (pull_request) Successful in 14s
Count columns use glyphs instead of long headers to save width, Comment
moves ahead of the data-collection params, transmission shown as the
stored value instead of percent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 17:04:43 +02:00
duan_jandClaude Fable 5.1 22228c0868 feat: highlight whole mounted row in sample list
CI / lint (pull_request) Successful in 42s
CI / test (3.12) (pull_request) Successful in 1m4s
CI / test (3.13) (pull_request) Successful in 1m3s
CI / test (3.14) (pull_request) Successful in 1m7s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m7s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m16s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m25s
CI / test-with-coverage (pull_request) Successful in 1m33s
CI / coverage-analysis (pull_request) Successful in 4s
CI / lint (push) Successful in 36s
Docs build and publish / docker (push) Successful in 5s
CI / test (3.12) (push) Canceled after 1m6s
CI / test (3.13) (push) Canceled after 57s
CI / test (3.14) (push) Canceled after 55s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 52s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 36s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 32s
CI / test-with-coverage (push) Canceled after 31s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 15s
The mounted sample used to tint only the frozen "#" cell. Paint the full
row in the same blue so it is findable at a glance, and keep the theme's
own text color on it (the mid-tone blue reads fine without the dark ink
that the pastel status tints need). Repaint the whole row span on
mount/unmount, not just column 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 16:54:43 +02:00
duan_jandClaude Fable 5.1 a2c5ed3d5c feat: show spreadsheet params in sample list
Add Oscillation, Exposure, Total range and Transmission columns to the
dewar sample table, read from SampleShortInfo.aaredb_params (already
filled by spreadsheetupdater from AareDB, so no server change).
Transmission is shown as percent like the user spreadsheet.

The new columns are nullable, so the header sort key now puts None last
instead of raising on None < float.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 16:54:43 +02:00
duan_jandClaude Fable 5.1 a56457d03b test: route the tell-state stand-in through object for basedpyright
CI / test (3.13) (pull_request) Successful in 1m4s
CI / lint (pull_request) Successful in 1m9s
CI / test (3.12) (pull_request) Successful in 1m13s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m11s
CI / test (3.14) (pull_request) Successful in 1m16s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m16s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m25s
CI / test-with-coverage (pull_request) Successful in 1m40s
CI / coverage-analysis (pull_request) Successful in 4s
CI / lint (push) Successful in 31s
Docs build and publish / docker (push) Successful in 15s
CI / test (3.12) (push) Canceled after 40s
CI / test (3.13) (push) Canceled after 37s
CI / test (3.14) (push) Canceled after 35s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 32s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 30s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 27s
CI / test-with-coverage (push) Canceled after 25s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 27s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:57:29 +02:00
duan_jandClaude Fable 5.1 ccb71bf43f test: format the mocked token and cast the tell-state stand-in for basedpyright
CI / lint (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) Canceled after 33s
CI / test (3.12) (pull_request) Canceled after 33s
CI / test (3.13) (pull_request) Canceled after 28s
CI / test (3.14) (pull_request) Canceled after 28s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Canceled after 23s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Canceled after 23s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Canceled after 18s
CI / test-with-coverage (pull_request) Canceled after 18s
CI / coverage-analysis (pull_request) Canceled after 0s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:56:55 +02:00
duan_jandClaude Fable 5.1 c3e7b9a2cc test: cover the sample camera pacing, the newest-frame drain and the frame slot
CI / lint (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 35s
CI / test (3.12) (pull_request) Canceled after 36s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Canceled after 21s
CI / test (3.13) (pull_request) Canceled after 31s
CI / test (3.14) (pull_request) Canceled after 31s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Canceled after 26s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Canceled after 26s
CI / test-with-coverage (pull_request) Canceled after 21s
CI / coverage-analysis (pull_request) Canceled after 0s
CI's diff-coverage gate sat at 29% because the ZMQ fixes live in code no
test reached: the subscriber's socket setup, _emit_image/notify_frame_
displayed, _recv_latest and the run() frame path, plus the main window's
frame slot. The subscriber now runs against a real inproc socket (the
drain and the in-flight cap are the fix, so a mock would prove nothing);
run() is driven with a stubbed _recv_latest that hands over one JPEG and
then stops. The busy overlay gets a check that cooling is the only blue
busy state.

The subscriber fixture takes qapp so no QImage is built before the
QApplication exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:56:16 +02:00
duan_jandClaude Fable 5.1 dbd5b9078f style: paint the unknown-session badge with the viewing-mode style
CI / lint (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 48s
CI / test (3.12) (pull_request) Successful in 1m3s
CI / test (3.13) (pull_request) Successful in 1m5s
CI / test (3.14) (pull_request) Successful in 1m10s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m10s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m13s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m24s
CI / test-with-coverage (pull_request) Successful in 1m35s
CI / coverage-analysis (pull_request) Failing after 4s
The sample camera drew its own gold/orange/red "Session Vacant /
Baton Requested / Guest Mode" box whenever the busy overlay had nothing
to show. Since the busy-style builder already receives the session
state, that box only ever appeared while the session was still unknown
(no status yet, or one without a session), so it flashed a different
color and shape than the yellow "Viewing mode, click here to grab the
baton" badge every known not-owned state gets.

Hoist that badge into VIEWING_MODE_STYLE and paint it from both paths;
the hover fill is shared through _badge_fill. The three MARK_TOOLTIP
constants had no other users and are dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 14:31:32 +02:00
duan_jandClaude Fable 5.1 f1cf55adb9 fix: take the aareDB transmission as the fraction aarecommon >= 0.7 declares
CI / lint (push) Successful in 1m11s
Docs build and publish / docker (push) Successful in 4s
CI / test (3.12) (push) Canceled after 1m22s
CI / test (3.13) (push) Canceled after 1m3s
CI / test (3.14) (push) Canceled after 1m0s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 55s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 56s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 31s
CI / test-with-coverage (push) Canceled after 23s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 17s
CI resolves aarecommon 0.7.3, where DataCollectionParameters.transmission
is a 0-to-1 fraction that rejects anything above 1.0 (the percent sheets
are converted inside AareDB >= 0.83, which main already requires). The
divide-by-100 from the earlier 'percentage' fix therefore failed the
model test on CI and would have turned 20% into 0.2% at the beamline.
Pass the fraction through and pin aarecommon>=0.7.3 so the older int
percentage model can no longer be installed; relock.

Also satisfy the diff typecheck gate: basedpyright only counts instance
variables assigned in __init__, so the Database/User toggle widgets are
created there and _build_source_toggle only lays them out.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 11:56:57 +02:00
duan_jandClaude Fable 5.1 f20bd065f5 feat: show AUTO CENTERING in the sample camera view while a loop centering runs
/status cannot tell an auto centering from a manual alignment move (both
are busy + SampleAlignment, the combo the overlay deliberately hides), so
the DAQ worker emits its own auto_centering flag: True while its own
/alc/center_loop POST is in flight (the endpoint blocks until done) or
while the automation progress reports the Center step running. The
sample camera views pass it to build_busy_overlay_style, which paints a
purple AUTO CENTERING wave text like the ROBOT MOUNTING/COOLING ones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 11:56:57 +02:00
leonarski_fandClaude Opus 5 63d902a82a fix: read the aareDB transmission as the percentage it is
DataCollectionParameters.transmission is a StrictInt documented as
"positive, between 0 and 100" (aareDB's own API description of the
spreadsheet payload), while every model that consumes it wants a 0-to-1
fraction. The old "divide by 100 only when above 1.0" guess therefore
turned a spreadsheet asking for 1% into a scan at 100% transmission -
a hundredfold dose on the sample. A fraction cannot be stored in that
column at all: the model rejects 0.2.

The mapping test now builds a real DataCollectionParameters instead of a
stand-in namespace, so a renamed column fails the test rather than the
beamline. That is what let 'totalrange' through.

daq.py's spreadsheet_params and get_auto_raster_params still carry both
the old column name and the same transmission guess.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
2026-09-08 11:56:57 +02:00
leonarski_fandClaude Opus 5 7cc662bfd8 fix: read the aareDB total angle from the column that exists
The column is totalangle, not totalrange, so reading it raised
AttributeError four times a second in the status loop.

Reading a spreadsheet cell now goes through _spreadsheet_float, which
treats a missing column, an empty cell and a cell that does not hold a
number all as "not set" and logs the column once. aareDB owns these
names; the GUI should fall back to its defaults when one moves, not die
on the status loop it is read from.

Note that the same misspelling is still in daq.py (spreadsheet_params
and get_auto_raster_params), where getattr's default hides it: automation
has never picked up the total angle from the spreadsheet either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
2026-09-08 11:56:57 +02:00
leonarski_fandClaude Opus 5 8833f3a9d1 fix: keep user values in the scan panels across sample mounts
The panels kept the effective detector distance, resolution and
transmission in their own attributes, and a sample change overwrote those
from the spreadsheet whatever the Database/User toggle said. The fields
went on showing the user's numbers while the scan request carried the
database ones, so a value had to be re-entered after every mount.

The fields are now the only place a value lives. Each setting is worth,
in order: what the user typed while "User values" is selected, what the
mounted sample asks for, the panel default - one rule, ScanSettingsPanel
._setting, that each panel spells out a line at a time. Overrides are per
setting, so a setting the user did not touch keeps following the sample.

Along the way:

* DbOverrideLineEdit is gone; the panels use NumberLineEdit directly, and
  the per-widget database/user bookkeeping is replaced by SampleParameters,
  the one place that knows the aareDB spreadsheet column names.
* The (name, widget, converter) mapping loops with getattr/hasattr are
  replaced by explicit per-field commit slots.
* The omega speed cap no longer drives one field's validator from another
  field's value: it is a read-out plus a check on the pair at Run time.
* _add_row/_add_pair_row build the label/field/unit rows, which also puts
  the Total angle degree sign on its own row instead of the header above.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
2026-09-08 11:56:57 +02:00
duan_j 416302224c feat: 900 Hz at 6d and 120 Hz at 10s for data collection. Paint input correspondingly. 2026-09-05 11:04:18 +02:00
duan_j 0696b583b0 test: no need to compare wording
CI / test-with-coverage (pull_request) Successful in 2m23s
CI / lint (pull_request) Successful in 47s
CI / test (3.12) (pull_request) Successful in 58s
CI / test (3.13) (pull_request) Successful in 1m3s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m6s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m6s
CI / test (3.14) (pull_request) Successful in 2m18s
CI / coverage-analysis (pull_request) Successful in 3s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 3m27s
Docs build and publish / docker (push) Successful in 4s
CI / lint (push) Canceled after 34s
CI / test (3.12) (push) Canceled after 34s
CI / test (3.13) (push) Canceled after 29s
CI / test (3.14) (push) Canceled after 29s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 24s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 24s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 19s
CI / test-with-coverage (push) Canceled after 19s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 15s
2026-09-02 09:13:08 +02:00
duan_j a19cbf1081 test: no need to test energy 0 and update tolerance
CI / lint (pull_request) Successful in 53s
CI / test (3.11) (pull_request) Successful in 58s
CI / test (3.13) (pull_request) Successful in 59s
CI / test (3.12) (pull_request) Successful in 1m3s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m12s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m14s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m18s
CI / test-with-coverage (pull_request) Successful in 1m29s
CI / coverage-analysis (pull_request) Successful in 3s
CI / lint (push) Successful in 34s
Docs build and publish / docker (push) Successful in 17s
CI / test (3.11) (push) Canceled after 37s
CI / test (3.12) (push) Canceled after 36s
CI / test (3.13) (push) Canceled after 32s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 31s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 27s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 26s
CI / test-with-coverage (push) Canceled after 22s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 20s
test: update test...
2026-08-24 19:24:29 +02:00
duan_j c71676a29c test: log panel 2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 a7bd46c14b test: sample padding pixel so movestate colors survive Linux fonts
x=20 sits on a digit glyph with CI's Linux fonts, so the assert compared
an antialiased glyph blend against the pure background. The padding area
(border 1px + padding 6px) can never contain text on any font.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0147jE48bQUTm9AqNQApT6b2
2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 b984a36f7c fix: energy spin movestate colors never rendered
The movestate property sat on the spinbox's inner QLineEdit, which both
themes deliberately paint transparent (QAbstractSpinBox QLineEdit rule)
- the state machine worked but no color ever showed, and the tests only
asserted the property, not the paint. The property now lives on the
spinbox itself with QAbstractSpinBox[movestate=...] added to both
themes' rules, verified by a pixel-sampling regression test that grabs
the rendered widget through the real stylesheet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 e1d11aa949 feat: MotorMoveGroup semantics for both Set Energy rows
The energy setpoint applies via the Change Energy button, not Enter, so
it gets the full motor state machine instead of the typing-pending
color: neutral tracks the readback, a user edit stages pending (and is
the only thing that enables the button), the click turns moving, and
readback arrival within tol returns to neutral. New SpinMoveState in
motor_move_group.py adapts the pattern to a QDoubleSpinBox+button pair
(colors ride the movestate QSS via the spin's internal QLineEdit);
replaces the hand-rolled pending-until-click in the exp-config row and
covers the Beamline setup row that had no color at all. The server's
0.0 detector-unavailable energy is kept out of the readback feed so it
cannot clamp the spin to the range minimum.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 225d7064c4 feat: bound Set Energy to a 4-20 keV placeholder range
Both Set Energy rows allowed 1-30 keV, an arbitrary spinbox default;
nothing downstream validates the request (daq.change_energy forwards
straight to bec). Shared ENERGY_MIN/MAX_KEV constants now bound both
spins to 4-20 keV with a tooltip flagging the range as pending
Beamline Scientist confirmation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 3e7c334abb feat: pending color while typing in Experiment configuration values
Values inside the Experiment configuration banner now show the same
pending color as the motor move boxes while the typed text differs from
the applied value; Enter (or focus-out) commits as before and clears it.
Opt-in via NumberLineEdit(track_pending=True) because MotorMoveGroup
owns the movestate property on its registered boxes; reuses the existing
movestate QSS, so no new styling. The Set Energy spinbox marks pending
until Change Energy is clicked, since its apply gate is the button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 19:24:29 +02:00
duan_jandClaude Fable 5 1a9f4d8709 test: Moving state no longer flips to combined view
CI / lint (pull_request) Successful in 38s
CI / test (3.11) (pull_request) Successful in 1m1s
CI / test (3.12) (pull_request) Successful in 1m2s
CI / test (3.13) (pull_request) Successful in 1m1s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m15s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m15s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m20s
CI / test-with-coverage (pull_request) Successful in 1m31s
CI / coverage-analysis (pull_request) Successful in 4s
CI / lint (push) Successful in 31s
Docs build and publish / docker (push) Successful in 17s
CI / test (3.11) (push) Canceled after 38s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 24s
CI / test (3.12) (push) Canceled after 34s
CI / test (3.13) (push) Canceled after 33s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 29s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 28s
CI / test-with-coverage (push) Canceled after 23s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 25s
The fix removed Moving from the motion-watch trigger but the unit test and code comment still described the old behavior, so pre-push failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 13:30:20 +02:00
duan_jandClaude Fable 5 36675e0204 feat: auto-centering row with apply-after-mount, grid button raster-only
Auto Centering moves out of the experiment tabs to its own row between
Manual sample and Exp. Config., with an apply-after-mount checkbox that
re-runs it on every detected sample mount (guarded against firing on GUI
startup). Draw a Grid now only shows on the Raster scan tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:54:50 +02:00
duan_jandClaude Fable 5 64058ae6c8 feat: fold expanded help cheatsheet when mouse leaves the box
Click is no longer the only way back to the "?" badge: moving the
pointer off the expanded box, or out of the widget entirely, folds it.
Checked before the interaction gate so it works in viewing mode too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:54:50 +02:00
duan_j d5c66afba9 fix: combined view only when state is moving or when in robotsampleexchange 2026-08-20 19:54:50 +02:00
duan_jandClaude Fable 5 5f068a6305 feat: hover HUD with pixel coords and grey scale bar on sample camera
Bottom-right corner shows the hovered pixel coordinates with a grey
1-2-5 scale bar above them, derived from geom.pixel_in_mm and the
current zoom. Works in viewing mode (before the interaction gate),
clears on leave, and skips drawing outside the image.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:54:50 +02:00
duan_jandClaude Fable 5 8b6ae51ce6 fix: satisfy lint and diff-coverage gates for admin gating
basedpyright requires _admin_tip_anchor to exist after __init__, so the
anchor is created eagerly instead of lazily. Cover the red-tip branch and
the status-bar menu gate; the menu test swaps in a QMenu subclass with a
no-op exec because PySide method lookup ignores class-attr monkeypatches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:54:50 +02:00
duan_jandClaude Fable 5 569b024456 feat: admin-gate beam diagnostics states, warn via passive red tip
Beam location, Beamstop alignment and Flux measurement grey out for
non-staff in the state strip and the status-bar transitions menu. All
"not admin" denials now show as an auto-dismissing QToolTip on a
Catppuccin red wash instead of a click-to-close QMessageBox.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 19:54:50 +02:00
duan_jandClaude Fable 5 167d78db68 style: collapse multiline _status calls for ruff format
CI / lint (pull_request) Successful in 52s
CI / test (3.11) (pull_request) Successful in 1m1s
CI / test (3.13) (pull_request) Successful in 1m3s
CI / test (3.12) (pull_request) Successful in 1m3s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m11s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m18s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m19s
CI / test-with-coverage (pull_request) Successful in 1m29s
CI / coverage-analysis (pull_request) Successful in 4s
CI / lint (push) Successful in 32s
Docs build and publish / docker (push) Successful in 15s
CI / test (3.11) (push) Canceled after 45s
CI / test (3.12) (push) Canceled after 42s
CI / test (3.13) (push) Canceled after 40s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 37s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 35s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 32s
CI / test-with-coverage (push) Canceled after 30s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 34s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:25:14 +02:00
duan_jandClaude Fable 5 7a01a866ef feat: status bar splits passive left / operable right
Operable controls (Fast Shutter, State, p-group, Session) group right
via addPermanentWidget and share one hover affordance - hand cursor +
underline - now owned by ClickableLabel; ValueLabel opts out for the
passive readouts, and the unwired busy label becomes a plain QLabel.
Cryo sits first in the operable group but stays passive: a cryo
operation is planned (TODO in status_bar.py), the placement just
reserves its spot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:25:14 +02:00
duan_jandClaude Fable 5 16b61b886b fix: no BEAMLINE BUSY overlay during Sample alignment
Same exception as the motion watch: busy in Sample alignment is the
alignment itself, and the sample motion is watched in these very
views - the curtain hid exactly what the user needed to see. Gated
at both style call sites (sample camera and the Axis views); the
session-driven 'In viewing mode' badge is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:25:14 +02:00
duan_jandClaude Fable 5 e9428ca64d 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
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>
2026-08-17 17:00:26 +02:00
duan_jandClaude Fable 5 b58c23d0f6 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
_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>
2026-08-17 16:49:30 +02:00
duan_jandClaude Fable 5 344888dac0 feat: Queued chip view shows the actual run order
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>
2026-08-17 16:24:09 +02:00