spacr.qt.app¶
QApplication bootstrap + MainWindow.
launch(argv) is the public entry point called by spacr-qt and
python -m spacr.qt.
Classes¶
Top-level window: sidebar + stacked screens + status bar. |
|
The application's navigation dock. |
Functions¶
|
Whether |
|
How finished |
|
The dock's TOP-LEVEL rows: Home's tiles, then Help, in that order. |
|
|
|
|
|
|
|
app key → stage, for every app in |
|
Install the registered dialog event filters on a Qt application. |
|
Put the spaceout fractal behind |
|
Bootstrap QApplication and show the main window. |
|
Build the Home page exactly as the running app builds it. |
|
Add one app to the registry. The seam a new module registers through. |
|
Import and return the pipeline callable app |
|
The factory registered for |
|
|
|
The |
|
Sort key placing |
|
The |
|
Remove app |
|
Registry rows allowed by the Alpha/Beta visibility preferences. |
Module Contents¶
- class spacr.qt.app.MainWindow(initial_app: str | None = None)[source]¶
Bases:
PySide6.QtWidgets.QMainWindowTop-level window: sidebar + stacked screens + status bar.
- Parameters:
initial_app – optional app key to navigate to on show; when omitted the window opens on the Home startup page.
Build the main window: the dock slot, the screen stack and the status bar.
The window is frameless and the menu bar is what you drag it by. Its first backing store is filled with the splash colour so the compositor cannot expose stale desktop pixels before anything has drawn – deliberately without
WA_OpaquePaintEvent, which is a promise to paint every pixel that this window cannot keep once the application stylesheet clearsautoFillBackgroundagain. Claiming it left whatever was already on screen underneath, and transparent children drew on top: overlapping text in the corner, and flicker on every text surface over the animated backdrop.The dock slot exists in every dock mode, because a
QMainWindow’s central widget cannot be swapped without re-parenting the stack, and re-parenting a stack holding live screens is how locking the dock would cost you the screen you were looking at.- Parameters:
initial_app – the module to open on startup;
Noneopens Home.
- __init__(initial_app: str | None = None)[source]¶
Build the main window: the dock slot, the screen stack and the status bar.
The window is frameless and the menu bar is what you drag it by. Its first backing store is filled with the splash colour so the compositor cannot expose stale desktop pixels before anything has drawn – deliberately without
WA_OpaquePaintEvent, which is a promise to paint every pixel that this window cannot keep once the application stylesheet clearsautoFillBackgroundagain. Claiming it left whatever was already on screen underneath, and transparent children drew on top: overlapping text in the corner, and flicker on every text surface over the animated backdrop.The dock slot exists in every dock mode, because a
QMainWindow’s central widget cannot be swapped without re-parenting the stack, and re-parenting a stack holding live screens is how locking the dock would cost you the screen you were looking at.- Parameters:
initial_app – the module to open on startup;
Noneopens Home.
- apply_dock_mode(mode: str | None = None) None[source]¶
Put the app list where the preference says it goes.
Two modes, one
Sidebarobject:lockedthe sidebar sits in the window’s dock slot, where itis an ordinary column in the layout: it never animates, and because it is a LAYOUT MEMBER rather than an overlay the page beside it is narrower by the dock’s width instead of running underneath it.
hiddenthe slot stays empty. The “All apps” action isdisabled with a tooltip that says where to turn it back on — a control that silently does nothing is worse than one that is greyed out.
There is no reveal-on-hover mode: a dock that slides in over the page covers the Home tiles, and needs a second container behind its panel to stay legible. The drawer object still exists and is kept closed and disabled, because it also carries the keyboard path that the “All apps” action uses.
Idempotent, and safe to call before the menu exists.
- changeEvent(event)[source]¶
Re-lay the menu bar when the window state changes.
A menu opens at the rectangle the menu bar holds for its action. Going fullscreen resizes the bar and its corner widget in one step, and a menu opened before the layout has caught up is placed against the previous action rectangle – which is how pressing spaCR drops a menu under Help.
- Parameters:
event – the change event; only a window-state change re-lays the menu bar.
- closeEvent(event)[source]¶
Cooperatively drain analysis and UI workers before destruction.
- Parameters:
event – the close event; ignored, leaving the window open, when a worker or an application screen does not stop, otherwise passed to the base class, and the application quits if it is accepted.
- dock_mode() str[source]¶
The user’s dock preference —
auto/locked/hidden.Read through here rather than inlined so a headless build, or one with an unwritable settings file, still gets the default rather than an exception during
__init__.
- eventFilter(watched, event)[source]¶
Handle frameless-window menu-bar drag and double-click gestures.
Teardown can invalidate the Qt menu bar while events are still queued. This filter returns
Falseinstead of allowing that condition to escape through PySide’s callback boundary.- Parameters:
watched – Qt object receiving the event.
event – Qt event delivered to the main window’s filter.
- Returns:
Truewhen a menu-bar mouse gesture is consumed; otherwiseFalse.
- keyPressEvent(event) None[source]¶
Up and Down change the spaceout zoom rate; Ctrl+R starts over.
- Parameters:
event – the key press; its key and Ctrl modifier are read, and keys the backdrop does not take are passed to the base class.
- open_module(app_key: str) str[source]¶
Navigate to the screen that carries
app_key, folded or not.A module folded into a host keeps its key everywhere a key is saved: a run journal, the force-restart record,
spacr-qt <app>in somebody’s shell history. That key no longer names a screen, and navigating to it directly would build one – an orphan page with no sidebar row, no tile and no way back to it.So the key is resolved to the host that took it over, and the fold it names is switched on: asking for Timelapse is asking for what the tracking switch reveals, not for mask generation with the switch off.
- Parameters:
app_key – the key that was asked for.
- Returns:
the app key actually opened.
Give every menu-bar action an explicit macOS role. Idempotent.
Called at the end of
_build_menu_barAND again fromspacr.qt.shortcuts.install, becauserecipes,walkthroughandfeature_dictionaryall add to Help afterwards. Each of those also pins its own – defence in depth – but a central re-sweep is what makes a module added later safe without its author knowing this problem exists.
- rebuild_app_screen(key: str, values=None) None[source]¶
Build
key’s screen again, carryingvaluesacross.- Parameters:
key – the app whose screen is to be rebuilt.
values – settings to apply to the new screen.
The whole screen is rebuilt because which settings a form holds depends on a few of its own values – the organelle count, and whether an object’s channel names a plane – so a committed change to one of those means a different form, not a changed one. Rebuilding is the same path every module open already takes.
The old screen is dropped from the cache and destroyed, so the new one is built from scratch rather than reusing widgets that belong to a shape that no longer applies.
- refresh_after_preferences(changed) str[source]¶
Redraw what a Preferences Apply changed, and nothing else.
THE APPLY THAT CHANGED ONE SLIDER REBUILT HOME. Page opacity is in the window’s signature, so moving it ran
refresh_theme()– Home rebuilt (2.1-2.6 s on a loaded box), the tree retranslated (0.3 s), every alpha gate walked – while the only things on Home that had taken the opacity were three rules, whichHomePage.refresh_pane_surfaces()sets again in place.- Parameters:
changed – names from the window’s signature whose values changed (
spacr.qt.preferences._window_changes()).- Returns:
"rebuilt","in place"or"nothing", so a test can say which.
- refresh_theme() None[source]¶
Rebuild everything preferences cannot update through QSS alone.
Three things do not follow a
setStyleSheetcall: the Home tiles set sizes/margins from the font scale in Python, every QIcon baked its pixmap at the theme in force when it was built, and the dock’s mode and the page’s opacity are layout decisions rather than colours. Called after the Preferences dialog closes, and found by duck-typing fromspacr.qt.preferences.apply_preferences_to_app()so a preference change from anywhere reaches the widgets.
- resizeEvent(event)[source]¶
Keep the loading screen covering the whole window, and erase the menu bar’s old band.
This runs on every resize, not only on a state change. Dragging an edge moves the corner buttons without changing the window state, so
changeEventdoes not fire, and without this the marks would be redrawn at their new x over the old ones.- Parameters:
event – the resize event, passed to the base class; the new geometry is read from the window itself.
- resume_after_restart() str[source]¶
Reopen the module a Force restart saved. Returns its key, or “”.
The state is taken, not read:
restart_state.takedeletes it as it hands it over, so a crash during startup cannot make spaCR reopen the same failing module on every later launch.The settings are applied and the run is not started: only the configuration is restored, not the runs.
- show_preferences_on(tab: str = '', label: str = '') bool[source]¶
Open Preferences, on a named tab, with a named row marked.
The route the Help search field takes for a preference result: the dialog carries nine tabs and a result that opened it on whichever one it happened to start with has answered half the question.
The tab is found by object name rather than by index or caption. An index moves whenever a tab is added, and a caption is translated – a Korean interface would have matched nothing. Every page sets its own
PreferencesTab*name, andtools/build_help_search_index.pyreads those same names out of this dialog’s source, so the two ends of the hand-off are the same string by construction.The dialog is usually built already (
spacr.qt.preferences._warm_the_preferences()), and the window redraws after the close only when something it draws from changed: the build, the first show and the unconditional redraw each held the GUI thread for longer than a frame.- Parameters:
tab – the page’s object name, e.g.
"PreferencesTabTheme";""opens the dialog as it opens from the menu.label – the row caption to mark;
""marks nothing.
- Returns:
True when the named tab was found and shown.
- snap_window(edge, screen=None) bool[source]¶
Snap the window to one side of a screen’s work area.
"up"maximises (the platform’s own maximised state, inside the work area that excludes the taskbar, dock or panels);"left"and"right"take the left or right half and"down"the lower half ofQScreen.availableGeometry(). Full screen is a separate state (toggle_fullscreen()) and is left first if it is on. Work areas are in logical pixels, so display scaling is Qt’s.The size before the first snap is kept in
_snap_restoreand comes back when the window is dragged off its tile or restored.On Wayland a client cannot place its own top-level window: the half tiles are applied as a size and the compositor decides the position, so they report
False. Maximising is a state request every compositor honours. Dragging the title bar on Wayland goes throughstartSystemMoveand the compositor’s own snapping instead.- Parameters:
edge –
"left","right","up"or"down".screen – the screen to snap on; defaults to the window’s own.
- Returns:
whether the requested geometry was applied.
- stylesheet_roots()[source]¶
The widgets that carry the application sheet, instead of me.
WHY NOT THE WINDOW. Setting a stylesheet on a window repolishes every descendant, and a session that has opened four modules owns 8,002 widgets of which 7,595 are inside module screens – ONE of which is on screen. Measured on this box, four modules open, alternating dark and light:
the whole window 945 ms first, ~1,479 ms steady chrome + the visible page 300 ms first, ~310 ms steady
WHAT IS IN THE LIST, and the shape is not the obvious one. The chrome is 407 widgets and only TWO of them are reachable from the central widget – the dock, the sidebar and the status strip hang off the window itself. An implementation that walked the central widget would leave 405 unstyled and look almost right, which is why
tests/qt/test_every_widget_wears_the_current_theme.pydoes exactly that on purpose and asserts it is caught.So: every direct child of the window except the central widget, every direct child of the central widget except the stack, and the stack’s CURRENT page. The pages that are not showing are marked instead, and sheeted on their own
showEventbefore they are painted.A child that is a window of its own – a dialog parented here, such as Preferences – is not chrome: the sheet reaches it as a window, and a hidden one that asked to wait for its show (
spacr.qt.theme.SHEETED_WHEN_SHOWN) waits.
- toggle_app_drawer() None[source]¶
Put keyboard focus on the app dock. The menu and Ctrl+Shift+A path.
The dock does not slide, so there is nothing to open: it is a permanent column when the preference is
locked. The action moves keyboard focus to its first row, so a keyboard user does not have to tab through the page to reach it.A no-op when the dock is hidden, so the shortcut does not override the preference. The action is also disabled in that mode; this check is a second guard.
- toggle_fullscreen(*_args) bool[source]¶
Enter or leave true fullscreen. Returns whether it is now full.
Leaving returns to the state before: maximised, a tile, or the ordinary window. Independent of the top snap, which maximises.
- class spacr.qt.app.Sidebar(parent=None)[source]¶
Bases:
spacr.qt.widgets.dock.DockThe application’s navigation dock.
The behaviour lives in
spacr.qt.widgets.dock.Dock. This class only binds it to the registry, and it keeps the nameSidebarbecause the theme’s#Sidebarrules, the tutorial highlighter, the home-variant generators and the tests all reach the dock by it.The module docstring of
spacr.qt.widgets.docklists the parts of the earlier_DockRow/Sidebarimplementation and what replaced each one.- Parameters:
parent – parent widget; ownership only.
Build the dock with Home above the registered module rows.
- Parameters:
parent – parent widget, or
None.
- spacr.qt.app.app_is_visible(key: str) bool[source]¶
Whether
keyshould appear in module navigation.Preferences are imported lazily so this registry remains safe for packaging and headless callers. If preferences cannot be read, preserve the historical all-modules-visible behaviour.
This is not a tile check. It answers whether the user may reach this module, which depends only on the maturity preference. A folded module is still reachable – from its host’s button, from Help, and from the command palette – so it is visible here although it has no tile. Use
tiled_apps()to ask whether a module has a tile.This function did briefly answer both, and the command palette is what caught it: filtering
TILELESS_APPSout here took nine modules out of Ctrl+K, so the folds removed a door instead of moving one. Two questions, two functions.- Parameters:
key – the app key whose stage is checked against the maturity preference.
- spacr.qt.app.app_stage(key: str) str[source]¶
How finished
keyis — one ofSTAGES.Unknown keys read as stable rather than raising: a stage is an annotation on an app, and an app with no annotation is one nobody has flagged.
- Parameters:
key – the app key looked up in
APP_STAGE; unknown keys giveSTAGE_STABLE.
- spacr.qt.app.dock_rows() List[Tuple[str, str, str, str]][source]¶
The dock’s TOP-LEVEL rows: Home’s tiles, then Help, in that order.
A row is a dock host if, and only if, Home draws a tile for it. Everything else in the registry reaches the dock as an indented child under its fold host, or under one of the Help modules, and nothing appears twice.
The rows therefore match the Home screen: modules that belong to Help are listed under Help, and a module folded into a host is nested under that host instead of being listed at the top level.
Measured before the change: 36 top-level dock rows against 19 Home tiles, and NINE keys drawn twice –
convert,external_masks,investigate_hit,layer_viewer,lineage,plate_view,profiler,tabulateandtrain_compareeach had a top-level row AND an indented row under the host they fold into. A module that appears in two places in the same column is a module the reader cannot learn the location of.tiled_apps()is the authority rather than a second hand-written list, so the dock cannot drift from Home again: promoting a module to a tile gives it a dock row, and folding one takes its dock row away, both without touching this function. That is the same lesson_HELP_MODULESalready encodes below – 330’s note records one bug from a duplicated list and the retired-section map records another.Help is not a section (see
SECTION_HELP). A section is Home’s categorisation and every Help module is tileless, so Help would be a tab with nothing on it. The dock has no such constraint.The dock walks these rows in order and starts a new heading whenever the section changes, so rows of one section must be adjacent: a row out of place draws its heading a second time.
- spacr.qt.app.folded_children() Dict[str, Tuple[str, ...]][source]¶
host key -> the module keys folded onto its masthead.One mapping, read from the hosts themselves, so the dock, the menu bar and the fold strips use the same assignment. Each host declares its own
FOLDED_APPS; this function only collects those tuples.The dock and the spaCR menu both draw the nested structure from this, showing a folded module one level below the host it belongs to.
Never raises: a host whose module cannot be imported contributes nothing, because a navigation aid must not be able to stop the window being built.
- spacr.qt.app.home_bands(apps: List[Tuple[str, str, str, str]] | None = None) List[Tuple[str, List[Tuple[str, str, str, str]]]][source]¶
(band, rows)for the Home tab, inSECTIONSorder.The same grouping the tabs use — every app once, under what it is about. Home is the “all of it” view and each later tab is a filter of it, which is only true while both read this one table.
Bands with no apps are dropped rather than drawn empty. None are, today; the guard is what stops a heading appearing over nothing the day a section’s last app is retired.
- spacr.qt.app.home_categories(apps: List[Tuple[str, str, str, str]] | None = None) List[Tuple[str, List[str]]][source]¶
(section, [app key])for every tab after Home, in tab order.Computed rather than written down, so a section cannot acquire a tab it has no apps for or lose one it does.
- spacr.qt.app.home_stages() dict[source]¶
app key → stage, for every app in
APPS.What
make_home_page()hands the Home page, so the tiles and the legend colour from the same table this module owns and the widget still knows nothing about what a stage means.
- spacr.qt.app.install_the_dialog_filters(app) tuple[str, ...][source]¶
Install the registered dialog event filters on a Qt application.
Installers are idempotent and isolated: a failure in one filter does not prevent the remaining filters from being installed.
- Parameters:
app – the Qt application each filter installer is called with.
- Returns:
Names of the filters installed successfully.
- spacr.qt.app.install_the_spaceout_fractal(screen) bool[source]¶
Put the spaceout fractal behind
screen, if this is spaceout.- Parameters:
screen – the widget the fractal is parented to and sized to fill; an event filter on it keeps the fractal following it.
- Returns:
True when it was installed, so the caller knows to skip the ordinary ambient backdrop. False in every normal launch – which is what keeps the mode hidden – and false rather than raising when the fractal cannot be built, so a machine that cannot draw it still gets the animation it always had.
The settings come from Preferences, which shows them only under spaceout, so one place decides both what is offered and what is drawn.
- spacr.qt.app.launch(argv: list[str] | None = None) int[source]¶
Bootstrap QApplication and show the main window.
- spacr.qt.app.make_home_page(parent=None)[source]¶
Build the Home page exactly as the running app builds it.
The grouping, the stages, the notes and the icon provider are four arguments that have to agree, and a test that assembles its own HomePage is testing a page that does not ship — which is the exact class of bug #16i was: Home grouped by one table and the tabs by another. One constructor call, used by
MainWindowand by the suite.
- spacr.qt.app.register_app(key: str, name: str, desc: str, section: str, *, factory=None, stage: str | None = None, title: str | None = None, intro: str | None = None, cli_note: str | None = None, api_module: str | None = None, entry: str | None = None, defaults_module: str | None = None, translations: Tuple[str, ...] | None = None) Tuple[str, str, str, str][source]¶
Add one app to the registry. The seam a new module registers through.
Call it at import time from the module that owns the app:
from spacr.qt.app import register_app, SECTION_EXPLORE, STAGE_ALPHA register_app("graph_builder", "Graph Builder", "Drag columns onto x / y / colour / facet", SECTION_EXPLORE, factory=make_screen, stage=STAGE_ALPHA)
The first four arguments put a tile on Home and a row in the sidebar. The keyword arguments after
stageare what make that tile a working app: they are the app’s strings, given ONCE here, and fanned out into the tables that used to need a hand-edit each — seeAPP_META.- Parameters:
key – stable app id. Load-bearing —
bridge,cli,validate, the drag-and-drop handlers,settings_modeland saved user state all key off it, so it is chosen once and never renamed. Must be unique across built-ins and plugins.name – display name; the sidebar row, tile and menu entry.
desc – one-line summary; the tooltip and status tip.
section – one of
SECTION_ORDER. A section with no apps has no tab, so registering the first app into a new section is what makes that section appear.factory – optional zero-argument callable returning the app’s
QWidgetscreen. It may declareapp_keyand/orhostkeyword parameters —hostis theMainWindow, for a screen that has signals to connect — and is given whichever of the two it accepts. Omit it and the app gets the generic settings-drivenAppScreen, like every pipeline module.stage – optional
STAGESmember. If omitted, no explicit stage entry is written. Missing entries read as stable untilspacr.qt.maturity.apply()assigns alpha to unassessed apps without a declaration. Already affirmed stable apps remain stable. Omitting or deleting an entry is not a stability assessment.title – header shown at the top of the app’s own screen. Defaults to
name; give it only when the screen wants the longer form (“Illumination Correction” over a tile that reads “Illumination”). Reachesapp_screen.APP_TITLES.intro – the paragraph beside that header — what the module does, in a sentence or two. Defaults to
desc. Reachesapp_screen.APP_INTROS.cli_note – for an app with NO headless path: one sentence saying so and what to do instead. Reaches
cli.INTERACTIVE_ONLY, which is whatspacr-run <key>prints instead of “unknown module”. Mutually exclusive withentryin spirit — an app is one or the other.api_module – dotted-or-slashed module path under the generated API docs (“qt/layer_viewer”), for the ⓘ link beside the settings. Reaches
settings_model._APP_API_MODULE.entry –
"module:function"of the callable the Run button runs. Resolved lazily byregistered_entry()and consulted byspacr.qt.bridge.resolve_pipeline_entry(); without it the Run button answers “Not runnable”.defaults_module – the module whose import calls
spacr.settings.register_defaults()for this key.settings_model.resolve_default_settingsimports it before asking whether the key has registered defaults — otherwise a module that registers its settings at import has no settings panel until something else happens to import it, and the app opens on an empty form. Imported on demand, so registering an app costs no numpy/pandas/torch at startup.translations – the display
namein the nine non-English UI languages, inspacr.qt.i18n.LANGUAGESorder. Reachesi18n._ROWSand its catalogs.
- Returns:
the row that was appended, so a caller can keep it.
- Raises:
ValueError – on a duplicate key, an unknown section, an unknown stage or an empty name/description.
TypeError – if
factoryis not callable.
- spacr.qt.app.registered_entry(key: str)[source]¶
Import and return the pipeline callable app
keyregistered.Nonewhen the app registered noentry=, which is what an interactive-only app (its own screen, no Run button) does.spacr.qt.bridge.resolve_pipeline_entry()consults this after its own built-in chain, soregister_app(..., entry="mod:func")is all a new pipeline app needs for its Run button to run something. Imported here, on demand, rather than at registration: registering an app must not drag numpy, torch or pandas into a process that only wanted to draw a sidebar.- Parameters:
key – the app key whose
entrymetadata ('module:function') is imported.
- spacr.qt.app.registered_factory(key: str)[source]¶
The factory registered for
key, orNone.A declared app is registered with a
LazyScreenFactorystanding in for the real callable, and this is where the stand-in ends: asking for the factory imports the screen and returns the module’s own function.Code downstream of this function therefore never sees the stand-in.
_call_screen_factory()decides whether to passapp_keyandhostby reading the factory’s signature, and a stand-in’s signature is its own, not the real one’s; a test that asserts the registered factoryisthe module’s function would likewise be comparing against the wrapper. Resolving here means neither ever sees one.The resolved callable replaces the stand-in in
APP_FACTORIES, so the import happens once even if the screen is opened and closed all afternoon. A stand-in whose module fails to import is left in place andNoneis returned: the app falls back to the generic settings screen rather than taking the window down.- Parameters:
key – the app key looked up in
APP_FACTORIES.
- spacr.qt.app.registered_metadata(field: str) dict[source]¶
{app key: value}for oneAPP_METAfield, empty ones dropped.The pull half of the seam. A side table calls this at the end of its own import to absorb every app that registered before it existed:
_app = sys.modules.get("spacr.qt.app") if _app is not None: for _key, _value in _app.registered_metadata("title").items(): APP_TITLES.setdefault(_key, _value)
setdefault, not assignment: a table’s own hand-written entry is the more specific one and wins.- Parameters:
field – the
APP_METAfield to collect, such as'title'.
- spacr.qt.app.section_members(section: str, apps: List[Tuple[str, str, str, str]] | None = None) List[Tuple[str, str, str, str]][source]¶
The
APPSrows a category’s tab shows, in registry order.Apps without a tile are not members. The result is what the tab draws and what its label counts, so a module with no tile is left out of both; otherwise the label would read “Results & QC (7)” over three tiles.
An explicit
appslist is filtered too. A caller passing its own rows is asking “which of these belong to this section”, and a tileless one does not belong to any tab whichever list it arrives in.- Parameters:
section – the category name, compared with the fourth element of each
APPSrow.
- spacr.qt.app.tile_sort_key(row: Tuple[str, str, str, str]) Tuple[int, int][source]¶
Sort key placing
rowwhereSECTION_TILE_ORDERsays.- Parameters:
row – an
APPSrow.- Returns:
(section index, position in that section). An unlisted key sorts after every listed one, keeping registry order among the unlisted by virtue of Python’s stable sort.
- spacr.qt.app.tiled_apps(apps: List[Tuple[str, str, str, str]] | None = None) List[Tuple[str, str, str, str]][source]¶
The
APPSrows that get a TILE, in registry order.APPSlists every registered app; this lists the apps Home draws a tile for. The two differ because modules fold into hosts – a folded module keeps its registry row (it still has a screen, an icon, a section and a key to navigate to) and loses only its tile, because it is now reached from a button on its host or from the Help menu.Anything asking “what does the user see on Home”, “how many tiles are in this band”, or “does this tile’s label fit” wants this. Anything asking “can this key be navigated to”, “does this app have artwork”, or “does every app have a screen” wants
APPS– a folded module must still answer yes to all three.An app registered with the alpha gate has no tile while Preferences > Show alpha features is off: Home does not draw it, so nothing that counts or lists Home’s tiles may count it either.
- spacr.qt.app.unregister_app(key: str) bool[source]¶
Remove app
keyfrom the registry.Trueif there was one.The counterpart to
register_app(), for a plugin that unloads and for tests that must not leak a registration into the next one — a stray row inAPPSis a stray tile, a stray sidebar entry and a stray Ctrl+N binding for every test that follows.- Parameters:
key – the app key to remove, converted to a string.
Nested helpers¶
- MainWindow.__init__._finish_installer_onboarding()¶
Show installer consent, optional cache review, report notice and tour.
Background cache inspection finishes before the choices open. The tour waits for the anonymised-report notice to close.
spacr/qt/app.py:3314
- MainWindow._on_cache_cleanup_preview_ready.apply()¶
Revalidate and apply only the explicit current-dialog selection.
- Returns:
Action outcome with a refusal reason or removed counts and any private retained paths.
spacr/qt/app.py:6050
- MainWindow._retry_screen_backdrop.again() None¶
Try the backdrop once more, if the screen is still alive.
spacr/qt/app.py:7909
- MainWindow._review_installation_caches.guard(paths)¶
Hold the application’s run lock and refuse registered active work.
- Parameters:
paths – Independently derived candidate cache directories.
- Yields:
Nonewhile application-local cleanup is allowed, or the existing cache refusal. This does not prove other processes idle.
spacr/qt/app.py:5943
- MainWindow._review_installation_caches.prepare()¶
Inspect the fresh operation’s caches inside the background worker.
- Returns:
Immutable preview with the live application-local run guard.
spacr/qt/app.py:5962
- MainWindow._show_about._line(html, size, *, muted=False, weight=400, gap=0)¶
One centred line of the About box, at the given size.
spacr/qt/app.py:4807
- MainWindow._start_a_sample_project.open_it(key)¶
Open the actual module, including its page inside a host screen.
spacr/qt/app.py:7062
- _collect_paint_diagnostics.failed(what)¶
Log the exception being handled and name it in
errors.spacr/qt/app.py:2639
- _declared_folds._as_string(node, _depth=0)¶
The string an AST node stands for, or
Noneif it is not one.Resolves a literal, a module constant, and another module’s constant read the same way rather than by importing it.
_depthbounds that indirection so a constant defined in terms of itself stops instead of recursing.spacr/qt/app.py:1854
- _import_the_data_libraries_off_the_gui_thread._work() None¶
Import each library, logging rather than raising a failure.
spacr/qt/app.py:598
- _start_icon_prewarm.warm()¶
Fill the icon caches, and never let a bad file reach the GUI.
spacr/qt/app.py:8710
- _start_settings_prewarm._warm_the_settings_imports()¶
Import each module, logging rather than raising a failure.
spacr/qt/app.py:8542
- _start_settings_prewarm.warm()¶
Import the remaining settings dependencies without building widgets.
spacr/qt/app.py:8536
- launch._drain_ai()¶
Stop every job runner before Qt starts destroying widgets.
Connected to
aboutToQuit, which fires however the application exits. See the comment below for why this covers every runner and not only the consoles.spacr/qt/app.py:8973