Skip to content

Prevent unsafe autofocus scans and plot progress in real time - #1241

Merged
kevin-m-dean merged 9 commits into
developfrom
kdean/issue-315-autofocus-safety
Aug 20, 2026
Merged

kevin-m-dean merged 9 commits into
developfrom
kdean/issue-315-autofocus-safety

Conversation

@kevin-m-dean

Copy link
Copy Markdown
Collaborator

Summary

  • reject autofocus trajectories that exceed enabled focus-stage limits without shifting, clamping, or truncating requested positions
  • provide immediate bounds feedback in the autofocus popup and refresh it when focus position or stage limits change
  • plot autofocus measurements incrementally using a persistent Matplotlib artist, Tk-thread coalescing, and blitting with a full-draw fallback
  • keep replaceable progress traffic separate from reliable warnings, final plots, and calibration-completion events
  • preserve defocus-calibration bookkeeping when the autofocus popup is closed during a sequence

Root cause

Autofocus frame planning and execution did not share an authoritative bounds check, so an out-of-range trajectory could begin and then stop after the first unsuccessful move. The popup also had no continuously refreshed indication that the requested path was unsafe. Plotting waited for the final autofocus event, and early progress delivery initially shared capacity and popup lifetime with reliable completion handling.

User impact

The requested scan geometry is now treated as an exact safety contract. Users see an inline red warning before starting, receive a final blocking dialog if needed, and can immediately see when moving the focus stage or changing limits makes the scan valid. During a valid autofocus run, the graph updates as measurements arrive while acquisition remains nonblocking.

Implementation

  • centralize exact coarse/fine position planning and reuse it for frame counts, controller preflight, and model execution
  • validate the initial scan before acquisition preparation and validate a combined fine scan against the measured coarse result
  • abort failed stage moves before queueing a measurement
  • freeze scan settings at dispatch so mid-run edits cannot alter the active trajectory
  • add a one-slot latest-progress multiprocessing queue drained by the existing Tk event pump
  • move autofocus completion and defocus-calibration processing into a persistent main-controller-owned helper
  • reuse the existing validated spinbox styling, stage update hooks, controller event pump, and histogram blitting pattern

Validation

  • 122 focused autofocus, popup, stage-limit, histogram, and main-controller tests passed
  • model regression suite passed
  • Ruff lint passed on all changed Python files
  • git diff --check origin/develop...HEAD passed
  • independent code review reported no Critical, Important, or Minor findings

Closes #315

Reuse analysis: centralize the existing autofocus step calculation, reuse stage limit state and ValidatedSpinbox error styling, and mirror HistogramController coalescing and blitting rather than adding parallel APIs or rendering infrastructure.
Reuse the existing Autofocus lifecycle, ValidatedSpinbox state, stage-update paths, event pump, and histogram blitting pattern rather than adding parallel public APIs.
Reuse the existing Autofocus lifecycle for one exact scan planner and authoritative stage checks. Reuse ValidatedSpinbox error styling and existing stage position/limit update paths for immediate popup feedback.
Reuse the existing event pump for defensive progress snapshots and mirror the histogram controller's Tk coalescing, persistent artist, blit cache, and full-draw fallback.
Keep progress publication nonblocking, freeze trajectory settings across acquisition setup and execution, refresh edited limits immediately, preserve precise diagnostics, and unregister popup event handlers on close.
Isolate replaceable real-time progress in a dedicated single-slot queue so final plots, warnings, and completion metadata retain reliable event capacity. Move defocus-calibration completion processing out of the popup lifecycle so closing the window cannot suppress configuration updates.\n\nReuse analysis: retains the existing model event pump, Autofocus feature, controller listener map, and channel-defocus notification paths; adds no user-callable API or parallel acquisition lifecycle.
Restore package-scoped dummy controller configuration, stage-limit methods, defocus state, and popup handlers after each autofocus test. Keep the advanced-stage limit-refresh test from leaking its f_max override into later channel and stage tests.
@kevin-m-dean
kevin-m-dean marked this pull request as ready for review August 7, 2026 21:36
Copilot AI lite review requested due to automatic review settings August 7, 2026 21:36

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.

