spacr.qt.widgets.preview_contract¶
One contract for every live view, written down once.
Four modules ship a live view — Mask
(live_preview), Timelapse
(timelapse_preview), Measure
(measure_preview) and Motility
(motility_preview) — and each of them grew its own
answer to the same handful of questions. The inventory taken before this
module was written, one column per live view:
question |
Mask |
Timelapse |
Measure |
Motility |
|---|---|---|---|---|
what the run button says |
Run preview |
Run preview |
Refresh crops |
Run preview |
public entry point |
run_preview |
run_preview |
refresh |
run_preview |
can the user cancel it |
method only |
no |
no |
no |
does it say why it cannot |
yes |
yes |
NO — silent |
yes |
what “busy” reads as |
“Preview already running.” |
“Preview already running.” |
n/a |
“Preview already running.” |
worker freed on |
yes |
no — deleteLater |
n/a |
no — deleteLater |
announces its result |
ready |
ready |
NO — none |
ready |
work runs off the GUI thread |
QThread |
QThread |
JobRunner |
QThread |
what a knob change costs |
nothing — explicit run |
a re-link, cached masks |
a re-crop, superseded by token |
a rescore from the cache |
where the picture goes |
twin zoom canvases |
twin zoom canvases |
crop grid |
matplotlib plot |
The last three rows are differences of substance — a crop grid is not a pair of zoom canvases, and a preview that re-links from a cache should not be made to demand a button press for the sake of symmetry. They stay. The rows above them are the ones that had no reason to differ, and every one of them is now a single implementation.
Three of those rows were bugs rather than differences of taste. Measure
returned from its re-crop without a word when no array was loaded, so the
button did nothing and said nothing. Timelapse and Motility wired
QThread.finished to worker.deleteLater and dropped their own
reference in the result slot — the pattern
spacr.qt.bridge.make_thread() documents as a race, and the one the
Mask panel had already been fixed away from — which leaves a running
QThread owned by nobody when the user closes the screen mid-pass. And
Measure never told anyone its pass had landed, so no screen could react to
a crop preview the way it reacts to the other three.
LivePreviewContract is what the four panels now inherit. It owns
the vocabulary (PREVIEW_RUN_TEXT and the message constants), the
run guard, the cancellation token and the busy/idle button state, so a
change to any of them is one change rather than four.
The words¶
A live view is a panel that re-renders the module’s own output from the current settings, on real input, before a run. That is Mask, Timelapse, Measure and Motility, and it is what the Live toggle opens. The Image UMAP explorer is not one: it makes an already-computed embedding clickable, and no setting changes what it draws. Its toggle therefore says Interactive, not Live — the same word had been on both.
Cancel and Stop are also two words on purpose. A live view’s
Cancel throws the pass away: nothing it produced is kept, because a half
-segmented field is not a result. A hyperparameter search’s Stop
(spacr.qt.screens.hyperparam.HyperparamPanel) finishes the trial
in flight and keeps every trial already scored, marking the result
partial. Same button position, deliberately different verbs, because a
user who presses one expecting the other loses either nothing or hours.
The search panel’s progress is already reported the way this contract
wants a live view’s to be — on_trial is called with idx + 1 after
a trial has been scored, so “12 of 40 configurations evaluated” counts
work done rather than work started.
Cellpose¶
preview_cellpose_model() is the other half of the sharing.
model_type= is accepted and IGNORED by Cellpose 4, so a preview that
passed it silently segmented with cpsam — including for a user who had
just trained their own checkpoint in spaCR’s Train Cellpose module. That
defect was written twice, in two preview files, and fixed twice. One
constructor here means the next Cellpose API change is one fix.
Classes¶
What every live-view panel promises, implemented once. |
Functions¶
|
Build the Cellpose model a live view segments with. |
|
Phrase one failed preview pass the same way in every module. |
Module Contents¶
- class spacr.qt.widgets.preview_contract.LivePreviewContract[source]¶
What every live-view panel promises, implemented once.
A panel mixes this in beside
QWidgetand supplies three things: a_statuslabel, a_run_btnbutton, and_preview_blocked_reason(). It gets the run guard, the cancel token, the busy/idle button state and the shared wording for free.The token is the cancellation mechanism. Neither Cellpose nor a numpy read exposes an interrupt, so a cancelled pass is left to run itself out and its answer is dropped on arrival:
preview_token()is captured when a pass starts and compared withpreview_stale()when it lands.- begin_preview() bool[source]¶
The shared guard at the top of every
run_preview.Refuses out loud — a live view that declines in silence is the defect this contract exists to remove — and otherwise marks the panel busy and hands back a fresh token via
preview_token().- Returns:
True when the caller should start a pass.
- cancel_preview() bool[source]¶
Abandon the pass in flight, if there is one.
The work is not killed — neither Cellpose nor a numpy read can be interrupted — the token is bumped so its answer lands as a no-op, and the panel is returned to the idle state at once.
- Returns:
True when a running pass was abandoned.
- display_primaries() str[source]¶
Which primaries this view draws channels in.
Read from the GLOBAL preference, never from a control on one panel. A user who needs the substitution needs it in every view and every session; a per-screen toggle is one they have to re-find, and the screen they forget to set is the one that misleads them.
A view MAY override this – a figure being prepared for publication wants
cmywhatever the author’s vision – but every view starts here.- Returns:
one of
spacr.crops.DISPLAY_PRIMARIES.
- preview_blocked_reason() str[source]¶
Say why the preview cannot run, or
""when it can.Never raises: a panel that fails while explaining itself would take the status line down with it.
- preview_stale(token: Any) bool[source]¶
True when a result carrying
tokenhas been superseded.- Parameters:
token – the preview generation a result was started under, as
preview_token()returned it;Noneor a value that is not an integer is never stale.
- set_preview_busy(busy: bool) None[source]¶
Enable exactly one of run / cancel.
- Parameters:
busy – true while a preview pass runs (cancel enabled, run disabled), false otherwise; a panel without either button skips it.
- set_preview_status(text: Any) None[source]¶
Put one sentence on the panel’s status line.
The display-primaries note is appended here rather than at each call site, so no panel and no code path can recolour an image without saying so. When the preference is off – which it is for almost everybody – this changes nothing at all.
- Parameters:
text – the sentence to show; converted to
strand translated. A panel with no status label ignores it.
- spacr.qt.widgets.preview_contract.preview_cellpose_model(model_name: Any, gpu: bool | None = None)[source]¶
Build the Cellpose model a live view segments with.
model_type=is accepted-and-IGNORED by Cellpose 4 — it logs “not used in v4.0.1+” and drops it, leavingpretrained_modelat itscpsamdefault. Passing the user’s choice there therefore segmented withcpsamwhatever they picked, including a checkpoint they had just trained in spaCR’s own Train Cellpose module.spacr.utils._resolve_cellpose_pretrained()is what the pipeline uses: it maps the legacy pre-SAM names ontocpsamand says so once, and returns a path unchanged when the name is a fine-tuned checkpoint.Cellpose and torch are imported inside the call, so importing a preview module cold — as the test suite does — needs no CUDA-capable stack.
CPU previews set
use_bfloat16=False, including an explicitgpu=Falsechoice and the CPU fallback when accelerator detection fails. A Cellpose 3 checkpoint rejected by Cellpose 4 raises a preview-specific compatibility message while retaining the original exception as its cause.- Parameters:
model_name – the model name or checkpoint path the user picked.
gpu – force the device choice;
Noneasks torch.
- Returns:
a
cellpose.models.CellposeModel.