spacr.qt.screens.project_browser¶
Workflow inputs and outputs¶
Project Browser¶
Locate projects and inspect their stage, last run, disk use and stale outputs.
Open: the application’s Help/tools menus.
Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.
Inputs
Run history and artifacts — Project run records, settings, output paths, artifact provenance, status and logs.
Outputs
Run history and artifacts — Project run records, settings, output paths, artifact provenance, status and logs.
Project Browser: every spaCR project under the search folders, in one table.
The table answers questions such as “which plates have been measured?”, “which one is the 400 GB?” or “which results no longer match the masks under them?” without opening each project folder.
It computes nothing. Every column comes from spacr.projects, which in
turn assembles spacr.data_manager.scan_project() (the size and the
unaccounted-for bytes), spacr.ports.declared_outputs() (the stage),
spacr.artifacts.Registry.is_stale() (what is out of date) and
spacr.chaining.next_steps() (what could run next), so the browser
reports the same values as the screens that compute them.
The browser has three properties that its design depends on.
A project the registry has never seen is still listed. Projects are found by walking the disk, so a folder copied from a colleague appears the moment the browser is pointed at its parent — with its stage, its size and the date its files were last written. What it does not show is “0 stale”, which would read as clean: with no provenance there is nothing to compare against, so the state column says “unknown — nothing recorded” and the note says why.
The scan never blocks the window. Walking a plate folder is tens of
thousands of stat calls, and doing it on the GUI thread would freeze the
application for however long the filesystem takes. Everything goes through
spacr.qt.job_runner.JobRunner, whose completion handlers here are
bound methods — read that module’s docstring for what a closure connected
to thread.finished costs. The background activity spinner follows the run
registry on its own, so nothing here has to drive it.
And neither does opening the screen, nor opening its folder chooser.
Two things here take a path the user typed on some other screen: the search
folders make_project_browser_screen() seeds from the recent-source list,
and the folder the “Add folder…” dialog starts in. Neither is checked with
os.path.isdir on the GUI thread: measured on one workstation, one such
folder was under an autofs mount whose share was asleep, and a single
isdir on it had not returned after twenty seconds, freezing the whole
application with no traceback. Both ask spacr.qt.path_probe, which
answers from a cache and stats in the background. Those two are the whole
inventory: the walk runs on a worker, and every column, note and detail line
the screen draws is rendered from the frozen
spacr.projects.ProjectSummary that walk already returned, so filling
the table and the detail pane reads nothing.
register() is not called at import; read its docstring.
Classes¶
The browser: a roots picker, a table of projects, and a detail pane. |
Functions¶
|
Factory handed to |
|
Put the Project Browser in the app registry. Idempotent. |
Module Contents¶
- class spacr.qt.screens.project_browser.ProjectBrowserScreen(parent: PySide6.QtWidgets.QWidget | None = None, *, threaded: bool = True, roots: Tuple[str, ...] = ())[source]¶
Bases:
PySide6.QtWidgets.QWidgetThe browser: a roots picker, a table of projects, and a detail pane.
- Parameters:
threaded –
Falseruns the scan inline through the sameJobRunnercode path, so a test drives the real thing synchronously.roots – folders to search on the first scan.
parent – parent widget; ownership only.
Build the project browser.
The project table (“Projects”) and the detail pane (“Project details”) fold by their headings and share a draggable edge in a
CollapsibleSplitter.- Parameters:
parent – parent widget, or
None.threaded – scan on a worker thread. Set
Falsein tests so a refresh finishes before it returns.roots – folders to scan for projects.
- add_root(path: str, *, scan: bool = True) bool[source]¶
Add a folder to search.
Truewhen it was not already there.- Parameters:
path – folder to add;
~is expanded and the path made absolute. It is also recorded as a recent folder.
- choose_root() None[source]¶
Ask for a folder and add it.
The dialog opens on a folder the probe cache has already confirmed rather than simply on the last one searched;
_start_directory()explains why: checking an unconfirmed folder could block the GUI thread.
- closeEvent(event)[source]¶
Stop background work and unlink before going away.
- Parameters:
event – the Qt close event.
- show_detail(root: str) str[source]¶
Draw the detail pane for one project. Returns what it drew.
- Parameters:
root – absolute project root as listed; a root with no listed summary clears the pane and returns
"".
- summaries() Tuple[source]¶
Every
spacr.projects.ProjectSummarycurrently listed.
- spacr.qt.screens.project_browser.make_project_browser_screen(app_key: str | None = None) PySide6.QtWidgets.QWidget[source]¶
Factory handed to
spacr.qt.app.register_app().Seeds the search folders from the ones the user last pointed any source-taking screen at, so the browser is useful on first open instead of empty.
- spacr.qt.screens.project_browser.register() bool[source]¶
Put the Project Browser in the app registry. Idempotent.
Called from
spacr.qt.SELF_REGISTERING_MODULES, whichspacr.qt.run()runs afterspacr.qt.appis fully executed and beforeMainWindow.__init__reads the registry.The row itself – the key, the name, the blurb, the section, the “no headless run” sentence, the API doc link and the nine translations of the display name – is declared in
spacr.qt.app_catalog.spacr.qt.app.register_app()distributes those into the four registry tables, and this function only names the row. The app can therefore be registered without importing this module: the launch reads the table, and the screen is imported when somebody opens it.- Returns:
Trueif this call is what registered it. Safe to call again: a module imported twice, or a test that re-imports it, must not raise on the duplicate key.