Pull request overview

This PR hardens autofocus safety and UX by enforcing exact stage-limit bounds for planned scan trajectories, providing immediate popup validation feedback, and streaming autofocus plot updates in real time without letting progress traffic interfere with reliable events or calibration completion handling.

Changes:

  • Centralizes scan-position planning and reuses it for frame counts, bounds validation, and execution-time moves (with authoritative model-side rejection of out-of-bounds scans).
  • Adds continuously refreshed bounds feedback in the autofocus settings popup and refresh hooks when stage position/limits change.
  • Introduces a dedicated single-slot progress transport and Tk-thread-coalesced Matplotlib updates (blitting with full-draw fallback), plus popup-independent defocus-calibration completion processing.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/model/features/test_autofocus.py Adds unit coverage for scan planning, bounds diagnostics, frozen settings, failed-move handling, and progress snapshot behavior.
test/controller/test_controller.py Verifies stage-limit/position updates refresh autofocus bounds and that progress transport can’t starve reliable events.
test/controller/sub_controllers/test_autofocus.py Adds headless GUI/controller tests for inline bounds warnings, progress coalescing, blitting behavior, and popup lifetime.
test/controller/sub_controllers/test_advanced_stage_settings.py Ensures editing stage min/max triggers autofocus popup bounds refresh when open.
src/navigate/view/popups/autofocus_setting_popup.py Adds an inline (reserved-row) bounds-warning label/variable in the autofocus popup.
src/navigate/model/model.py Threads a new single-slot autofocus progress queue through model construction.
src/navigate/model/features/autofocus.py Implements shared scan planning, authoritative bounds checks, failed-move hard stops, frozen scan settings, and progress snapshot publishing.
src/navigate/controller/sub_controllers/stages_advanced.py Refreshes autofocus bounds immediately after live stage-limit edits for the active microscope.
src/navigate/controller/sub_controllers/autofocus.py Adds live bounds validation, progress plotting with Tk coalescing + blit caching, and routes calibration completion to a persistent helper.
src/navigate/controller/controller.py Adds the one-slot progress queue, drains it ahead of reliable events, and refreshes popup bounds on stage state changes.
src/navigate/controller/autofocus_calibration.py New persistent controller to process autofocus completion metadata independent of popup lifetime.
docs/superpowers/specs/2026-08-07-autofocus-bounds-realtime-plot-design.md Documents the design constraints and behavior for bounds validation and real-time plotting.
docs/superpowers/plans/2026-08-07-autofocus-bounds-realtime-plot.md Documents the implementation plan and test expectations for the feature set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +66 to +68
frame_count = int(scan_range // step_size) + 1
first_position = center - (frame_count // 2) * step_size
return tuple(first_position + index * step_size for index in range(frame_count))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Resolved.

@codecov

codecov Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.62472% with 47 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.38%. Comparing base (95e36ae) to head (270bba2).
⚠️ Report is 5 commits behind head on develop.

Files with missing lines Patch % Lines
...c/navigate/controller/sub_controllers/autofocus.py 89.23% 24 Missing ⚠️
src/navigate/model/features/autofocus.py 91.04% 12 Missing ⚠️
src/navigate/controller/autofocus_calibration.py 84.74% 9 Missing ⚠️
src/navigate/controller/controller.py 96.00% 1 Missing ⚠️
src/navigate/model/model.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1241      +/-   ##
===========================================
+ Coverage    65.29%   65.38%   +0.09%     
===========================================
  Files          190      193       +3     
  Lines        26612    27719    +1107     
===========================================
+ Hits         17377    18125     +748     
- Misses        9235     9594     +359     
Flag Coverage Δ
unittests 65.38% <89.62%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kevin-m-dean
kevin-m-dean merged commit c33333e into develop Aug 20, 2026
1 check 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.

Autofocus does not work if search bounds are outside of focus stage bounds

3 participants