Skip to content

Refactor area detector into per-dock module structure#82

Open
Osayi-ANL wants to merge 38 commits into
mainfrom
dev
Open

Refactor area detector into per-dock module structure#82
Osayi-ANL wants to merge 38 commits into
mainfrom
dev

Conversation

@Osayi-ANL
Copy link
Copy Markdown
Collaborator

@Osayi-ANL Osayi-ANL commented May 26, 2026

What changed

Major refactor of the area detector viewer. The monolithic area_det_viewer.py was split into a package under src/dashpva/viewer/area_det/, with each panel extracted into its own dock module (docks/image_dock.py, docks/roi_dock.py, docks/stats_dock.py, docks/mask_dock.py, docks/mouse_pos_dock.py, docks/analysis_dock.py). The CLI entry point now resolves to the new path. Subsequent commits on the branch are layered on top of this restructure:

  • Initial refactor: new file structure, dock split, CLI rewired, dock header theming
  • Dock header theme color + test_cli to exercise the CLI button surface
  • Dock theming polish, PV channel area det switching cleanup, per-curve toggles in the stats plot,
  • removal of HKL Viewer/Save buttons from the image dock in area_det
  • Removed a stale HKL-clear comment block in the PV-channel switch path

Test plan

  • Launch the area detector via the CLI — confirm it lands on the new area_det_viewer.py and the window opens with stats, mouse position, and mask docks initially
  • Start Live View against a known PV channel — image renders, stats labels update, ROI overlays appear
  • Switch the PV channel mid-session — HKL/ROI/Stats monitors rebind cleanly, no duplicate callbacks, no "Missing PV data" spam, no orphaned overlays
  • Stop Live View — timers stop, no stale callbacks left attached
  • Plot Stats - Toggle each stat visibility in the stats plot
  • Drag/resize/rearrange docks — dock headers render with the themed color and titles stay readable in both light and dark system themes
  • Confirm the image dock no longer shows the removed HKL Viewer/Save buttons
  • pytest tests/unit/test_cli.py passes -- because we changed the area_det structure it passes

Osayi-ANL added 6 commits May 22, 2026 15:20
- Area detector in a new file structure
- Area detector includes docks
- cli points to new area_detector_viewer.py file
- Dock Header theme

add: roi and stats pv's are hardcoded in pva_reader.py
add: test_cli to test the cli buttons run
…urve toggles in stats plot; drop HKL Viewer/Save buttons from image dock
@Osayi-ANL Osayi-ANL requested a review from pecomyint May 26, 2026 16:24
Copy link
Copy Markdown
Collaborator

@pecomyint pecomyint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Notes — PR #82

Ruff violations (5 errors)

  • pva_reader.py:1I001 import block unsorted (collections is stdlib, must come before third-party imports)
  • area_det_viewer.py:680F841 unused variable process (assigned but never read)
  • test_cli.py:31, 39, 47F841 unused variable result (3 occurrences)

Bug: user_config.py config file path

_CONFIG_FILE = Path(__file__).resolve().parents[1] / ".dashpva_data.json" resolves to src/dashpva/.dashpva_data.json — inside the package directory. This will fail on pip-installed packages (site-packages is read-only) and would get tracked by git. Should use settings.PROJECT_ROOT / ".dashpva_data.json" instead.

Bug: IOC_PREFIX vs DETECTOR_PREFIX in PVAReader._configure

The fallback changed from DETECTOR_PREFIX to IOC_PREFIX. ROI/Stats PVs use the detector prefix (e.g., s6lambda1:ROI1:MinX), not the IOC prefix (e.g., 6idb1:ScanOn:Value). At beamlines where these differ (6-ID-B: DETECTOR_PREFIX=s6lambda1, IOC_PREFIX=6idb1), this constructs wrong PV names. Additionally, IOC_PREFIX includes a trailing colon, so f'{pva_prefix}:ROI1:MinX' produces "6idb1::ROI1:MinX" (double colon). Currently latent because the area detector viewer always passes pva_prefix explicitly, but the old DETECTOR_PREFIX behavior was correct.

Cosmetic: Value label alignment inconsistency

ROI dock values use Qt.AlignRight — good for numeric readability. But Stats dock and Mouse Position dock value labels have no alignment set (left-aligned by default). All numeric value labels across docks should use Qt.AlignRight | Qt.AlignVCenter for consistency.

Cosmetic: Decimal formatting

Numeric values (pixel values, stats totals, etc.) should be formatted to 2 decimal places (e.g., f"{value:.2f}") so digits align vertically and users can read them at a glance. Currently some values display with full floating-point precision.

Default dock visibility

Current defaults: Stats + Mask + Mouse Position visible; Image + ROI + Analysis hidden. Suggested order for defaults:

  1. Stats — always visible (first)
  2. Image — always visible (second)
  3. Mouse Position — always visible (third)
  4. ROI, Analysis, Mask — available via Windows menu, shown on demand

Copy link
Copy Markdown
Collaborator

@pecomyint pecomyint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments above.

