spacr.qt.help_index

Everything in spaCR that is addressable by name, and how to rank it.

The search field beside the Help menu is only as good as this: a user who knows what a thing is CALLED should reach it without knowing which module owns it or which collapsed heading it was filed under.

THE INDEX IS NEVER A HAND LIST. Every row here is derived from something that already decides the answer:

kind

where the rows come from

module

spacr.qt.app.APPS, the live app registry

setting

resolve_default_settings() and categories_for_app() per module, described by get_tooltips()

api

API_ENTRIES in spacr.qt.help_api_index, generated from the published API manifest

preference

PREFERENCE_ENTRIES, walked out of the source of the preferences dialog

A setting that cannot be found is then a fact about a generator rather than about whether somebody remembered to add a row, which is the whole argument of the instruction.

ADDING A KIND IS REGISTERING A PROVIDER, not editing a switch. A provider is () -> Iterable[HelpEntry]; register_provider() adds one and build_index() calls each of them behind its own guard, so a provider that raises costs its own rows and nothing else. What a result DOES when it is opened is registered the same way, in spacr.qt.help_search.

IMPORTING THIS COSTS NOTHING; BUILDING THE INDEX COSTS A SECOND. Every registry is imported inside the provider that reads it, so the module itself pulls in neither Qt nor spacr.settings. Building it measured 1.1 s and 13,000 rows on this tree, which is far too much for the GUI thread – spacr.qt.help_search runs build_index() on a worker and this module stays testable without a display.

Classes

HelpEntry

One addressable thing, and what it takes to get back to it.

Functions

api_entries(→ List[HelpEntry])

One entry per public API symbol, saying which settings it reads.

build_index(→ List[HelpEntry])

Run every provider and collect what they know.

module_entries(→ List[HelpEntry])

One entry per module in the live registry.

preference_entries(→ List[HelpEntry])

One entry per labelled row of the preferences dialog.

provider_names(→ Tuple[str, ...])

The registered providers, in registration order.

register_provider(→ None)

Add or replace a source of entries.

rendered_description(→ str)

An entry's description in the reader's language.

rendered_subtitle(→ str)

An entry's subtitle in the reader's language.

score(→ Optional[float])

The entry's score for a whole query, or None when a term misses.

search(→ List[HelpEntry])

The best matches for query, best first.

setting_entries(→ List[HelpEntry])

One entry per module a setting appears in, plus its API consumers.

Module Contents

class spacr.qt.help_index.HelpEntry[source]

One addressable thing, and what it takes to get back to it.

Variables:
  • kind – which provider produced it; also which opener knows what to do with it. One of KIND_ORDER, or a kind a caller registered.

  • title – the identifier a user would type – a setting key, a module name, a dotted API symbol, a preference label.

  • subtitle – where it lives. “in Mask ▸ Cell Segmentation” for a setting, “Preferences ▸ Animation” for a preference.

  • description – what it does, in the language the user thinks in. It is matched on, which is what makes “touching” find merge_edge_pathogen_cells.

  • payload – what the opener needs and nothing more – an app key, a setting key, a dotted symbol, a tab object name.

  • subtitle_source – the English TEMPLATE subtitle was rendered from, when the words in it are this module’s own rather than a name taken from a registry. "" when there is nothing to translate.

  • subtitle_values – what to substitute into that template, as (name, value) pairs – a tuple, because the entry is frozen and hashable and a dict would be neither.

  • description_source – the same for description.

  • description_values – the same for description.

WHY A TEMPLATE AND NOT THE FINISHED STRING. subtitle and description are what the search MATCHES on, so they stay English: the query and the haystack have to be in one language, and the index is built once per session while the interface language can change after it. What the user READS is rendered from the template at the moment a row is drawn, by rendered_subtitle() and rendered_description() with spacr.qt.i18n.tr() passed in – so a language change is picked up by the next keystroke and this module still imports no Qt and no catalog. “API reference” is the subtitle of ten thousand of the rows; a finished English string here is a wholly English result list for every reader who does not work in English.

