Skip to content

Separate reusable GUI from HEX configuration - #128

Open
Anthony Sligar (sligara7) wants to merge 27 commits into
NSLS2:mainfrom
sligara7:gui-config-separation
Open

Anthony Sligar (sligara7) wants to merge 27 commits into
NSLS2:mainfrom
sligara7:gui-config-separation

Conversation

@sligara7

@sligara7 Anthony Sligar (sligara7) commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Scope

Code changes only; documentation updates follow in a separate PR.

  • Extract importable, beamline-neutral GUI specs/builders and reusable widgets under hextools.gui.
  • Keep HEX-only plan/device composition, adapters, labels and configuration ownership in hextools.gui_config; no HEX imports or identity branches in generic GUI modules.
  • Preserve python -m hextools.gui and compatibility wrappers where feasible.
  • Retain synthetic non-HEX spec and absolute/relative/dynamic import-boundary regressions. The identity boundary check is focused, not exhaustive.
  • Preserve Beamline plans including Frontend Aperture and Filters, and the existing 20-device HEX profile contract.
  • No PDF/XPD code, QueueServer backend/API/transport, session protocol/lifecycle, DCLM/filter restructuring or deferred GUI behavior changes.

Updated base and history

PRs #126 and #127 are merged into canonical main:

No rebase, flattening or force-push. Jakub's DCLMConstants / .constants, profile and photon-delivery implementation/tests match canonical main exactly. The merge-added gui/hex_config.py duplicated the already extracted HEX shutter adapter: generic import/identity tests failed on it before its removal. The same discovery and manual allow_actuation=True, sleep_time=0.0 shutter policy remains in gui_config.py, outside the reusable GUI.

Local verification — merged tree, 2026-10-29

All commands used this branch's own locked Pixi dev environment, Python 3.13.18, without borrowing another checkout's environment or PYTHONPATH.

  • pixi run --locked -e dev pytest -o addopts='' -q tests/gui tests/test_gui_expected_devices.py tests/photon_delivery_system/test_dclm.py — 129 passed. Includes theme, plan-history, window-close and temporary-file tests; no GUI files excluded.
  • pixi run --locked -e dev pytest -o addopts='' -q tests/photon_delivery_system tests/test_nl_msg_hook.py tests/test_edxd* — 195 passed.
  • pixi run --locked -e dev lint — all configured pre-commit hooks passed, including Ruff lint/format and import contracts; repeated on the final committed tree.
  • pixi run --locked -e dev type-check — 0 errors, 0 warnings.

Actual mock GUI, not standalone widgets

Launched pixi run --locked -e dev python -m hextools.gui --mock --profile <isolated-copy-of-collection> --history-file <isolated-history> and drove its actual IPython/main-window session:

  • Selected Beamline / Tomography / EDXD / XRD; verified and displayed Frontend Aperture and Filters.
  • Verified generic slit/Theta forms and Save As Tiff defaults in both EDXD scan forms; toggled the scan checkbox. Parameterized widget regressions check editability for both forms.
  • Executed both mock shutter button actions through their actual manual plans. Both buttons disabled while pending, then re-enabled after RunEngine returned idle and both readbacks showed open.
  • Captured and visually inspected the actual full main window, techniques, aperture/filters, TIFF default and shutter states. Screenshots are not part of this PR.
  • The isolated copied profile redirected detector files and document backups to local validation storage; IPython, Qt configuration and history also used isolated storage. GUI process exited after verification.

Isolation and measured limits: CA/PVA automatic discovery disabled, loopback-only address lists. An additional Python socket audit hook denied non-loopback networking; the only blocked lookup was optional Open-Meteo weather. No Redis or beamline-host lookup/connection was attempted. The active mock path uses in-memory RunEngine metadata, mock devices and local Tiled simple; the profile's unused Redis hostname is not a mock-launch blocker. Earlier verification omitted this launch and temporary-file tests; the results above supersede those omissions.

Camera panes had no live image source. No detector acquisition plan, TST/real beamline or QueueServer service was exercised. This is actual mock GUI/control verification, not detector-stream or live-beamline certification. Draft retained pending review and refreshed CI; this PR has not been merged.

Future client/API limits

