spacr.qt.settings_search¶
Find a setting, and meet a module’s settings a few at a time.
A spaCR module can render a lot of settings. Mask alone renders 190 of them under thirteen collapsed headings, and across the shell there are 1,022. Until now the only way to reach one was to guess which heading somebody filed it under and open headings until it appeared — and the only thing a first-time user saw was those thirteen headings, with nothing to say which two of them they actually had to touch.
This module adds one strip above the settings form:
a search box that matches the setting’s key, its label and its description. The description is the only part written in the language a user thinks in, so “touching” finds
merge_edge_pathogen_cellsand “gpu” findsn_jobs— neither word appears in either name.a Modified filter that shows only what differs from the module’s defaults. It is the fastest possible answer to “what did I change?”, and it shares
spacr.qt.settings_diff._values_equal()with the diff dialog and the run journal so all three agree about what an edit is.an Essentials / All switch — the progressive disclosure. Essentials shows the module’s inputs plus the handful of decisions
spacr.qt.screens.settings_model.essential_keys()derives from its curated layout, expanded and ready; All restores every heading, collapsed as before. Essentials is the default on a module’s first visit and the choice is remembered per module thereafter, so a returning expert never meets the training wheels twice.
The three compose: with a query typed, Essentials narrows the search rather than fighting it, and the count line always says exactly what is being shown out of how many.
Installation is from outside the screen, deliberately:
from spacr.qt.settings_search import install_window_hooks
install_window_hooks(window)
spacr.qt.shortcuts calls that once from MainWindow.__init__. The
installer then follows the screen stack, so a module built later in the
session gets its strip when it is first shown rather than needing a line
inside the shared screen.
Classes¶
Search box, Modified filter, Essentials/All switch, and a count line. |
Functions¶
|
The remembered disclosure level for |
|
Forget one module's disclosure choice, or every module's. |
|
Put a search strip above |
|
Follow |
|
Persist the disclosure level chosen for |
Module Contents¶
- class spacr.qt.settings_search.SettingsSearchBar(screen: PySide6.QtWidgets.QWidget, parent: PySide6.QtWidgets.QWidget | None = None)[source]¶
Bases:
PySide6.QtWidgets.QWidgetSearch box, Modified filter, Essentials/All switch, and a count line.
Owns no settings state of its own — it reads the screen’s
SettingsWidgetsmodel and shows or hides rows that already exist. Hiding rather than rebuilding is what keeps a half-typed value alive across a filter change, which a rebuild would silently discard.- Parameters:
screen – the module screen to filter. The bar owns no settings state – it reads that screen’s
SettingsWidgetsmodel and shows or hides rows that already exist, which is what keeps a half-typed value alive across a filter change.parent – parent widget.
Build the settings search strip above a module’s form.
Fixed height, explicitly: the strip is two rows tall and the scroll area under it wants everything else, so without a policy the two share the pane by stretch factor and the search box lands 800 pixels high on the first layout.
The key-to-section index is built once from the rendered form, so filtering never has to guess which section a setting ended up in, and which sections the user had open is remembered – clearing the box puts the form back rather than leaving it splayed.
- Parameters:
screen – the module screen whose settings this filters.
parent – parent widget, or
None.
- add_trailing_widget(widget: PySide6.QtWidgets.QWidget) None[source]¶
Add
widgetto the right-hand end of the control row.The seam other modules use to put a settings-scoped control where the settings are, instead of inventing a second strip. Reparents
widgetonto the bar.- Parameters:
widget – any widget; it keeps its own size policy.
- apply(reopen: bool = True) None[source]¶
Recompute which rows and sections are shown.
Called on every change to the query, the Modified filter or the disclosure level — one path, so the three can never disagree about what should be on screen. The screen also calls it after each pass of the object rule, so a channel the user commits is judged by the same filter as every other row.
The per-object table has no form rows of its own to count. Its section is counted by the settings it answers for instead, so under Essentials the table stays on screen while it holds the channels, which the flat form no longer shows while the table is on.
A heading that holds only sub-headings has no rows of its own either, so its matches are rolled up out of the headings below it before any section is hidden — see
_counting_the_sub_headings().- Parameters:
reopen – while the filter narrows, open every section it keeps. The screen passes
Falsewhen it re-applies the filter after the object rule or after laying out rows: a section the user shut then stays shut, and only a section this call brings back onto the form is opened.
- count_text() str[source]¶
The sentence under the controls. Public so tests read what users read rather than recomputing it.
- keys_it_hides() set[source]¶
The indexed settings
apply()would hide right now.What the object rule asks before it sets rows (
SettingsWidgets.rows_the_screen_hides), so a row this strip is about to hide is not shown by the rule first.
- minimumSizeHint() PySide6.QtCore.QSize[source]¶
As narrow as the widest single control, or the Modified switch with its caption, or the box’s own minimum.
Not the whole row: when the strip is narrower than that, the controls move under the box (
_fit()). The switch keeps its caption beside it, so the two never wrap apart.
- resizeEvent(event) None[source]¶
Re-decide where the controls go for the new width.
- Parameters:
event – the resize event; its new width is read.
- reveal(key: str) bool[source]¶
Show one setting with every other category shut.
WHAT A HELP-SEARCH RESULT NEEDS, and it is deliberately NOT the search filter. Typing the key into the box above would hide every other setting as well, so a user who arrived from the Help search and then wanted to look at the neighbouring rows would first have to work out what had happened to the form. Revealing instead leaves the module whole and only decides which heading is open.
Nothing is rebuilt and no value is read or written: the row was already on the form, and this shows its section and scrolls to it – or, for a category not built yet (indexed with no field), the screen builds it first, as opening it would. That is what makes arriving here from a search safe for a half-typed value – the same property the filter has, for the same reason.
THE DISCLOSURE LEVEL IS CHANGED ONLY IF IT HAS TO BE, AND THE CHANGE IS NEVER REMEMBERED. Switching to All settings unconditionally would work, and it would also rewrite this module’s remembered Essentials/All choice every time anybody arrived here – a setting the user chose, changed as a side effect of looking something up. So the filter is cleared first and the level is raised only when the row is still not on the form afterwards, which is exactly the case where Essentials is what is hiding it; and the raise goes through
_show_all_without_remembering(), so the form shows the row while the store still holds the level the user picked. Most settings are not essentials, so a lookup that persisted the raise would move almost every module out of Essentials for good.- Parameters:
key – the setting to reveal.
- Returns:
True when the module renders
keyand it was revealed.
A nested heading is reachable only with every containing heading open, and logical ancestry also crosses bodies parked off the widget tree.
- section_of(key: str) PySide6.QtWidgets.QWidget | None[source]¶
The section widget holding
key’s row, orNone.- Parameters:
key – a setting key.
- Returns:
the collapsible section, or
Nonewhen this module does not render that setting.
- set_level(level: str) None[source]¶
Switch disclosure level and remember the choice.
- Parameters:
level – disclosure level:
'all'shows every setting; any other value selects'essentials'.
- set_modified_only(on: bool) None[source]¶
Turn the Modified filter on or off.
- Parameters:
on –
Trueto show only settings changed from their defaults; coerced withbool().
- set_query(text: str) None[source]¶
Type
textinto the search box, filtering as it goes.- Parameters:
text – search text for the box;
Noneor empty clears it.
- spacr.qt.settings_search.disclosure_for(app_key: str) str[source]¶
The remembered disclosure level for
app_key.Defaults to
ESSENTIALS, which is the whole point: a module is met a few settings at a time until its user says otherwise.- Parameters:
app_key – the module’s app key.
- spacr.qt.settings_search.forget_disclosure(app_key: str | None = None) None[source]¶
Forget one module’s disclosure choice, or every module’s.
- Parameters:
app_key – the module to forget, or
Nonefor all of them.
- spacr.qt.settings_search.install(screen: PySide6.QtWidgets.QWidget) SettingsSearchBar | None[source]¶
Put a search strip above
screen’s settings form.The form is a
QScrollAreasitting directly in the screen’s splitter. The strip goes outside the scroll area, in a container that takes its place: a search box that scrolls away with the results it is filtering is a search box you have to scroll back up to reach.Returns the strip, or
Nonewhen the screen has no settings form (a bespoke screen), the form failed to build, or one is already installed. Never raises — a missing search box must not cost anyone a module.- Parameters:
screen – an
AppScreen.
- spacr.qt.settings_search.install_window_hooks(window: PySide6.QtWidgets.QMainWindow) _StackWatcher | None[source]¶
Follow
window’s screen stack, adding the strip to each module.Called once from
spacr.qt.shortcuts.install(). Screens are built lazily on first navigation, so this cannot be a one-shot sweep; it connects to the stack and also installs into anything already built.- Parameters:
window – the main window; nothing is installed unless it has a
_stackscreen stack, and a watcher already on it is returned.- Returns:
the watcher, kept alive by the window, or
None.