property haystack: str[source]

Title, subtitle and description, lowercased, for matching.

spacr.qt.help_index.api_entries() → List[HelpEntry][source]

One entry per public API symbol, saying which settings it reads.

The settings half is what makes an API row findable by the name of a setting: searching cell_diameter offers the setting’s own row in each module that exposes it AND the entry of each function that takes it, which is what a setting query returns.

Returns:

kind="api" entries carrying {"symbol": dotted name}, and "reads" when the symbol consumes settings.

spacr.qt.help_index.build_index(names: Sequence[str] | None = None) → List[HelpEntry][source]

Run every provider and collect what they know.

Each provider is guarded on its own: a search field that loses its settings because the API manifest could not be read is worse than one that quietly offers fewer kinds.

Parameters:

names – providers to run; all of them when None.

Returns:

every entry, in provider order.

spacr.qt.help_index.module_entries() → List[HelpEntry][source]

One entry per module in the live registry.

Returns:

kind="module" entries carrying {"app": key}.

spacr.qt.help_index.preference_entries() → List[HelpEntry][source]

One entry per labelled row of the preferences dialog.

Returns:

kind="preference" entries carrying {"tab", "label"}, where tab is the page’s object name.

spacr.qt.help_index.provider_names() → Tuple[str, ...][source]

The registered providers, in registration order.

spacr.qt.help_index.register_provider(name: str, provider: Provider) → None[source]

Add or replace a source of entries.

Parameters:
  • name – the provider’s name; registering the same name twice replaces the first, which is what lets a test swap a cheap provider in for an expensive one.

  • provider – () -> Iterable[HelpEntry].

spacr.qt.help_index.rendered_description(entry: HelpEntry, translate: Callable[..., str] | None = None) → str[source]

An entry’s description in the reader’s language.

An API summary inside it is a docstring and stays as it was written; what this translates is the sentence spaCR wraps around it.

Parameters:
Returns:

the line for the row’s tooltip.

spacr.qt.help_index.rendered_subtitle(entry: HelpEntry, translate: Callable[..., str] | None = None) → str[source]

An entry’s subtitle in the reader’s language.

Parameters:
Returns:

what the row should show under or beside the title.

spacr.qt.help_index.score(entry: HelpEntry, terms: Sequence[str]) → float | None[source]

The entry’s score for a whole query, or None when a term misses.

Terms are ANDed, the same rule the per-module settings search uses: typing a second word must narrow, because otherwise typing more is a way of getting more results, which is the opposite of what typing means.

Parameters:
  • entry – the candidate.

  • terms – lowercased query terms.

Returns:

the summed score plus the kind tiebreak, or None.

spacr.qt.help_index.search(index: Sequence[HelpEntry], query: str, limit: int = 40, per_kind: int | None = PER_KIND_LIMIT) → List[HelpEntry][source]

The best matches for query, best first.

Parameters:
  • index – what build_index() returned.

  • query – raw text from the search box.

  • limit – how many rows to return in total.

  • per_kind – how many rows of one kind may be returned; None lifts the cap, which is what a test asking “is it in there at all” wants.

Returns:

matching entries, highest score first; ties are broken by kind and then by title, so the same query always lists the same way.

spacr.qt.help_index.setting_entries() → List[HelpEntry][source]

One entry per module a setting appears in, plus its API consumers.

ONE ROW PER MODULE: a setting in four modules is four rows, each naming its module and the category it sits under, rather than one row that then asks which. The user already knows which module they meant; a row that asks is a second click for information the list could have shown.

THE API HALF OF A SETTING IS NOT EMITTED HERE. SETTING_CONSUMERS names every addressable function that reads a key, and api_entries() folds that into the API row for each of those functions – one row per symbol, saying which settings it reads. Emitting them from both providers listed spacr.io.preprocess_img_data once for itself and once for every setting it reads, which was eleven identical rows in one result list.

Returns:

kind="setting" entries carrying {"app", "key", "category"}.