Skip to content

GUI: Resume button resumes a paused plan (run RE.resume() on the main thread) - #94

Merged
Jakub Wlodek (jwlodek) merged 3 commits into
NSLS2:mainfrom
sligara7:hex-gui-resume-main-thread
Oct 8, 2026
Merged

Jakub Wlodek (jwlodek) merged 3 commits into
NSLS2:mainfrom
sligara7:hex-gui-resume-main-thread

Conversation

@sligara7

Copy link
Copy Markdown
Contributor

Problem

At HEX, in the GUI's in-process mode (pixi run hex-gui), an EDXD scan paused with Pause: Immediate could not be resumed: pressing Resume did nothing and the RunEngine stayed paused.

Cause

The local-mode Resume button ran RE.resume() in a FunctionWorker thread. resume() enters bluesky's SigintHandler, and Python only lets the main thread install signal handlers, so it raised ValueError: signal only works in main thread of the main interpreter. The button connected only the worker's finished signal, not errored, so the failure was invisible. Plans themselves are started on the main thread (RE(plan(...)) through IPython), so only Resume was affected; Stop/Abort/Halt run on the Qt main thread already. The scan type is not involved: any paused plan fails the same way.

Fix

Resume goes through run_in_ipython("RE.resume()") after the click returns, the same route RE(plan(...)) takes. The [GUI] RE.resume() line is echoed in the terminal like a plan call.

How this was verified

  • Mock tests: new tests/test_re_execution_controls.py drives the real QtReExecutionControls(local=True) offscreen with an IPython shell and a simulated RunEngine (ophyd.sim): it starts a scan, presses Pause: Immediate, then Resume, and asserts the scan completes. It was observed failing on main (RE stayed paused) and passes with this change. It needs Qt, so it runs in the gui-dev environment and is skipped where qtpy/bluesky_widgets are absent.
  • Real beamline: the symptom was seen at HEX on 2026-10-06; this fix has not yet been confirmed there.

In local mode the Resume button ran RE.resume() in a FunctionWorker thread. resume() enters bluesky's SigintHandler, and Python lets only the main thread install signal handlers, so it raised ValueError('signal only works in main thread of the main interpreter'). The button listened only for 'finished', never 'errored', so the click did nothing visible and the plan stayed paused. Resume now goes through run_in_ipython like RE(plan(...)) does.

Verified with mock tests: test_resume_button_resumes_a_paused_plan pauses a running scan with Pause: Immediate and presses Resume; it failed (RE stayed paused) before this change.

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 6, 2026 15:26

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.

Copilot review overview

🟡 Changes recommended

Custom RunEngine resolution is broken, and the regression test is skipped by all CI test jobs.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Routes local RunEngine resume actions through IPython’s main thread and adds a regression test.

Changes:

  • Replaces worker-thread resume execution with deferred IPython execution.
  • Adds an offscreen Qt pause/resume integration test.
File Description
src/​hextools/​gui/​re_execution_controls.py Moves resume execution to the main thread.
tests/​test_re_execution_controls.py Tests resuming a paused simulated scan.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/hextools/gui/re_execution_controls.py Outdated
Comment thread tests/gui/test_re_execution_controls.py
Review on PR 94: resuming by the name in the IPython shell could resume a different engine (or raise) when the controls were given re= or their own namespace. Resume now targets the resolved engine, still on the main thread: through IPython when that engine is the shell's RE (so the terminal echoes it), directly after the click otherwise. Pinned by test_resume_button_resumes_a_paused_plan[re|namespace], observed failing before this change.

GUI tests move to tests/gui/ and a new CI job runs them in the gui-dev environment (offscreen Qt); the py3.11-3.13 jobs lack Qt and skip them.

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 6, 2026 15:42

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.

Copilot review overview

🟡 Changes recommended

Resume must prevent duplicate queued actions from rapid repeated clicks.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
Resolved since last review (1)

Comment thread src/hextools/gui/re_execution_controls.py
Comment thread tests/gui/test_re_execution_controls.py Outdated
Comment thread src/hextools/gui/re_execution_controls.py
…t uses RE

Review on PR 94 (Jakub, agreeing with Copilot): scheduling resume() for after the click left the button enabled until the 0.5 s poll, so a double-click queued two resume() calls; the second ran while the plan was already running. Resume now disables itself on click and a pending flag keeps it disabled (the poller included) until resume() returns, at completion or at the next pause. The old worker-thread version had this guard; moving to the main thread dropped it.

Verified with mock tests: test_double_click_on_resume_resumes_once observed failing (resume() called twice: paused, running), passes now; all 4 GUI tests pass. Tests name the engine RE (Jakub).

Assisted-by: copilot:claude-opus-4-8
Copilot AI balanced review requested due to automatic review settings October 6, 2026 17:34

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.

Copilot review overview

🟢 Approval recommended

The implementation addresses the threading failure and includes targeted CI-backed regression coverage.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

@jwlodek
Jakub Wlodek (jwlodek) merged commit bc1bc1f into NSLS2:main Oct 8, 2026
1 of 6 checks passed
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