Skip to content

Feature/live refresh + sim#1

Merged
maragall merged 2 commits intomaragall:mainfrom
Cephla-Lab:feature/live-refresh-sim
Mar 20, 2026
Merged

Feature/live refresh + sim#1
maragall merged 2 commits intomaragall:mainfrom
Cephla-Lab:feature/live-refresh-sim

Conversation

@hongquanli
Copy link
Copy Markdown
Contributor

This pull request adds live dataset refresh support to the ndviewer_light.py viewer and introduces a new script, simulate_live_acquisition.py, for simulating ongoing acquisitions on disk. The main focus is on enabling the viewer to automatically detect and display new data as it appears, which is particularly useful for live imaging experiments.

The most important changes are:

Live refresh and dataset monitoring in the viewer (ndviewer_light.py):

  • Added a periodic polling mechanism using QTimer to check for new timepoints or fields of view in the dataset folder, enabling the viewer to automatically refresh and display new data during ongoing acquisitions. This includes robust detection of changes and efficient in-place updates to minimize flicker. [1] [2] [3]
  • Implemented methods for efficiently updating the viewer's data in place when possible, and for cleaning up old file handles to avoid resource leaks.
  • Changed the status label update to display only the dataset name (not the full dimensions), keeping the UI stable during live acquisition.

Color mapping adjustment:

  • Updated the color mapping for wavelengths ≥700nm from 'darkred' to 'magenta' for better visual distinction.

Simulation tool for live acquisition:

  • Added a new script, simulate_live_acquisition.py, which generates a "single_tiff" style dataset on disk, simulating the gradual appearance of new timepoints and FOVs. The script can also launch the viewer automatically, allowing for end-to-end testing of live refresh functionality.

maragall pushed a commit that referenced this pull request Jan 22, 2026
* Display channel names instead of numeric indices

- Extract channel names from OME metadata or filenames
- Store channel names in xarray attrs for later use
- Add _update_channel_labels() method to update NDV's internal
  _lut_controllers with actual channel names
- Use QTimer delay to ensure NDV initialization completes before
  updating labels

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments for channel name display

- Keep xarray coords numeric instead of string channel names to avoid
  luts/coords type mismatch (addresses comments #2 and #3)
- Replace fixed 500ms QTimer delay with retry mechanism that polls for
  _lut_controllers readiness with bounded retries (addresses comment #4)
- Add documentation noting _lut_controllers is a private API that may
  change in future ndv versions (addresses comment #1)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 2)

- Fix single-TIFF path to use numeric coords instead of string channel
  names, consistent with OME-TIFF path (comment #1)
- Add generation counter to cancel stale retry callbacks when viewer is
  replaced by a new dataset load (comment #2)
- Add hasattr check for synchronize() method with debug logging when
  missing (comment #3)
- Remove placeholder issue URL, keep explanation about private API
  (comment #4)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add comment explaining synchronize() purpose

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 4)

- Fix misleading "all axes" comments to accurately describe which axes
  use numeric indices vs actual values (comments #3, #5)
- Preserve partial channel names from OME metadata instead of discarding
  all when count doesn't match n_c (comment #4)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 5)

- Initialize _channel_label_generation in __init__ for clarity instead
  of relying on getattr defaults (comment #5)
- Add debug logging when channel label update succeeds (comment #6)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 6)

- Rename 'channels' to 'channel_names' in single-TIFF path for
  consistency with OME-TIFF path (comment #1)
- Initialize _pending_channel_label_retries in __init__ alongside
  _channel_label_generation for consistency (comment #3)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add unit tests for channel name display feature

Test coverage includes:
- OME-TIFF channel name extraction and fallback logic
- Single-TIFF channel name extraction from filenames
- Retry mechanism with generation counter
- Channel label update on NDV controllers
- Integration tests for end-to-end behavior

25 tests, all passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 7)

- Clarify that numeric coordinates convention applies to both OME-TIFF
  and single-TIFF paths (comment #5)
- Document graceful failure mode if private _lut_controllers API changes
  (comment #9)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 8)

- Change timeout log from debug to warning for user visibility (comment #2)
- Add pytest install note to tests/README.md (comment #3)
- Remove unused pytest import (comment #8)
- Remove unused ch2 variable (comment #6)
- Remove unnecessary pending_retries assignment (comment #7)
- Add explanatory comments to except clauses (comments #9, #10, #11)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Address PR review comments (round 9)

- Fix debug log to track actual updated names instead of slicing list
  (handles controller gaps correctly) (comment #15)
- Remove unnecessary getattr for _pending_channel_label_retries since
  it's now initialized in __init__ (comment #21)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix channel labels not updating during in-place refresh

During live acquisition, if _try_inplace_ndv_update succeeds, the code
returned early without scheduling channel label updates. This caused
channel labels to show numeric indices instead of names (e.g., "DAPI",
"GFP") after in-place data swaps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix CI: black formatting and skip downsampling tests when unavailable

- Apply black formatting to ndviewer_light.py and test_channel_names.py
- Add pytest skipif marker to test_downsampling_wrapper.py to skip tests
  when Downsampling3DXarrayWrapper is not available (requires scipy)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add scipy to CI environment for 3D downsampling tests

scipy.ndimage.zoom is used by Downsampling3DXarrayWrapper for
resampling large 3D volumes to fit within OpenGL texture limits.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove accidentally committed IDE config files

- Remove .claude/agents/ directory (IDE-specific configuration)
- Remove .coverage file (test artifact)
- Add both to .gitignore to prevent future commits

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Replace line number references with method names in tests

Line numbers drift as code evolves, making references misleading.
Method names are more stable and searchable with IDE navigation.

Updated:
- test_channel_names.py: All inline comments now reference method names
- tests/README.md: Documentation uses method names instead of line numbers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix README: "three main areas" → "five main areas"

The test file has five test classes, not three.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Change synchronize() fallback log from debug to warning

If synchronize() method doesn't exist on LUT controller, channel labels
may not appear in the UI. A warning is more appropriate than debug level.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use _ = for intentionally unused ET.SubElement return value

Makes explicit that the return value is intentionally unused.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Extract constants and helper for channel label update retry

- Add CHANNEL_LABEL_UPDATE_MAX_RETRIES (20) constant
- Add CHANNEL_LABEL_UPDATE_RETRY_DELAY_MS (100) constant
- Extract _initiate_channel_label_update() helper method to DRY up
  duplicated code between _store_data_from_loader and _set_ndv_data

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@maragall maragall merged commit f783cf1 into maragall:main Mar 20, 2026
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