spacr.qt.widgets.home¶
HomePage — the Home screen.
┌────────────────────────────────────────────────────────────────┐ │ 🖼 spaCR End-to-end microscopy → single-cell measurements … │ │ ┌ Mask · running ────── 41 of 96 ──── [Open] [Pause] ────────┐ │ │ │ Home │ Core │ Data │ Segmentation models │ Results │ Toxo │ │ QUEUED │ │ CORE 9 ────────────────────────────────────────────────── │ │ RECENT │ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │ SYSTEM │ │ │ ▧ │ │ ▧ │ │ ▧ │ │ ▧ │ │ ▧ │ │ │ NEWS │ │ │Mask│ │Time│ │Moti│ │Meas│ │Anno│ │ │ TOTALS │ │ └────┘ └────┘ └────┘ └────┘ └────┘ │ │ ──────── │ │ DATA 6 ───────────────────────────────────────────────────│ │ ● Alpha │ │ ┌────┐ ┌────┐ … │ │ ● Beta │ └────────────────────────────────────────────────────────────┘ │ ● Stable │ Hover a tile to see what it does. │ └────────────────────────────────────────────────────────────────┘
The first tab displays every registered app grouped into the same sections as
the category-filter tabs. AppTile supplies one consistent icon-and-
name tile in every view; tooltips and the hint bar provide descriptions.
categories and bands remain separate inputs so navigation and the
all-app layout can evolve independently while both derive from
spacr.qt.app.APPS.
Maturity is represented by each tile’s stage property and the legend,
using spacr.qt.app.APP_STAGE and
spacr.qt.theme.STAGE_HOVER. The right column presents state rather
than navigation: queued work, recent runs, system information, release news,
totals, and maturity labels.
Home subscribes to spacr.qt.bridge.registry to display jobs started by
any screen. RunningBanner exposes only the controls supported by the
worker; cooperative Pause remains disabled when the pipeline has no
spacr.qt.bridge.PauseGate checkpoints. Widget colors are resolved
from spacr.qt.theme.active_palette() so runtime theme changes remain
consistent.
Attributes¶
Classes¶
The tile: a large square-ish button, icon over module name. |
|
Home. |
|
Every spaCR release, with links, in a box the reader can resize. |
|
Captioned box for the right-hand column. |
|
The plate queue, when there is one. |
|
Last few journalled runs of a REAL module; each row navigates. |
|
"spaCR is doing something right now" — with honest controls. |
|
What the three hover colours mean. One row per stage. |
|
GPU / VRAM / Disk, read on build and on every Home revisit. |
|
Aggregate counts from the automatically complete run journal. |
Functions¶
|
The palette for the theme that is on screen right now. |
Module Contents¶
- class spacr.qt.widgets.home.AppTile(text: str, description: str = '', icon: PySide6.QtGui.QIcon | None = None, *, width: int, height: int, icon_px: int = 52, stage: str = 'stable', parent=None)[source]¶
Bases:
PySide6.QtWidgets.QPushButtonThe tile: a large square-ish button, icon over module name.
One class for every tab including Home. There used to be two — a dense icon-beside-name row for Home and a tall card carrying the app’s one-line description everywhere else — and the difference made the first tab read as a list and the rest as a launcher. The description is gone with them: it was a third copy of a sentence already on the tooltip and in the hint bar at the foot of the page, and three lines of 11 px grey under every tile is what made the tiles small enough to need two sizes in the first place.
Deliberately not an
HTilesubclass.HTileis a horizontal row whose name and description live in aQLabelstack beside the button’s own icon; this is a vertical stack with the icon drawn as a child label. It has its own object name,AppTile, so the stylesheet can give it a height floor without giving one to every horizontal tile in the app.Its height floor is in the QSS, not here, and that is not a style choice.
setFixedSizedoes not survive polish, and neither does answering throughsizeHint/minimumSizeHint: the app stylesheet’s blanketQPushButton { min-height: 22px }becomes a realsetMinimumHeight(22), andqSmartMinSizelets an explicit minimum override the hints. On a page that does not fit, every tile then collapses to 22 px and paints its name over its icon. Seespacr.qt.theme.TILE_H. The hints below are still worth having — they are what the layout prefers — andheightForWidthis overridden with them becauseQWidgetItem::sizeHintreads it in preference tosizeHint().height()whenever it is available.- Parameters:
stage –
stable/beta/alpha. Set as a Qt property, which is what the stylesheet’sQPushButton#AppTile[stage="alpha"]:hoverrule selects on. Set before the widget is first polished, or the rule does not apply until something else forces a repolish.
Build one tile: an icon over a module name.
- Parameters:
text – the module name drawn on the tile, and what the tile is identified by.
description – accepted and NOT DRAWN. The tile stopped showing it because it was a third copy of a sentence already on the tooltip and in the hint bar, and three lines of grey under every tile is what forced two tile sizes in the first place. Kept in the signature so callers that pass it still work.
icon – the module’s mark, drawn above the name.
width – tile width in pixels.
height – tile height in pixels.
icon_px – the icon’s edge in pixels.
stage – maturity –
"stable","beta"or"alpha". Set as a Qt property, so the theme paints the badge and the maturity filter can find the tile without reading its text.parent – parent widget.
- heightForWidth(width: int) int[source]¶
At least the tile height, more if a child somehow needs it.
- Parameters:
width – proposed tile width in pixels, passed to the base implementation.
- is_name_elided() bool[source]¶
Whether the module name is being cut short to fit.
- Returns:
True when the label is eliding.
- minimumSizeHint() PySide6.QtCore.QSize[source]¶
The same as
sizeHint(): a tile does not shrink below its shape.- Returns:
the minimum size.
- sizeHint() PySide6.QtCore.QSize[source]¶
The tile’s preferred size, height derived from its width.
HEIGHT FOLLOWS WIDTH because the tile is icon-over-name in a fixed proportion; asking for a free height would let the grid stretch one tile and not its neighbours.
- Returns:
the preferred size.
- class spacr.qt.widgets.home.HomePage(apps: List[Tuple[str, str, str, str]], icon_provider: Callable[[str], PySide6.QtGui.QIcon | None], parent=None, *, section_notes: Dict[str, str] | None = None, categories: Sequence[Tuple[str, Sequence[str]]] | None = None, bands: Sequence[Tuple[str, Sequence[str]]] | None = None, stages: Dict[str, str] | None = None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetHome.
tile_clicked(str key)fires when a tile is pressed.Drop-in for the page it replaces: same constructor, same signal, same
set_reserved_contentescape hatch.- Parameters:
apps –
(key, name, description, section)per app.icon_provider – app key → QIcon (or
None).section_notes – optional section → one line, drawn under that category’s heading on its own tab. A category with two apps in it looks broken until it says why; passed in rather than imported so this widget still knows nothing about
spacr.qt.app.categories – optional ordered
(title, [app key])— one entry per tab after Home. Defaults to groupingappsby their section in first-appearance order, which is what every test that builds a HomePage out of a handful of tuples wants.bands – optional ordered
(title, [app key])for the Home tab. Same default. Kept separate fromcategoriesbecause the two answer different questions, even when — as today — they return the same list. See the module docstring.stages – optional app key →
stable/beta/alpha. Becomes each tile’sstageproperty, which is what the app stylesheet turns into its hover colour, and what the legend at the foot of the aside is drawn from. Anything missing is stable.parent – parent widget; ownership only.
Build Home: the hero, the module tabs and the right-hand column.
- Parameters:
parent – parent widget.
- apply_release_news(releases) None[source]¶
Hand a fetched release list to the News panel.
The answer to
news_refresh_requested, and the only way in: the window never reaches into the panel, so a page rebuilt at a new font scale simply asks again.- Parameters:
releases – records from
spacr.updater.fetch_release_notes(), or anything at all.
- closeEvent(event)[source]¶
Stop Home-page background activity before closing.
Shut down the journal reader, disconnect run-registry notifications, and stop the refresh ticker before delegating to the base close handler. This prevents pending work from invoking a page that Qt is destroying.
- Parameters:
event – the close event; it is passed on to the base class after background activity stops.
- eventFilter(obj, event)[source]¶
Watch the widgets this filter is installed on.
- Parameters:
obj – the object the event is for.
event – the event.
- Returns:
True to stop the event going further.
- page_fill()[source]¶
The flat colour Home paints itself, or
None.The same rule, and the same reasoning, as
spacr.qt.screens.app_screen.AppScreen.page_fill(): with an animation installed the animation is the page, with an image theme the window’s wallpaper is, and otherwise it is this — a real colour rather than thebgslab that no page-opacity setting can reach.Never raises.
- paintEvent(event) None[source]¶
Paint the page under everything Home lays out.
Does not chain to
super()when it fills: the base implementation is what draws the stylesheet background, and that background is the slab being replaced.- Parameters:
event – the paint event; passed to the base class only when no page fill colour is set.
- refresh() None[source]¶
Re-read everything that can change while Home is off screen.
The two run-journal panels are read on a worker thread. Together
recent_runs+journal_totalswalk every manifest under the runs root twice — 774 ms on a machine with 4 865 journalled runs, measured, and it grows with the journal — and this used to run inline on every single return to Home, which is the most-travelled navigation in the application.The panels keep whatever they are already showing until the worker delivers; a stale count for half a second beats a frozen window, and on the first ever call they are showing their empty state anyway. Everything else here is cheap (a JSON read and three stat calls) and stays inline.
- resizeEvent(event)[source]¶
Re-flow the tile grid for the new width.
- Parameters:
event – the Qt resize event.
- set_reserved_content(widget: PySide6.QtWidgets.QWidget) None[source]¶
Fill the featured/news surface with real content.
- Parameters:
widget – the widget to show in the featured/news panel in place of the release notes.
- show_module_hint(key: str, summary: str = '') bool[source]¶
Explain
keyin the strip. Called by the DOCK as well as Home.The dock’s rows and Home’s tiles name the same modules, so they say the same thing in the same place –
MainWindow._show_module_hintroutes a dock hover here whenever Home is the page on screen.- Parameters:
key – the module to explain.
summary – the sentence, already resolved and translated by the caller. Empty falls back to Home’s own registry, which is what a tile hover uses – it has the description in hand and has no reason to ask the window for it.
- Returns:
whether anything was written. A key Home does not know is not an error: the dock lists Help modules that have no tile.
- property legend: StageLegend[source]¶
The colour-to-maturity key at the foot of the right column.
- class spacr.qt.widgets.home.NewsPanel(version: str = '', parent=None)[source]¶
Bases:
PanelEvery spaCR release, with links, in a box the reader can resize.
THE NOTES ARE BUNDLED, and the bundle is what draws. They come from
spacr/resources/release_notes.json, whichtools/build_release_notes.pywrites from the GitHub releases and which.github/workflows/release.ymlrefreshes on every release. This panel is on the first screen the application shows, so making its CONTENT depend on api.github.com would mean a dashboard that is empty offline, throttled behind a shared NAT, and slower to draw than the window it is in. The bundled file therefore remains the offline source of truth and the panel is complete before anything touches a socket.AND THEN IT CATCHES UP. The wheel for a release cannot contain its own release note – the note is written when the GitHub release is published, which is after that wheel is on PyPI – so a bundled file is always one release behind the build carrying it, and that is what went wrong: “im on 1.5.1.0 and the news only goes to 1.5.0.7. the news section should always automatically reflect the latest spacr release news.” So after the page is shown,
refresh_requestedasks the window to read the public releases list on a worker thread, andapply_releases()merges whatever comes back in front of the bundled list. Nothing here opens a socket: the panel only asks, and a fetch that fails, is rate-limited, is switched off in Preferences, or simply finds nothing newer leaves the bundled list exactly as it was drawn.There was previously no feed at all and this panel said so – “No release notes bundled with this build” – which was honest and useless. What it shows now is the real thing: “News should contain all the releas information with links. i nkow that there was release information for the current 1.5.0.4 version. this one as well as all of the other ones should be scrollable and the user should be able to controll the height.”
So: every release newest-first, each one’s body rendered with its links live, the whole list inside a scroll area, and a grip along the bottom edge that drags the box taller or shorter. The height is remembered between sessions – a reader who made it tall wants it tall next time.
The update check stays a BUTTON. Offering to install something is a decision, so it is still made only when pressed.
- Parameters:
version – the build to name in the heading. Empty leaves the heading as the translated word alone – the two are kept separate because the catalog is keyed on “News”, so composing the release into the caption first would leave the only aside panel that names a build in English.
parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
- apply_releases(fetched) None[source]¶
Merge a fetched release list into the list on screen.
Silence is the contract. An empty list, a list of rubbish, or a list that says nothing the bundled file did not already say leaves the panel untouched and says nothing to the reader – the failure of an unasked-for background fetch is not the reader’s problem.
- Parameters:
fetched – release records from
spacr.updater.fetch_release_notes(), or anything at all.
- static merge_releases(bundled, fetched) list[source]¶
The bundled and fetched lists as one, newest first.
One record per tag, and a fetched record wins: the same release can have its notes edited on GitHub after it ships, and the live copy is then the true one. Ordering is by publication date and then by the version in the tag, so a release published on the same day as the one before it still lands above it.
- Parameters:
bundled – the records read from the wheel.
fetched – the records read from GitHub, or
None.
- Returns:
a new list; neither argument is modified.
- static read_releases() list[source]¶
The bundled release records, newest first, or
[].Degrades to an empty list on ANY failure, and the panel then draws the placeholder it drew before there was a feed. A dashboard must not fail to appear because a resource file is missing from a wheel.
- static render_body(body: str, link_colour: str) str[source]¶
Turn a release body into the small HTML subset a QLabel draws.
NOT A MARKDOWN RENDERER, and not trying to be. Release bodies are GitHub-flavoured markdown; what they actually contain is bullet lists, bare URLs and
**bold**, and a QLabel understands a handful of tags. So: escape everything first – a body is text from a web page and must never reach a rich-text widget as markup – then put back the three constructs that are worth having.Escaping FIRST is what makes this safe. Linkifying first and escaping after would escape the anchors too and show the reader their own tags; escaping after building the HTML is the mistake that turns a release note into an injection.
- Parameters:
body – release-note text in GitHub-flavoured markdown; it is HTML-escaped before bare URLs,
**bold**,##headings and bullet lines are turned into tags.link_colour – CSS colour for the generated links.
- set_content(widget: PySide6.QtWidgets.QWidget) None[source]¶
Replace the release list with real content.
The escape hatch
HomePage.set_reserved_content()exposes. It hides the bundled notes rather than deleting them, so a caller that drops content in has not thrown the feed away.- Parameters:
widget – the widget to insert at the top of the panel body; any previous content widget is deleted and the bundled notes are hidden.
- showEvent(event)[source]¶
Ask for a refresh the first time the panel is shown.
AFTER the page exists and ON THE EVENT LOOP, not during construction: the single-shot timer means the emit lands on a later turn than this show, so Home’s first paint is never waiting on it. Once per panel, because a page that is shown again – a tab revisited, a font-scale rebuild – is not news.
- Parameters:
event – the Qt show event.
- class spacr.qt.widgets.home.Panel(title: str, parent=None, *, beta: bool = False)[source]¶
Bases:
PySide6.QtWidgets.QWidgetCaptioned box for the right-hand column.
The border lives on a
QFramewith its own object name and the rule is scoped to it. An unscoped border rule cascades into every child row and outlines each one — a mistake this codebase has already made once on the Home dashboard.- Parameters:
beta – mark the header with
BETA_SUFFIX. The suffix is appended after the upper-casing, so it stays lower case and reads as a mark on the heading rather than part of it.title – the caption above the panel. Drawn OUTSIDE the panel’s frame, beside any action word, rather than inside it.
parent – parent widget; ownership only.
Build a captioned box for the right-hand column.
- Parameters:
title – the caption.
parent – parent widget.
- action(text: str) PySide6.QtWidgets.QPushButton | None[source]¶
The action word named
text, orNone. For tests.- Parameters:
text – the action’s label, matched case-insensitively.
- add(widget: PySide6.QtWidgets.QWidget) PySide6.QtWidgets.QWidget[source]¶
Add a widget to the panel body, transparent so the box shows through.
The border and fill live on the frame around the body, so a child painting its own background would draw a square inside the rounded box rather than sitting in it.
- Parameters:
widget – the widget to add.
- Returns:
the same widget, for chaining.
- add_action(text: str, *, kind: str = 'danger', tip: str = '') PySide6.QtWidgets.QPushButton[source]¶
Put an action WORD on the right of the panel’s caption.
Not a button in the styled sense – no frame, no fill, no padding that would make it look pressable. It is the word alone, in the muted caption ink, and it takes its colour when the pointer is on it or while it is held down: “just the text clear which turns red upon hover or click” (2026-09-03).
Rendered through a QPushButton rather than a QLabel because the word is a CONTROL: a button is what Tab reaches, what Space activates, what a screen reader announces as pressable, and what already has a
:pressedstate to hang the click colour on. A clickable QLabel has none of that.- Parameters:
text – the word to draw. Not upper-cased – the caption beside it is, and matching it would make the word read as a second heading.
kind –
dangerfor a word that throws something away, drawn red;safefor one that only re-reads, drawn in the accent.tip – optional hover help. Also becomes the accessible description, because that is what a screen reader reads.
- Returns:
the button, so the caller can connect it.
- class spacr.qt.widgets.home.QueuedPanel(parent=None)[source]¶
Bases:
PanelThe plate queue, when there is one.
Reads
~/.spacr/queue.jsonthroughspacr.qt.plate_queue. PlateQueue— read-only; the Queue screen owns writes. The panel hides itself when the queue is empty rather than drawing an empty box, which is the difference between “nothing queued” and “queue broken”.- Parameters:
parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
- clear_queue() int[source]¶
Drop every item from the plate queue. Returns how many went.
WRITES, which no other part of this panel does – the class docstring says the Queue screen owns writes, and this is the one exception: Clear empties the queue from here rather than sending the reader to another screen to do it.
Not confirmed, and deliberately. A queue entry is a plate waiting to be processed – settings and a source path, no results – so clearing one throws away a few seconds of setting up, not any data. A confirmation on a two-word action that costs that little is a dialog people learn to dismiss without reading.
Every failure mode ends with the panel telling the truth about what is in the queue, because it re-reads from disk afterwards either way.
- queue_items() List[source]¶
Whatever is in the saved plate queue right now.
Read from disk on each call rather than cached: the queue is written by other screens, and a Home page showing a stale count is worse than one that costs a file read when it is looked at.
- Returns:
the queued items, empty when there is no queue.
- class spacr.qt.widgets.home.RecentRunsPanel(limit: int = 4, known_keys=None, parent=None, read_now: bool = True)[source]¶
Bases:
PanelLast few journalled runs of a REAL module; each row navigates.
“Real module” is doing work here. The panel used to list whatever the run journal’s newest manifests said, and those rows were clickable without representing runs – they opened a
_jobmodule. On one machine the journal held 11,046 run folders of which 7,323 were written under the app_key_joborjob: a test fixture’s key, written straight into the realspacr/runsbecause nothing sandboxed it (fixed intests/conftest.py). So most rows named a module that does not exist, and clicking one asked the window to open it.The pollution is stopped at the source now, but a filter here is still the right thing: this panel NAVIGATES, so a row it draws is a promise that pressing it goes somewhere. Anything whose key is not a module Home knows about is dropped.
- Parameters:
limit – how many journalled runs to show.
known_keys – callable returning the module keys that exist, or a mapping of key to display name (or either, directly).
Nonefilters nothing, which is what a standalone panel with no registry to consult has to do.parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
read_now – read the journal on the calling thread now. Home passes
Falseand fills the panel from its worker read, so the window is not held up by the journal.
- clear_list() None[source]¶
Hide every run listed, by moving the watermark to now.
NOTHING IS DELETED. See
spacr.qt.preferences.get_dashboard_watermark()for why: this panel reads the run journal, and the journal is the record Run History searches and a run’s manifest documents.
- name_for(key: str) str[source]¶
key’s display name when Home knows one, elsekeyitself.A row used to be captioned with the raw app_key, which is what put
_jobandmaskon the dashboard in the same typeface as each other. The names are already in Home; the panel just had no way to ask for them.- Parameters:
key – application key to name; returned unchanged when the name registry does not know it.
- read() list[source]¶
The journal entries this panel would show. Worker-thread safe.
Split out of
refresh()soHomePagecan call it off the GUI thread: it touches no widget, only the run journal.recent_runsopens and JSON-parses every manifest in a bounded window of the newest run folders before it sorts and truncates – measured at 540 ms over 4,865 of them – so it is not something the GUI thread should be doing on the way back to Home.THE FILTERING HAPPENS HERE, not in
refresh, for two reasons. It is the half that runs off the GUI thread, and it changes how much has to be read: dropping four rows in five after asking for five leaves one, so this asks for a multiple of the limit and truncates afterwards.
- class spacr.qt.widgets.home.RunningBanner(icon_provider: Callable[[str], PySide6.QtGui.QIcon | None], names: Dict[str, str], parent=None)[source]¶
Bases:
PySide6.QtWidgets.QFrame“spaCR is doing something right now” — with honest controls.
Reads
spacr.qt.bridge.registry(), so it reflects a job started from any screen. Hidden entirely when nothing is running, which is the common case and should cost the page nothing.On the Pause button. It is enabled if and only if the running job’s entry point declares itself
bridge.pausable— i.e. it actually pollsbridge.checkpoint(). No shipped pipeline does, so today it renders disabled withPAUSE_UNAVAILABLEas its tooltip. That is the whole point: a Pause button that stops the thread wherever it happens to be is not a pause, and the honest thing to draw is a control that says so.- Parameters:
icon_provider – called with a module key for that module’s icon. A callable rather than a dict of icons, so the banner does not build artwork for modules that never run.
names – module key to the name a human reads, for the banner’s text and for
open_requested’s meaning.parent – parent widget.
Build the banner for one running module.
- Parameters:
icon_provider – how to get a module’s icon.
names – display names, keyed by module.
parent – parent widget.
- class spacr.qt.widgets.home.StageLegend(parent=None)[source]¶
Bases:
PanelWhat the three hover colours mean. One row per stage.
The legend follows the numeric status panels in the right-hand column because it explains the module tiles rather than reporting machine state.
Each row draws the stage’s hue as a filled swatch and names the stage in words. Colour alone would fail WCAG 1.4.1 and would be invisible to the colour-blind mode this app already ships — the words are what make it a legend rather than a palette.
The rows are built from
spacr.qt.theme.STAGE_HOVER, which is the same table the stylesheet builds the hover rules from, so the swatch and the tile it explains cannot drift apart.- Parameters:
parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
- class spacr.qt.widgets.home.SystemPanel(parent=None)[source]¶
Bases:
PanelGPU / VRAM / Disk, read on build and on every Home revisit.
Every reading degrades to a string rather than vanishing — a blank row reads as “broken”, while
n/ahonestly says the lightweight system probe could not measure a device. Home must not import a model runtime merely to decorate the dashboard.- Parameters:
parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
- static disk_used() str[source]¶
Disk use for the working volume, as text for display.
- Returns:
the reading, or a dash when it cannot be taken.
- static gpu_util() str[source]¶
Current GPU utilisation, as text for display.
NEVER RAISES. A missing NVML, a machine with no GPU and a driver mismatch are all ordinary here, and none of them is a reason for the Home page to fail to build.
- Returns:
the reading, or a dash when it cannot be taken.
- static gpu_vram() str[source]¶
Current VRAM use, as text for display.
Never raises, for the same reason as
gpu_util().- Returns:
the reading, or a dash when it cannot be taken.
- class spacr.qt.widgets.home.TotalsPanel(parent=None, read_now: bool = True)[source]¶
Bases:
PanelAggregate counts from the automatically complete run journal.
- Parameters:
parent – parent widget.
Build the panel and its caption.
- Parameters:
parent – parent widget.
read_now – read the journal on the calling thread now. Home passes
Falseand fills the panel from its worker read.
- read() dict[source]¶
The journal totals. Worker-thread safe — see
RecentRunsPanel.read();journal_totalswalks the same thousands of manifests, measured at 247 ms.Counted from the Reset watermark when one is set, which is the one case this cannot answer out of the cached totals file: that file holds LIFETIME counts, so a windowed count has to walk the runs the window covers. It is bounded by the window, which is the property that makes the feature affordable — somebody who reset yesterday is counting yesterday’s runs, not eleven thousand of them.
- refresh(totals: dict | None = None) None[source]¶
Redraw the panel.
- Parameters:
totals – counts a worker has already read;
Nonereads them on the calling thread.
- reset_counts() None[source]¶
Count from now on, by moving the watermark.
NOTHING IS DELETED, for the same reason Clear on Recent runs deletes nothing — see
spacr.qt.preferences.get_dashboard_watermark(). What these counts are FOR is telling the user how much this installation has done, and a reset that removed the manifests would take Run History and every run’s provenance with it.
- spacr.qt.widgets.home.active_palette() dict[source]¶
The palette for the theme that is on screen right now.
Not
theme.PALETTE: that module-level dict is the dark palette and nothing ever updates it, so a widget that inlines colours from it renders dark on every theme. On the light theme that produced black panels with black text in the right-hand column — unreadable, and invisible to any test that only checks widget structure.Home is rebuilt from scratch on a theme change (
MainWindow._rebuild_startup_page), so resolving once per widget construction is enough.
- spacr.qt.widgets.home.PAUSE_UNAVAILABLE = Multiline-String[source]¶
Show Value
"""Pause is not available for this module. Pausing means holding the pipeline at a point where nothing is half-written — between fields, not mid-write. spaCR's pipelines do not yet offer such a checkpoint, so a Pause button here could only freeze the thread wherever it happened to be, which can truncate a mask file or leave a field measured into some tables and not others. Use Stop, or queue plates so the run can be halted between them."""
Nested helpers¶
- HomePage._build_hero._draw_mark(label=label, source=logo, side=logo_px)¶
Redraw the mark at the ratio of the screen it is on.
Always from the 3334 px master. Re-scaling whatever is already on the label would compound one resample for every screen the window has been dragged across.
spacr/qt/widgets/home.py:2290
- _scale_text_under.scaled(sheet: str) str¶
sheetwith every font-size in it multiplied byratio.spacr/qt/widgets/home.py:1816
- _scale_text_under.scaled.one(match)¶
One
font-sizedeclaration, scaled and floored.spacr/qt/widgets/home.py:1818