The generic entry point is hextools.gui; specs are defined with hextools.gui.spec.GuiSpec. HEX composition lives in hextools.gui_config. bluesky_widgets is not modified, and from bluesky_widgets import gui is illustrative only—not an existing API. Moving the generic layer there remains future work.

Load selected slit readbacks asynchronously, prepare GeRM statistics, restore initial scan positions, and serialize manual shutter actions without changing settling delays. Label the tilt parameter Two theta while retaining theta. Add full-plan failure/abort/pause regressions and document the removed gui environment and deployment-side exporter launcher diagnostics.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep zero delay for manual shutter buttons as confirmed by AJ, alongside mutual exclusion. Acquisition-plan timing remains unchanged.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep Python plan defaults unchanged; narrow slit input types for Pyright and verify manual shutter zero-delay exclusion.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Retain approved manual shutter mutual exclusion and zero manual delay.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move GeRM statistics setup into data logic, use grid reset decorator, remove deferred GUI special cases and clean up PR125 configuration/docs without restoring removed features.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve Jakub’s merged set_fe_aperture API unchanged.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explicit save_as_tiff=False retains HDF5. Verify emitted resource formats and generic checkbox defaults.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore merged PR125 implementation, profile construction and coupled tests; keep agreed operator fixes. Restructuring remains in history for a separate later proposal.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reproduced collection failures with qtpy absent; verify both modules skip with qtpy or bluesky_widgets unavailable and still execute with GUI extras.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract spec-driven GUI builders and reusable widgets while keeping HEX composition, device adapters, and configuration ownership in hextools.gui_config. Add synthetic-spec and import-boundary regression coverage, preserve existing plan tabs and compatibility paths, and include localhost-only GUI screenshots.

Assisted-by: copilot:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Read expected devices from the HEX GuiSpec instead of the generic launcher, and apply the formatter required by CI. The extracted list matches all 20 PR126 device entries.

Assisted-by: copilot:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Capture the complete HEX GUI after launching the real locked mock profile with isolated local storage and localhost-only EPICS address lists. The screenshot shows the Beamline technique, Filters plan, and full window; the live validation asserted all technique tabs and the Frontend Aperture and Filters plan widgets.

Assisted-by: copilot:gpt-6.1-sol

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sligara7
Anthony Sligar (sligara7) marked this pull request as ready for review October 29, 2026 18:02
Copilot AI balanced review requested due to automatic review settings October 29, 2026 18:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Environment precedence can cause --mock to launch in live mode or override --history-file, and opening the browser without configured specs leaves Search vulnerable to an IndexError.

2 open issues
What changed in this PR

This PR separates reusable GUI components from HEX-specific configuration through a new GuiSpec interface. It retains the HEX launcher and includes the operator fixes from prerequisite PR #126.

Changes:

  • Extracts configurable windows, widgets, and shared helpers into hextools.gui, with HEX composition in gui_config.py.
  • Adds import-boundary, configuration, and operator-behavior regressions.
  • Updates developer guidance, launcher documentation, and screenshot tooling.
File Description
tests/​test_review_followup.py Checks GeRM statistics activation for both formats.
tests/​test_gui_expected_devices.py Reads expected devices from HEX configuration.
tests/​test_edxd.py Tests omitted TIFF arguments and updates recovery commentary.
tests/​test_edxd_operator_recovery.py Covers recovery, interruptions, and reset opt-outs.
tests/​gui/​test_window_close.py Adapts lifecycle tests to spec-based windows.
tests/​gui/​test_shutter_status.py Tests shutter exclusion and busy-state guards.
tests/​gui/​test_gui_is_generic.py Checks generic boundaries and HEX composition.
tests/​gui/​test_generic_widget_boundary.py Checks widget imports and embedded HEX policy.
tests/​gui/​test_edxd_plan_defaults.py Verifies signature-driven TIFF checkboxes.
src/​hextools/​utils/​nl_msg_hook.py Preserves narrator compatibility exports and logger defaults.
src/​hextools/​log.py Uses the extracted error helper.
src/​hextools/​gui/​window.py Builds and launches spec-configured acquisition windows.
src/​hextools/​gui/​spec.py Defines configuration types and spec loading.
src/​hextools/​gui/​shutter_status.py Injects shutter policy and guards submissions.
src/​hextools/​gui/​plan_widget.py Uses the GUI-local error helper.
src/​hextools/​gui/​plan_status.py Uses the extracted narrator.
src/​hextools/​gui/​narrator.py Houses reusable RunEngine message narration.
src/​hextools/​gui/​misc/​weather.py Accepts configurable site coordinates and timezone.
src/​hextools/​gui/​misc/​__init__.py Removes HEX-specific wording from the docstring.
src/​hextools/​gui/​live_re_md_viewer.py Injects proposal-system operations.
src/​hextools/​gui/​errors.py Provides shared failed-status unwrapping.
src/​hextools/​gui/​device_sources.py Adds local device-path resolution.
src/​hextools/​gui/​auto_exporter_view.py Accepts the exporter module explicitly.
src/​hextools/​gui/​_theme.py Makes preference storage scope configurable.
src/​hextools/​gui/​_ipython.py Uses the extracted error helper.
src/​hextools/​gui/​__main__.py Retains the HEX launcher and compatibility helpers.
src/​hextools/​gui/​__init__.py Exposes reusable specs and a lazy launcher.
src/​hextools/​gui_config.py Owns HEX composition, adapters, and defaults.
src/​hextools/​exporters/​tomography.py Replaces redundant conversion with a type cast.
src/​hextools/​edxd.py Updates TIFF defaults and scan-position recovery.
src/​hextools/​detectors/​germ.py Enables statistics during acquisition preparation.
src/​hextools/​bl_constants.py Removes an unnecessary type-ignore comment.
schemas/​filters.yml Removes the obsolete filter schema.
pixi.toml Constrains the Pyright version range.
docs/​profile.md Corrects imports and filter-construction guidance.
docs/​gui.md Documents shutter guards, TIFF defaults, and recovery.
docs/​getting_started.md Documents exporter launchers and workstation diagnostics.
docs/​developer.md Explains spec-based composition and extension boundaries.
docs/​cli.md Corrects filter configuration guidance and example casing.
docs/​_scripts/​capture_screenshots.py Launches screenshots with the explicit HEX spec.

Review effort: Balanced

Comment thread src/hextools/gui/window.py Resolved
Comment thread src/hextools/gui/window.py Resolved
Preserve NSLS2#126 operator behavior and NSLS2#127 DCLMConstants/.constants without restructuring. Keep the extracted HEX shutter adapter outside the generic GUI; boundary tests failed on the duplicate merge-added adapter before removal. Validate the actual locked mock launcher, technique tabs, aperture/filters, TIFF defaults and shutter actions; record screenshots and full GUI, operator, lint and type-check results.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 29, 2026 18:08
@sligara7 Anthony Sligar (sligara7) changed the title [Dependent on #126] Separate reusable GUI from HEX configuration Separate reusable GUI from HEX configuration Oct 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Active defects can crash searches when run specs are empty, duplicate narration when a hook is already installed, and silently lose remote proposal updates under the default metadata-sharing setting.

4 open issues

Review effort: Balanced

Comment thread src/hextools/gui/spec.py Resolved
Comment thread src/hextools/gui/window.py Resolved
Apply base environment before mock and explicit history overrides. Only offer the scan browser when run specs are configured; cover both optional and configured paths with synthetic specs.

Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: copilot:gpt-6.1-sol
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Creating a fresh narrator bypasses PlanMonitor’s identity check when the utility narrator is already attached, causing each narration line to be logged twice.

No open issues 🎉

4 resolved since last review

Review effort: Balanced

Removes the validation screenshots and restores docs/developer.md and docs/gui.md to main; documentation follows separately.

Assisted-by: copilot:claude-opus-5-5
Copilot AI balanced review requested due to automatic review settings November 9, 2026 22:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

An existing RunEngine narrator can be chained with a new one, duplicating narrated steps in the terminal and GUI log; narrator reuse and regression coverage are needed.

1 open issue

Review effort: Balanced

self._plan_monitor = (
PlanMonitor(
re_client,
narrator=MsgHookNarrator(logger=spec.logger_name),

This branch has not been deployed

No deployments
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.

3 participants