spacr.qt.screens.model_zoo

Model Zoo — every model this machine can segment or classify with, in one list.

The question “which models do I have, where did they come from, and does this one work on my images” is currently answered with find, memory and a full plate run. This screen answers it in a list, a provenance card and three fields.

Layout:

┌──────────────────────────────────────────────────────────────────────┐
│ /data/screen1                       [Choose folder…] [Scan]          │
├──────────────────────────────────────────────────────────────────────┤
│ model            kind      source  v  size    checksum  trained on   │
│ toxo_plaque…     cellpose  bundled 1  25 MB   none      /nas_mnt/…   │
│ maxvit_t_epoch…  classifier local  1  310 MB  none      /data/…      │
│ hela_60x.CP…     cellpose  remote  1  26 MB   published unknown      │
├──────────────────────────────────────────────────────────────────────┤
│ hela_60x_confluent.CP_model  [cellpose · remote · v1]                │
│   uri        https://…                                               │
│   sha256     9f86d0…  (published)                                    │
│   trained on unknown                                                 │
│   ! this model does not say what it was trained on…                  │
├──────────────────────────────────────────────────────────────────────┤
│ Download to [~/.spacr/models    ] [Choose…] [Download] [Cancel]      │
│ [====================              ] 12.4 MB / 26.5 MB               │
├──────────────────────────────────────────────────────────────────────┤
│ Test on [3] fields from [/data/screen1/plate1/1] [Choose…]  [Test]   │
│ field    objects  seg_qc  flags                    ┌───────────────┐ │
│ A01_f01  212      ok      -                        │  [ masks ]    │ │
│ A01_f02  198      warn    high_border_fraction     └───────────────┘ │
└──────────────────────────────────────────────────────────────────────┘

Design notes:

  • Provenance is a column, not a tooltip. “What was this trained on” is what decides whether a model applies to your images at all, so it is in the table and spelled unknown where it is unknown — a blank cell reads as “no constraints”, which is the opposite of what it means.

  • Downloads are off the GUI thread, atomic, and cancellable. The bytes go to a temp file inside the destination and are renamed only after the checksum passes (spacr.model_zoo.fetch()), so Cancel leaves the destination exactly as it was — no half-written file at a name that looks like a model.

  • worker.finished is relayed through a signal into a bound method. PySide6 delivers a plain closure connected to a worker’s signal as a direct call on the worker thread, and the completion handler here fills a QPlainTextEdit and a QTableWidget. Building QTextDocument children off the GUI thread is undefined behaviour, so finished chains through ModelZooScreen._job_settled — a widget-affine bound method — exactly as PlateViewScreen does.

  • No modal dialogs on any error path. A folder with no models, a checksum mismatch, a corrupt checkpoint, a cancelled download: all inline. A QMessageBox hangs a headless run.

  • Two selected models hand off to Model Compare. Benchmarking each one separately and reading the two tables is not a comparison — the A/B harness in spacr.model_compare is, and it is the one that says out loud that neither model is ground truth. This screen builds a configured ModelCompareScreen and emits it rather than growing a second, weaker comparison of its own.

  • Benchmarks are never sorted across field sets. The results table belongs to one model on one set of fields, and the summary line names the field set, because a score on your three fields says nothing about a score on anybody else’s.

Classes

ModelZooScreen

Browse, verify, download and benchmark models supported by spaCR.

Functions

compose_labels(→ Optional[numpy.ndarray])

Blend one model's mask over its field, in one colour.

group_entries(→ list)

Collapse a listing to one row per model family, newest version first.

Module Contents

class spacr.qt.screens.model_zoo.ModelZooScreen(parent=None, threaded: bool = True)[source]

Bases: PySide6.QtWidgets.QWidget

Browse, verify, download and benchmark models supported by spaCR.

Parameters:
  • parent – Qt parent.

  • threaded – run scans, downloads and benchmarks on worker threads (the default). Tests pass False to run them inline; both paths emit the same signals in the same order.

Variables:

last_error – text of the most recent failure, '' after a success. Errors go here and to the inline status label — never to a dialog.

Build the zoo: the scan list, the downloads and the benchmark.

Parameters:
  • parent – parent widget.

  • threaded – whether scans and downloads run on workers.

active_jobs() → int[source]

How many worker threads are still winding down.

benchmark_rows() → List[List[str]][source]

The benchmark table as plain strings.

build_comparison_screen(threaded: bool = True)[source]

A ModelCompareScreen, configured.

The host can show this directly instead of wiring compare_requested itself.

Parameters:

threaded – passed through to the comparison screen.

Returns:

the configured screen, or None when two models with local files are not selected.

cancel_download() → bool[source]

Ask the running download to stop. Nothing is left behind.

The flag is a plain dict read by the worker between chunks; the fetch deletes its temporary file and raises, so the destination folder never sees a partial model.

Returns:

True when there was something to cancel.

chosen_entry(row: int)[source]

The entry a row currently stands for, honouring its version pick.

Parameters:

row – index into the grouped listing, i.e. the model family in the order set_entries() laid the rows out.

closeEvent(event)[source]

Let every in-flight job finish before the widget dies.

A QThread collected while still running aborts the process, so the widget waits rather than dropping its references and hoping.

Parameters:

event – the close event, passed on to the base class after running jobs are cancelled and waited on (up to five seconds each).

compare_selected() → bool[source]

Hand two selected models to the A/B comparison.

Two separate benchmarks are not a comparison: they have no shared object matching, no split/merge attribution and no statement about which differences are real. spacr.model_compare has all three, so this emits compare_requested for the host to open that screen rather than growing a weaker comparison here.

Returns:

True when the request was emitted.

detail_text() → str[source]

The provenance card for the selected model, or ''.

download_progress() → int[source]

The progress bar’s current value (test helper).

download_selected() → bool[source]

Download the selected model into the destination folder.

Off the GUI thread, atomic, checksummed and versioned — see spacr.model_zoo.fetch(). A cancel leaves the destination exactly as it was.

Returns:

for the synchronous path, whether a file was installed; for the threaded path, True once the job started.

entries() → List[spacr.model_zoo.ModelEntry][source]

Everything currently listed, in table order.

field_names() → List[str][source]

The loaded field names, in order.

fields_folder() → str[source]

The loaded field folder, or ''.

is_busy() → bool[source]

True while a scan, download or benchmark is in flight.

preview_size()[source]

(w, h) the preview OCCUPIES, (0, 0) when there is none.

Widget coordinates, not the pixel count behind them: the picture is rendered at the screen’s density, so on a HiDPI panel those two numbers differ by the device pixel ratio.

result() → spacr.model_zoo.BenchmarkResult | None[source]

The most recent spacr.model_zoo.BenchmarkResult.

rows() → List[List[str]][source]

The listing as plain strings.

run_benchmark() → bool[source]

Segment the loaded fields with the selected model and score them.

Returns:

for the synchronous path, whether a result was produced; for the threaded path, True once the job started.

scan(folder: str | None = None, include_catalogue: bool = True) → bool[source]

List the catalogue plus every checkpoint under folder.

Never raises and never opens a dialog: a mistyped path, a folder with no models, an unreadable tree all land in the status label.

Parameters:
  • folder – folder to scan; None uses whatever is in the box.

  • include_catalogue – also list the bundled and declared entries.

Returns:

for the synchronous path, whether anything was listed; for the threaded path, True once the job started.

select(*rows: int) → None[source]

Select these rows (test/programmatic helper).

Goes through the selection model rather than selectRow because the latter clears the selection first in ExtendedSelection mode, so select(0, 1) would leave exactly one row selected — and “two selected models” is the whole input to the compare hand-off.

select_field(row: int) → bool[source]

Draw field row’s mask over its image.

Parameters:

row – index into the benchmark table.

Returns:

True when something was drawn.

selected_entries() → List[spacr.model_zoo.ModelEntry][source]

The selected models, in table order.

Through the identity stamped on each row, not the row number: the table sorts, and a row number stops naming a model the moment a header is clicked.

selected_rows() → List[int][source]

Indices of the selected rows, in order.

set_entries(entries) → None[source]

Replace the listing (used by the scan, and directly by tests).

One row per model family. The version column is a combo box, so a model with several versions is one row the user opens rather than several rows they have to tell apart by suffix.

Parameters:

entries – iterable of spacr.model_zoo.ModelEntry; grouped into families by key, newest version first.

set_fields_source(folder: str) → bool[source]

Load benchmark fields without blocking the GUI thread.

Parameters:

folder – a folder of .tif / .png / .npy / .npz.

Returns:

with threaded=False, True when at least one field loaded; otherwise True once the load starts. On failure the reason is in the status label.

set_opener(opener: Callable | None) → None[source]

Override how bytes are fetched.

fn(uri) -> chunks or fn(uri) -> (chunks, total); None restores spacr.model_zoo.open_uri(). This is the seam the tests use, and it is why no test in this suite touches the network.

Parameters:

opener – callable of that form, or None.

set_segment_fn(fn: Callable | None) → None[source]

Override the segmentation backend.

fn(images, config) -> masks; None restores spacr.model_compare.segment_with_cellpose(). Every test injects one, which is why nothing here loads Cellpose.

Parameters:

fn – callable of that form, or None.

status_text() → str[source]

The inline status message (test/introspection helper).

summary_text() → str[source]

The benchmark summary line, or ''.

uninstall_selected() → bool[source]

Remove the selected backend’s environment, after asking.

Returns:

whether it was removed.

spacr.qt.screens.model_zoo.compose_labels(image: numpy.ndarray | None, mask: Any, alpha: float = 0.45) → numpy.ndarray | None[source]

Blend one model’s mask over its field, in one colour.

The greyscale backdrop comes from spacr.qt.screens.model_compare.to_display_gray() — the same 1-99.9 percentile stretch the comparison screen uses, so the same field looks the same in both places.

One colour rather than a palette: a single segmentation has no correspondence to encode, and colouring by label id across two screens invites reading label 3 here as label 3 there.

Parameters:
  • image – the field, or None for a black backdrop.

  • mask – a 2-D label image.

  • alpha – overlay strength.

Returns:

a uint8 (H, W, 3) array, or None when the mask is not a 2-D label image.

spacr.qt.screens.model_zoo.group_entries(entries) → list[source]

Collapse a listing to one row per model family, newest version first.

The zoo used to show one row per checkpoint, so a model with three versions pushed two unrelated models off the screen. Grouping makes the row a MODEL and the version a choice within it.

Nested helpers

ModelZooScreen._run_job._job(payload: Dict[str, Any]) → None

Call the wrapped function, stashing its result in the payload.

The payload is how a value crosses back from the worker thread: a return would be swallowed by the runner.

spacr/qt/screens/model_zoo.py:1432

ModelZooScreen.download_selected._job() → zoo.ModelEntry

Install the chosen model. Off the GUI thread.

spacr/qt/screens/model_zoo.py:1050

ModelZooScreen.rows.text(r, c)

The text shown in cell (r, c), from its item or its combo box.

spacr/qt/screens/model_zoo.py:861

ModelZooScreen.run_benchmark._job() → zoo.BenchmarkResult

Benchmark one model against the loaded fields. Off the GUI thread.

spacr/qt/screens/model_zoo.py:1241

ModelZooScreen.scan._job() → List[zoo.ModelEntry]

Find every model, catalogue plus local. Off the GUI thread.

spacr/qt/screens/model_zoo.py:897

ModelZooScreen.set_fields_source._job()

Load the benchmark fields. Off the GUI thread.

spacr/qt/screens/model_zoo.py:1159