@Osayi-ANL Osayi-ANL requested a review from pecomyint May 27, 2026 22:04
@pecomyint
Copy link
Copy Markdown
Collaborator

PR #82 — Review (Round 2)

Several items from the first review were fixed (ruff clean, user_config.py path, DETECTOR_PREFIX fallback, value label alignment). The following issues remain.

1. PV prefix handling — reverted from agreed pattern

The dev-peco pattern is: ConfigDialog asks for "Detector Prefix" (placeholder: e.g. s6lambda1), then dialog_accepted() always appends :Pva1:Image. The user only ever types a prefix. Simple and clear.

This PR changed the pv_config.ui placeholder to "e.g. pvapy:image" which invites the user to type a full channel name instead of just the prefix. This introduced the need for _PVAPY_FULL_CHANNEL special-casing, colon-splitting in __init__, suffix-stripping in update_pv_prefix(), and the pva_prefix parameter threading — all to handle a case that shouldn't arise from the UI.

Fix: Restore the dev-peco pattern:

  1. In ConfigDialog.init_ui(), override the label and placeholder like dev-peco does: self.lbl_input_channel.setText("Detector Prefix") and self.le_input_channel.setPlaceholderText("e.g. s6lambda1")
  2. In dialog_accepted(), always construct the channel as f"{prefix}:Pva1:Image" (keep the pvapy:image exception in _build_image_channel for CLI/programmatic use, but don't surface it in the UI)
  3. The ROI PVs like pvapy:image:ROI1:MinX failing is expected and fine — the viewer is designed for real detectors. No need to special-case simulation ROIs.

2. Autoscale default ON — not working

The code sets chk_autoscale.setChecked(True) on line 279, but then line 282 does max_setting_val.setValue(1.0) which triggers update_min_max_setting() — that method unchecks autoscale (line 1631) because it interprets any spin-box value change as a manual override. Result: autoscale is always OFF at startup. Fix: Either move setChecked(True) after setValue(1.0), or wrap the setValue call with blockSignals(True/False).

3. GPU/CPU in status bar

PR #82 changed DiffractionImageWindow from inheriting QMainWindow (dev-peco) to BaseWindow. BaseWindow.init_perf_statusbar() (base_window.py:439-452) adds "CPU: -%" and "GPU: N/A" labels to the status bar of every window. The area detector viewer doesn't use GPU, and CPU reads from /proc/stat which doesn't exist on macOS (always shows "CPU: N/A"). DO NOT SHOW GPU since it is irrelevant. Fix: Either override init_perf_statusbar in the area detector viewer to skip GPU (and use a cross-platform CPU method), or don't inherit from BaseWindow for this viewer.

4. Hardcoded theme colors

Osayi moved the PV input channel, start/stop buttons, and value labels into theme.qss using objectName selectors (good). However:

  • roi_dock.py lines 28-43 still has _STATS_BTN_BASE and _PLOT_BTN_STYLE with 6 hardcoded hex colors (#8f8f91, #f6f7fa, #dadbde, #e8f4e8, #c8dcc8) and inline font: bold 12pt 'Sans Serif'. These should be in theme.qss.
  • roi_dock.py has 11 setStyleSheet() calls for ROI colors — these use ROI_COLORS from theme_colors.py which is fine, but the button gradient colors are not from the theme.
  • Do not add new color constants (like #E4E4E4 for QLabel[valueLabel="true"] or #FFFFFF for editable inputs) to theme.qss. Stick with the existing theme colors we already have defined in theme_colors.py.

5. Dock stacking order

Current layout (lines 317-342): Stats + Image + Mouse Position are stuffed into a single QTabWidget inside a wrapper QDockWidget("Info"), pinned to the top. Mask + ROI + Analysis are separate docks below.

Preferred layout: Stats and Mask tabified together (visible), Image and Mouse Position tabified together (visible), both groups stacked vertically — no "Info" wrapper dock. Osayi used QTabWidget inside a QDockWidget which is non-standard (Qt dock system already provides tabbing via tabifyDockWidget()). The eventFilter + _repin_info_to_top hack (lines 436-477) to keep the Info dock pinned adds unnecessary complexity.

Fix: Use standard tabifyDockWidget() for same-group tabs and splitDockWidget() to stack groups vertically, matching the pattern used elsewhere in the codebase.

6. "Info" dock title bar visible

QDockWidget("Info", self) at line 336 shows a visible "Info" header. Add self.info_tabs_dock.setTitleBarWidget(QWidget()) to hide it — the Stats/Image/Mouse Position tabs already identify the content.

7. Fusion style forced globally

configure_app() now forces QStyleFactory.create("Fusion") for all viewers. This changes the look of every window in the app (launcher, scan monitor, etc.), not just the area detector. If Fusion is only needed for dock title styling, scope it to the area detector viewer.

8. "Show Rois" → "Show ROIs"

image_dock.py:68 and roi_dock.py:97. "ROIs" is the correct abbreviation.

9. Decimal formatting — values jumping

Decimal places are mostly correct (.2f), but the decimal point still jumps around as values change magnitude. Fix: Use monospace font or fixed-width formatting like f"{value:>10.2f}" on the value labels.

Copy link
Copy Markdown
Collaborator

@pecomyint pecomyint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the new comments above

@Osayi-ANL Osayi-ANL requested a review from pecomyint May 28, 2026 19:30
Osayi-ANL added 4 commits May 28, 2026 15:26
fix: revert autoscale to dev-peco design (passive update_min_max, per-frame apply, drop timer_autoscale)
…_channel, _PVAPY_FULL_CHANNEL)

fix: tabify stats/mask docks
…and have/perf/spinner constants

remove: dead code (roi_x test stub, commented imports/disconnects)

fix: lbl_roi_specifi_stats typo

fix: detector prefix label capital P
Copy link
Copy Markdown
Collaborator

@pecomyint pecomyint left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. start_scan_monitor() is defined but never called
    The scan FLAG_PV camonitor was extracted out of start_channel_monitor() into a new start_scan_monitor() method (
    pva_reader.py:L514
    ), but nothing in _connect_pv_pollers() or anywhere else actually calls it. This means scan-mode users will never get the FLAG_PV monitored — is_scan_complete is never toggled, which can cause the viewer to sit at "listening" with no images. Please wire it into _connect_pv_pollers() after the stats monitors.

  2. Hard-coded light-mode colors break macOS dark mode
    The new QSS rules force light hex backgrounds without setting a corresponding dark text color:
    QLineEdit#pv_prefix { background-color: #F8F9FA } — white bg, but Fusion dark palette gives white text → invisible
    QLabel[valueLabel="true"] { background-color: #DEE2E6 } — same problem on every value label in the docks
    QSpinBox#plotting_frequency, QDoubleSpinBox#min_setting_val, QDoubleSpinBox#max_setting_val — same
    start_live_view / stop_live_view buttons already had a light gradient, but the text color is never set explicitly → white-on-white under Fusion dark
    These widgets were unstyled on main and worked fine across light/dark because Qt's native style handled it. Please either use palette() roles instead of hex values, or add explicit color: #2C3E50 to every rule that forces a light background.
    Alternatively, revert these specific background rules — the dock structure itself doesn't depend on them.
    This is a style consistency issue too — the project's existing convention in theme.qss was to use palette() for adaptive elements (e.g. QMessageBox QLabel { color: palette(text) }, QDockWidget::pane { border: 1px solid palette(mid) }). The new rules break that convention by mixing in hard-coded hex colors that only work in light mode.

  3. DOCK_MAX_WIDTH = 380 is a hard-coded pixel value
    BaseDock (
    base_dock.py:L6
    ) is logical px on macOS Retina (auto-scaled, fine) but physical px on beamline Linux boxes running stock X11 without QT_SCALE_FACTOR (unusably narrow on high-DPI). Make it a constructor kwarg with a default, or compute from font metrics so it scales on both platforms.

  4. Dock object names could collide setObjectName(self.title) uses bare names ("Stats", "ROI"). If workbench/HKL later use BaseDock with the same titles, saveState will cross-wire layouts. Namespace them (e.g. f"area_det_{self.title}").

  5. Typo: "horiontal" → "horizontal" at
    area_det_viewer.py:L198

…ts / PlotStats; namespace BaseDock objectName by host; typo horiontal->horizontal
@Osayi-ANL Osayi-ANL force-pushed the dev branch 5 times, most recently from 52c6b72 to d202cd0 Compare June 3, 2026 22:38
remove: strip QSS + setObjectName on stats/plotstats/live-view buttons - match mask-dock pattern so they render natively and adapt to OS dark mode

remove: strip generic QPushButton QSS from launcher.ui so launch buttons render natively and adapt to OS dark mode

fix: editable inputs use palette(midlight) + border for visible non-white bg adapting to light/dark

fix: adaptive palette colors for value labels, spinbox buttons, and borders in area det docks

fix: adaptive palette styling across area det docks; remove torch from default deps
@Osayi-ANL Osayi-ANL force-pushed the dev branch 3 times, most recently from dc07f62 to 8694911 Compare June 4, 2026 15:41
…spinboxes under fusion; reset dock to initial layout
@Osayi-ANL Osayi-ANL requested a review from pecomyint June 4, 2026 16:56
Osayi-ANL added 6 commits June 4, 2026 14:27
…spinboxes under fusion; reset dock to initial layout

fix: adaptive palette(base) for all inputs and value labels; white/black spinboxes under fusion; reset dock to initial layout

fix: revert start/stop live view buttons to light gradient; keep white/black spinboxes

remove: DOCK_MAX_WIDTH constant and setMaximumWidth from all area-det docks

fix: restore pva_prefix tracking, pvapy:image default, mask hidden, start_scan_monitor wired

fix: white/black spinboxes; remove DOCK_MAX_WIDTH; restore pva_prefix, pvapy:image default, mask hidden, start_scan_monitor
…80px; reset via remove/re-add

fix: dpi-aware dock sizing, clamp to window thirds, clamp window geom to screen on restore and reset

fix: roi dock as standalone at bottom; only clamp window geom when exceeding screen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants