spacr.qt.screens.qc_dashboard

Workflow inputs and outputs

QC

Inspect stored segmentation, unit, leakage and plate-effect checks. Open its nested viewers for detailed inspection.

Open: Home → QC.

Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.

Inputs

  • Measured objects — measurements/measurements.db; object tables depend on the enabled cell, nucleus, pathogen and organelle masks. Relevant tables, depending on the route: cell, nucleus, pathogen, cytoplasm. Relevant columns, depending on the route: plateID, rowID, columnID, fieldID.

  • Quality-control results — Stored project checks and QC reports; a missing check is not a passing result.

Outputs

  • Quality-control results — Stored project checks and QC reports; a missing check is not a passing result.

  • Figures and table exports — The output location chosen by the tool; exports describe the selected data and filters.

Before this module

  • Measure: Review available checks and missing evidence.

API reference.

Module tutorial.

QC dashboard screen: every stored QC verdict for a project, side by side.

The screen half of spacr.qt.widgets.qc_summary. It shows the segmentation, units, leakage, plate-effect and agreement verdicts side by side, with the one-line summary they add up to.

What it is for. Checking a project before its numbers are used. After Mask and Measure, and again after Classify, QC gathers the verdicts those steps already wrote into one place, so the question does not need five screens.

What it needs. A project or plate folder, chosen with Browse or dropped onto QC. Each card reads a file that is already on disk: the segmentation scorecards a Mask run writes (seg_qc), the units stamp Measure puts on every row of measurements.db, leakage.json from the newest Classify (CV) evaluation bundle, and plate_qc.json and agreement.json when a plate-effect or annotator-agreement report has been saved under the project.

What it produces. Nothing on disk. Each card carries a verdict – ok, missing, warn, fail or error – with a headline, the details behind it and, for a check that has never run, the step that would produce it; the overall verdict is the worst of them. A card older than its inputs is marked stale rather than downgraded, and missing means nothing was checked, not that nothing is wrong.

What to do next. Fix what a warn or fail card names at its source and re-run that step; the cards are read again when a file has changed. Layer Viewer shows the images behind a failing check, Control Charts follows the same checks over time and Outliers finds the wells or objects unlike the rest; all three open from this screen’s masthead.

It reads; it does not score. The rule is spacr.qt.prerun’s, and that module says why: opening a plate’s masks costs seconds to minutes, which is too slow to repeat on every visit. So the reads go through a fingerprint cache – one listdir and one stat per artifact – and the parse only happens when something on disk has actually changed.

Nothing here disables anything. Dashboard.blocks_run is a constant False, and this screen has no Run button to gate.

Classes

QCDashboardScreen

Read every QC verdict for a project and show them together.

Functions

install_folds(...)

Put qc_dashboard's fold strip on screen's masthead.

make_qc_dashboard_screen(→ PySide6.QtWidgets.QWidget)

Factory handed to spacr.qt.app.register_app().

register(→ bool)

Add the QC Dashboard to the app registry. Idempotent.

Module Contents

class spacr.qt.screens.qc_dashboard.QCDashboardScreen(parent: PySide6.QtWidgets.QWidget | None = None, *, src: Any = '', threaded: bool = True, reader=None)[source]

Bases: PySide6.QtWidgets.QWidget

Read every QC verdict for a project and show them together.

Parameters:
  • src – project folder to read; may be set later.

  • threaded – False reads inline, emitting the same signals in the same order, so a test can drive the screen synchronously.

  • reader – substitute for spacr.qt.widgets.qc_summary.read_dashboard(), for tests.

  • parent – parent widget; ownership only.

Build the dashboard and arm its drop zone.

The registry key is named here rather than inherited: screens that build themselves rather than being the generic AppScreen had none, and fold installation dispatches on exactly that – so this screen could declare folds and never be handed them.

Its job runner is marked not user-visible, because it never runs anything: it reads verdicts already on disk, plus the folder check and the fingerprint, on every visit including the ones where nothing has changed. Visible, each of those would flash “QC - running” on Home for a read the user never started.

Parameters:
  • parent – parent widget, or None.

  • src – project or plate folder to open with.

  • threaded – read on a worker thread. Set False in tests so refresh finishes before it returns.

  • reader – an alternative verdict reader, for tests.

active_jobs() → int[source]

How many worker threads are still winding down.

as_text() → str[source]

The dashboard as plain text, for a log or a paste.

closeEvent(event)[source]

Stop background work and unlink before going away.

Parameters:

event – the Qt close event.

dashboard() → spacr.qt.widgets.qc_summary.Dashboard | None[source]

The most recent Dashboard.

is_busy() → bool[source]

True while a read has not delivered its result.

refresh(*, force: bool = False) → bool[source]

Re-read the verdicts, entirely off the GUI thread.

The parse and the two decisions in front of it – “is this a folder?” and “has anything changed?” – all run on the worker, because both decisions touch the disk at a path the user typed. os.path.isdir is one call, and _fingerprint is a find_scorecards listing plus a stat per artifact.

Measured on one workstation: a single os.path.exists under /nas_mnt, an autofs mount whose share was asleep, had not returned after twenty seconds – the stat is what triggers the automount. Run on the GUI thread, that call would freeze the whole interface, with no traceback, whenever such a folder was dropped, browsed to or refreshed.

path_probe is not used for the folder guard. It answers optimistically, so it could only ever say “go on and read”, which the worker then checks anyway; a second guard on the GUI thread would only allow the two answers to disagree. The screen’s messages are unchanged; they appear when the worker finishes.

Parameters:

force – read even when the fingerprint says nothing changed.

Returns:

whether a read of the disk was started. Reading inline (threaded=False) that is exact, because the worker half has already run and reported by the time this returns. Threaded, it means the job was started: the fingerprint is not taken yet, so “nothing changed” arrives later, on the status line.

set_source(src: Any) → None[source]

Point the screen at a project folder and read it.

Parameters:

src – the project folder; its string form is put in the folder field and the verdicts are re-read with refresh().

source() → str[source]

The folder currently shown.

status_text() → str[source]

The status line. For tests.

visible_text() → str[source]

Every card line currently on screen, joined. For tests.

spacr.qt.screens.qc_dashboard.install_folds(screen: PySide6.QtWidgets.QWidget) → spacr.qt.widgets.fold_strip.FoldStrip | None[source]

Put qc_dashboard’s fold strip on screen’s masthead.

Reached by the one pass over the stack that serves every host – see spacr.qt.screens.map_barcodes.FOLD_HOST_MODULES.

spacr.qt.screens.qc_dashboard.make_qc_dashboard_screen(app_key: str | None = None) → PySide6.QtWidgets.QWidget[source]

Factory handed to spacr.qt.app.register_app().

spacr.qt.screens.qc_dashboard.register() → bool[source]

Add the QC Dashboard to the app registry. Idempotent.

Nested helpers

QCDashboardScreen.refresh.work(s=src, r=reader, force=force, previous=self._cache_key, had_one=self._dashboard is not None)

Off the GUI thread. Touches no widget – returns a verdict.

The cache comparison comes with it rather than staying behind: the key it compares is the walk of the disk, so leaving the comparison on the GUI thread would leave the walk there too.

spacr/qt/screens/qc_dashboard.py:388