feat(napari): responsive mouse source + pinnable signals - #429
Open
sdiebolt wants to merge 8 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the napari Signal Plotter’s interactivity and persistence by throttling mouse-driven updates, enabling Shift-press plotting without cursor motion, and introducing pinnable (persisted) signals via a renamed “stored signals” concept across the plugin.
Changes:
- Rename the signal store API/types from “imported” to “stored” to reflect both imported and pinned signals.
- Add pinning support:
SignalStore.pin_signal, a “Pin” toolbar button, and plot-time dedup so pinned copies don’t duplicate currently-live sources. - Add “Export Selected” to the Signal Manager dialog, plus supporting tests and icon/theming updates.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_napari/test_signal_store.py | Updates tests for stored-signal rename and adds pinning behavior tests. |
| tests/unit/test_napari/test_signal_plotter.py | Updates integration tests for stored signals and adds pin/dedup + pin-button behavior tests. |
| tests/unit/test_napari/test_signal_manager.py | Updates stored-signal rename usage and adds export-selected tests. |
| src/confusius/_napari/assets/pin.svg | Adds a pin icon asset used by the new Pin button. |
| src/confusius/_napari/_theme.py | Generalizes toolbar button creation/styling to support Pin + configurable icon/tooltip. |
| src/confusius/_napari/_signals/_store.py | Introduces StoredSignal + pin_signal, renames store APIs, and updates docs/errors. |
| src/confusius/_napari/_signals/_plotter.py | Implements mouse throttling, Shift-press plotting, pinning workflow, and stored-signal overlay changes. |
| src/confusius/_napari/_signals/_manager.py | Renames UI to “stored” and adds “Export Selected” for stored signals. |
Suppressed comments (1)
src/confusius/_napari/_signals/_plotter.py:1507
- In
_stored_plot_signals(), the loop variable is namedsignalseven though it represents a single stored signal. This makes the subsequent attribute access (signals.x,signals.y, etc.) harder to read and easy to confuse with the surrounding list variables.
s.y_values,
linewidth=linewidth,
alpha=alpha,
label=s.label,
color=s.color,
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
|
📖 Doc preview: https://confusius.tools/pr-preview/pr-429/ |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Shift-hover previously required movement to trigger a plot (pressing Shift while already resting on a voxel did nothing) and every raw mouse-move event ran the full extract-and-redraw pipeline synchronously, causing backlogs and jitter on large recordings. - Bind Shift in the napari keymap so pressing it alone plots the signal at the resting cursor, restoring any prior binding on close. - Throttle mouse-source updates to ~60/s with a leading-edge scheme: process immediately when enough time has elapsed (zero added latency in the common case), otherwise collapse a fast burst of events into one trailing update instead of queuing every intermediate position. - Replace the fast-path's relim()/autoscale_view() (which rescans every plotted line) with a direct numpy min/max over the arrays already held for the current frame. Claude-Session: https://claude.ai/code/session_01SHcGjS2Loy7KoNXStz4Xin
Rename ImportedSignal/SignalStore.imported_signals to StoredSignal/ stored_signals, since the store no longer only holds file imports: add SignalStore.pin_signal to snapshot a currently plotted live signal (mouse voxel, a point, or a label region) into a persistent stored signal, since a live signal previously only existed while its source mode was active and was dropped on switching modes. - SignalPlotter tracks the currently plotted live signal(s) and exposes a "Pin" toolbar button (reusing/generalizing create_export_button and style_export_button) to snapshot them. - A pinned signal is hidden from the plot while a live signal with a matching origin is still active, so re-entering the same source mode doesn't show a duplicate overlapping line; mouse pins are exempt since the live mouse id is a fixed "mouse-0", not per-voxel. Pinned entries get a "(pinned)" name suffix so they're never a silent duplicate of the live one in the legend/manager. - Re-pinning the same origin updates that entry's data in place instead of duplicating (e.g. after repainting a label mask). - Add an "Export Selected" button to the Signal Manager dialog to export only the selected stored signals. Fixes #428. Claude-Session: https://claude.ai/code/session_01SHcGjS2Loy7KoNXStz4Xin
The dedup that hid a pinned signal while a live signal with the same origin was still active consistently read as "pinning silently did nothing" (confirmed for labels and points, not just visually but at the API level too) rather than as intended duplicate-avoidance. Drop it: the "(pinned)" name suffix already keeps the two distinguishable in the legend/manager, and two overlapping identical lines is a much smaller cost than a signal appearing to vanish. Also always show the legend in the stored-only render path (a lone line there has no other identifying context, unlike the mouse-hover title which already names the voxel). Claude-Session: https://claude.ai/code/session_01SHcGjS2Loy7KoNXStz4Xin
Promotes SignalStore to the top-level widget and threads it into the Signals and QC panels, so a computed DVARS trace is added there directly, making it selectable elsewhere (e.g. as a Preprocessing panel confound/sample mask) without importing it from a file. Claude-Session: https://claude.ai/code/session_01SHcGjS2Loy7KoNXStz4Xin
The plotter previously held the bare Shift keybinding for its whole lifetime, displacing any other Shift binding even while in points/ labels mode where it has no effect. Bind/unbind now follows set_source_mode instead of widget open/close.
sdiebolt
force-pushed
the
feat/signal-plotter-mouse-source
branch
from
September 1, 2026 09:48
358d768 to
9af4bd6
Compare
AGENTS.md now routes all napari-plugin changelog entries to a dedicated :frame_photo: Napari plugin section (always last) instead of a **[Napari plugin]** prefix inside the type-based sections.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ImportedSignal/imported_signalstoStoredSignal/stored_signalsacross the signal store, manager, and plotter, since the store no longer only holds file imports.SignalStore.pin_signal+ a "Pin" button on the plotter to snapshot a currently plotted live signal (mouse voxel, point, or label region) so it survives a source-mode switch instead of being dropped.Closes #428.