resizable camera region, menu panel collapse, zoom ladder #231

Merged
duan_j merged 12 commits from feat/gui-panel-resize into main 2026-09-16 16:16:48 +02:00
Owner

Work in progress - GUI layout flexibility batch from the sandbox.

  • Center area is now a QSplitter (left controls | camera tabs |
    beamline column): dragging resizes the camera region width.
  • Side panels collapse via checkable View menu actions
    (Show Left Panel / Show Right Panel), no on-screen buttons.
  • Ctrl+plus / Ctrl+minus zoom walks a fixed 100/125/150 percent
    ladder (old free 10 percent steps produced in-between sizes where
    the fixed-width boxes clipped); saved scales snap to the ladder.
    The side boxes scale their widths with the ladder.
  • Camera tab widget minimum width clamped to 1/3 of its natural
    minimum (the min came from tab labels and the refresh-button rows,
    not the video views).
  • Resize bars are short centered grips (66x3 px PNGs in
    src/aare/gui/graphics), shared by QMainWindow separators and
    QSplitter handles - one style source in styles.py. The old
    full-length separator gradients are removed.
  • Window minimum width drops 1244 -> ~376 px: the dewar automation
    row and the sample-list filter-chip row no longer propagate their
    natural minimums (Ignored-width hosts, rows clip from the right),
    and the portrait page fixed width is gone (portrait mode pins the
    window width itself).

Gates run locally: ruff, format, full unit suite (527 passed),
diff-quality basedpyright 100 percent, diff-cover 92 percent.

Generated with Claude Code

Work in progress - GUI layout flexibility batch from the sandbox. - Center area is now a QSplitter (left controls | camera tabs | beamline column): dragging resizes the camera region width. - Side panels collapse via checkable View menu actions (Show Left Panel / Show Right Panel), no on-screen buttons. - Ctrl+plus / Ctrl+minus zoom walks a fixed 100/125/150 percent ladder (old free 10 percent steps produced in-between sizes where the fixed-width boxes clipped); saved scales snap to the ladder. The side boxes scale their widths with the ladder. - Camera tab widget minimum width clamped to 1/3 of its natural minimum (the min came from tab labels and the refresh-button rows, not the video views). - Resize bars are short centered grips (66x3 px PNGs in src/aare/gui/graphics), shared by QMainWindow separators and QSplitter handles - one style source in styles.py. The old full-length separator gradients are removed. - Window minimum width drops 1244 -> ~376 px: the dewar automation row and the sample-list filter-chip row no longer propagate their natural minimums (Ignored-width hosts, rows clip from the right), and the portrait page fixed width is gone (portrait mode pins the window width itself). Gates run locally: ruff, format, full unit suite (527 passed), diff-quality basedpyright 100 percent, diff-cover 92 percent. Generated with Claude Code

Coverage report (automated)

Total line + branch coverage: 50%
Diff coverage vs main: 95% (minimum 80%)

Full report: coverage artifact on this run.

Coverage report (automated) Total line + branch coverage: 50% Diff coverage vs main: 95% (minimum 80%) Full report: coverage artifact on this run.
duan_j added 7 commits 2026-09-16 13:28:18 +02:00
- 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>
User feedback: idle bars stay short centered grips, but hovering or
grabbing a resize bar brings back the full-length 2px line + shadow
gradient so the active bar reads over its whole run. The now-unused
hint grip PNGs are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
CI installs latest index deps (it does not read uv.lock) and resolved
PySide6 6.11.2, which segfaults the unit suite in teardown on about
half the runs - reproduced on PLAIN origin/main, so it is not this
branch's code. 6.9.0 (the uv.lock pin) is stable. Cap the dependency
so CI resolves a 6.9.x build; drop the cap once the teardown crash is
hunted down. uv.lock re-locked (it was stale at aaredaq 0.22.1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bec plugin editable installs upgrade PySide6 to 6.11.x past the
pyproject cap, which brings the teardown segfault back in exactly the
test-with-beamline-plugins jobs. Reinstall the capped version after
the plugin install.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chore: retrigger CI (pxi_bec job hit the known eventFilter teardown flake)
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 49s
CI / test (3.12) (pull_request) Canceled after 45s
CI / test (3.13) (pull_request) Canceled after 44s
CI / test (3.14) (pull_request) Canceled after 40s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Canceled after 39s
CI / test-with-coverage (pull_request) Canceled after 30s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Canceled after 35s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Canceled after 34s
CI / coverage-analysis (pull_request) Canceled after 0s
b29b5f121e
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j force-pushed feat/gui-panel-resize from a4ed9d8afb to b29b5f121e 2026-09-16 13:28:18 +02:00 Compare
duan_j added 2 commits 2026-09-16 13:29:08 +02:00
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
26fbe2ca42
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>
duan_j added 2 commits 2026-09-16 14:37:22 +02:00
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>
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
5cb57f4737
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>
duan_j added 1 commit 2026-09-16 14:40:51 +02:00
fix: suppress takeAt override complaint from newer basedpyright stubs
CI / lint (pull_request) Successful in 55s
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 1m5s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Successful in 1m14s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Successful in 1m22s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Successful in 1m25s
CI / test-with-coverage (pull_request) Successful in 1m34s
CI / coverage-analysis (pull_request) Successful in 3s
Docs build and publish / docker (push) Successful in 6s
CI / lint (push) Canceled after 49s
CI / test (3.12) (push) Canceled after 46s
CI / test (3.13) (push) Canceled after 44s
CI / test (3.14) (push) Canceled after 41s
CI / test-with-beamline-plugins (pxi_bec) (push) Canceled after 39s
CI / test-with-beamline-plugins (pxii_bec) (push) Canceled after 36s
CI / test-with-beamline-plugins (pxiii_bec) (push) Canceled after 34s
CI / test-with-coverage (push) Canceled after 31s
CI / coverage-analysis (push) Canceled after 0s
Build and Publish / release (push) Successful in 29s
145b94b7ef
Qt's takeAt contract is nullptr-if-out-of-range; the newer PySide
stubs on CI declare a non-Optional return. Suppress rather than lie.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
duan_j marked the pull request as ready for review 2026-09-16 16:16:43 +02:00
duan_j merged commit 145b94b7ef into main 2026-09-16 16:16:48 +02:00
Author
Owner

Filip tested it at the beamline, Vincent seem to like it in terms of how the resize bar works. So merge and deploy.

Filip tested it at the beamline, Vincent seem to like it in terms of how the resize bar works. So merge and deploy.
Sign in to join this conversation.