spacr.qt.screens.settings_model

Bridge between spacr’s plain-python default settings and Qt form widgets.

The existing spacr GUI expresses settings as {name: (widget_type, options, default)} triples via spacr.gui_utils.convert_settings_dict_for_gui. Here we consume the same conversion output and materialize each entry as a real Qt widget grouped into logical Section boxes based on spacr.settings.categories.

Classes

SettingsSection

Represent one settings-panel heading and its nested content.

SettingsWidgets

Container for the Qt widgets bound to a settings dict.

Functions

api_docs_url(→ str)

Return the spaCR API URL for an app or shared setting.

attach_api_tooltip(→ str)

Attach typed, linked API help metadata to one setting widget.

categories_for_app(→ Dict[str, List[str]])

Return category keys after applying module-specific relocations.

category_tooltip(→ str)

Return the plain-language blurb for one settings category.

category_tooltip_is_curated(→ bool)

True when a category has a written blurb rather than the fallback.

essential_keys(→ List[str])

The settings a first-time user of app_key should meet first.

explainer_width(→ int)

Return the minimum explainer width in monospace characters.

format_tooltip(→ str)

Return localized typed HTML with an unchanged API-document URL.

formula_for(→ str)

The formula actually fitted, for one model term and the plate settings.

get_categories(→ Dict[str, List[str]])

Return the {category_name: [setting keys]} mapping.

get_tooltips(→ Dict[str, str])

Return per-key tooltip text (spacr.settings.descriptions and .tooltips).

has_csv_column_picker(→ bool)

True when this module gives key a CSV picker of its own.

has_curated_layout(→ bool)

Return True when app_key's settings panel has a layout of its own.

has_section_explainer(→ bool)

Return whether a settings section begins with explanatory prose.

install_api_tooltips(→ None)

Give every mapped/generated popup setting label consistent API help.

keys_hidden_by_their_object(→ set)

Which of keys must not be on the form, because they do not apply.

language_resolved_once()

Cache language and translation lookups during one synchronous build.

list_shape_for(→ Optional[Tuple[bool, bool, Any, Any]])

Decide whether key is a list setting, and of what shape.

maths_for(→ List[str])

The statistical statement, as lines, for one model term.

mixed_cost_note(→ str)

What mixed costs, as one paragraph, built from the measurement.

model_api_link(→ Tuple[str, str])

(name, url) for one backend's API, or ("", "").

needs_curated_layout(→ bool)

Return True when app_key has enough settings to need grouping.

normalise_regression_level(→ str)

Return a supported regression level, defaulting to 'both'.

object_of_setting(→ Optional[str])

Which object a setting belongs to, or None for the great majority.

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

The keys that decide whether role is in the run.

organelle_morphology_now(→ Optional[str])

Resolve the morphology currently applicable to an organelle slot.

permutation_test_explainer(→ str)

Return localized plain-text permutation-test guidance.

permutation_test_explainer_html(→ str)

Render localized permutation-test guidance as HTML.

plain_tooltip(→ str)

Same content as format_tooltip but plain text — used by the

refresh_api_tooltips(→ None)

Refresh semantic setting help beneath root in language.

regression_design_scan(→ dict)

How big the fit is about to be, read off its count input tables.

regression_model_explainer(→ str)

Describe the regression formula selected in the settings panel.

regression_model_explainer_html(→ str)

Render localized model or inference guidance as HTML.

resolve_default_settings(→ Dict[str, Any])

Return a fresh defaults dict for an app key, mirroring the Tk GUI

retarget_field_tooltips(→ int)

Move editor tooltips to the labels that identify their settings.

section_explainer(→ str)

Return localized plain-text guidance for a settings section.

section_explainer_html(→ str)

Return localized HTML guidance for a settings section.

section_shows_anything(→ bool)

Report whether a settings section contains visible content.

section_tooltip(→ str)

Return the blurb for one heading of the settings TREE.

section_tooltip_is_curated(→ bool)

True when a tree heading has written help rather than the fallback.

timelapse_and_motility_keys(→ set)

Every setting key owned by the Timelapse / Motility Assay modules.

Module Contents

class spacr.qt.screens.settings_model.SettingsSection[source]

Bases: tuple

Represent one settings-panel heading and its nested content.

The class remains a (title, rows) tuple for compatibility with callers that unpack section pairs or pass them to dict. rows contains all controls in the subtree, allowing clients without nested-section support to render every control exactly once.

The hierarchy is exposed through own_rows, children, and path. The path contains section titles from the root to the current node, for example ("Advanced settings", "Object filtration", "Cell"), so sections with identical titles remain distinguishable.

Initialize self. See help(type(self)) for accurate signature.

__new__(title, own_rows=(), children=())[source]

Build a section, flattening its children’s rows into its own.

The tuple half is (title, rows) where rows is this section’s own rows followed by every descendant’s, so a consumer that only knows the tuple still sees the whole subtree.

Parameters:
  • title – the section’s caption.

  • own_rows – rows belonging to this section itself.

  • children – nested sections; each is re-parented onto this section’s path.

Returns:

the new section.

walk()[source]

This section and every section below it, outermost first.

property rows: List[Tuple[str, PySide6.QtWidgets.QWidget]][source]

Every row in this heading and in everything nested under it.

class spacr.qt.screens.settings_model.SettingsWidgets(app_key: str, parent: PySide6.QtWidgets.QWidget | None = None, *, skip_keys=(), current=None)[source]

Container for the Qt widgets bound to a settings dict.

Instantiate with an app_key; call build_sections() to get a list of (section_title, list_of_(label, widget)) tuples to feed into the Section widgets on a screen. collect() returns the current settings dict after user edits.

Load the app’s defaults and prepare its empty widget map.

Parameters:
  • app_key – id of the app whose settings are being edited.

  • parent – optional Qt parent for created widgets.

  • skip_keys –

    settings to build NO widget for.

    For a FOLD, which mounts one module’s extra settings onto another’s panel. The timelapse fold on the mask screen built all 364 of timelapse’s settings – 1,552 widgets, 1,148 ms – and kept the 14 that mask does not already have, discarding the rest because the host already owns them. Naming them here skips them instead, which is the same result for 4% of the work.

  • current – optional mapping from the form being rebuilt. Recognized settings replace the app defaults; its organelle count, slot, and object-channel values determine which controls the replacement form builds.

apply_organelle_presets_from_mapping(settings: Dict[str, Any]) → None[source]

Apply sparse imported presets without replacing explicit values.

A settings file that supplies morphology/method/thresholds owns those values. A file that supplies only a type asks the picker to populate its missing recommendations just as a direct user choice does.

Parameters:

settings – imported settings mapping; its keys decide which organelle slots are affected, and a slot’s recommended values are applied only to keys it does not supply with a non-None value.

build_sections() → List[SettingsSection][source]

Build the section tree with the UI language resolved once.

The scope is the whole reason this wrapper exists; see language_resolved_once(). Every tooltip, type hint, label and documentation URL below asks what language the interface is in, and without the scope each of those asks reads QSettings again.

Returns:

what _build_sections() returns, unchanged.

collect() → Dict[str, Any][source]

Read all widgets and return the current settings dict.

A control still waiting for its category to be opened is read without being built; see _read_value().

essential_keys() → List[str][source]

The rendered subset of essential_keys() for this module.

Filtered to keys that actually produced a widget, so a key named in a layout but skipped by convert_settings_dict_for_gui cannot make the disclosure control promise a row that is not there.

On Mask and Timelapse, each object’s segmentation settings are added for every object whose channel names a plane, so setting a pathogen channel brings the Pathogen Segmentation rows into Essentials as well as into All settings. See _essentials_that_follow_their_object().

The module’s layout part is computed once per set of settings: it rebuilds the module’s whole category layout, 16 ms on Regression, and the search strip asks on every pass of the object rule.

grow_to_fit_the_organelle_count(count) → int[source]

Build the organelle slots a raised count now asks for.

Parameters:

count – the new number_of_organelles.

Returns:

how many slots the panel holds afterwards.

THE PANEL OPENS WITH WHAT THE RUN HAS. Building every nameable slot up front and hiding the surplus is what made the Mask screen 1,551 widgets; a control that was never built cannot be revealed, so the panel has to be able to grow instead.

ONE CONTROL, DELIBERATELY CHANGED. This is safe to do here and was not safe to do per keystroke: the count is a single spinbox somebody sets on purpose, where a channel is a field they type digits into. Growing never shrinks – a slot built once keeps whatever the user has since put in it, and a count lowered and raised again finds its values where it left them.

hide_the_rows_the_grid_speaks_for(keys) → None[source]

Take keys off the form because a grid now shows them.

The widgets STAY – they are what collect() reads and what the grid writes through to – so this hides rows rather than dropping them. The settings search still indexes them and every check that walks the form still finds them holding their values.

Parameters:

keys – the setting keys the grid answers.

hide_the_rows_the_mode_leaves_out(keys) → None[source]

Take keys off the form because the module’s mode does not read them.

Plaque Assay’s Plaque and Figure modes read different settings. The widgets stay, holding their values, exactly as for hide_the_rows_the_grid_speaks_for(); only the rows go.

Parameters:

keys – the setting keys the current mode does not read.

keys_hidden_by_the_run() → List[str][source]

Return settings hidden by the latest object-visibility pass.

Returns:

Hidden keys in no guaranteed order. The result is empty before visibility is evaluated or when the model has no rows.

keys_matching(query: str) → List[str][source]

Setting keys matching every whitespace-separated term in query.

Terms are ANDed and matched as substrings, which is what makes “cell diameter” narrow rather than widen — the alternative, OR, turns a second word into a way of getting more results, which is the opposite of what typing more means.

An empty or whitespace-only query matches everything, so the caller can wire this straight to textChanged without special-casing the moment the box is cleared.

Parameters:

query – raw text from the search box.

Returns:

matching keys, in the order the widgets were built.

keys_whose_object_the_run_lacks() → set[source]

Return setting keys excluded by the current object configuration.

The screen calls this before constructing captions and tooltips so settings for unavailable object types remain unbuilt. It uses the same visibility rule as refresh_object_visibility().

Returns:

Hidden setting keys, or an empty set if visibility cannot be determined.

modified_keys() → List[str][source]

Setting keys whose widget no longer holds the module’s default.

Compared with the same normaliser the run journal and the settings diff use, so “differs from default” means one thing across the app. Without that, a value round-tripped through CSV — channels read back as the string "[0, 1, 2]" — reads as an edit here and as unchanged there.

Returns:

keys in the order the widgets were built.

organelle_keys_to_spawn(count) → List[str][source]

The settings spawn_organelle_slots() would build, unbuilt.

Asked first so the screen can check it has a heading for every one of them before anything is built.

Parameters:

count – the new number_of_organelles.

Returns:

the keys of the slots past those already built, in the order the defaults declare them; empty when there are none.

plain_tooltip_for(key: str) → str[source]

Return the plain-text hint (description + docs URL) for a setting.

Parameters:

key – setting key whose description is looked up; an unknown key gets the generic fallback text.

refresh_object_visibility() → None[source]

Show only the rows whose object this run actually has.

Idempotent, and it decides EVERY gated row every time rather than toggling the ones that changed – so a row put back on screen by something else answering a different question (the settings search releasing its filter shows every row it indexed) is hidden again on the next call instead of drifting.

Public because the screen has to be able to ask for it: it is the screen that lays the rows out, and the screen that hands row visibility back after a filter.

EACH ROW IS SET ONCE. The rows the screen’s filters hide anyway – everything under the Essentials view that is not essential, the rows of a switched-off dimension – are left hidden here instead of being shown and then hidden again by the filter that runs next (rows_the_screen_hides). The rows end where they always ended; what goes is the round trip. Measured on Regression under Essentials, showing and re-hiding 145 rows was 68 ms of a 90 ms pass, run twice every time a category was built.

The pass ends by calling rows_are_filtered_by when the screen has set it. This pass shows every row its objects allow, so the settings search, which also decides rows, has to be applied after it; without that, a channel committed in Essentials put non-essential rows back on the form and left a newly relevant heading off it.

refresh_training_basis_enablement() → None[source]

Disable settings that the selected training basis does not use.

Controls remain present so their values are still collected and the pipeline does not substitute defaults for missing keys. Applicability is read from spacr.training_basis, which is shared with the pipeline.

remember_section_rows(section, keys, has_children: bool) → None[source]

Record the settings and nesting state associated with a section.

Parameters:
  • section – Section-heading widget.

  • keys – Settings declared directly in the section, in order.

  • has_children – Whether the section contains nested headings.

search_text_for(key: str) → str[source]

The lower-cased haystack one setting is matched against.

Three fields, in the order a reader would scan them: the key as the API spells it, the label as the form spells it, and the description as the tooltip explains it.

Parameters:

key – the setting key.

set_hidden_value(key: str, value: Any) → bool[source]

Update a known run setting whose widget is not on this form.

This includes dedicated controls outside the form and object rows omitted by the current shape.

Hidden does not mean absent: imported values live in _defaults and still reach collect().

A slot above the current count is accepted only when this app owns the count and the key is a declared setting; foreign-app keys remain rejected.

Parameters:
  • key – setting key; it must already be a run setting, or an organelle-slot key declared in expected_types when this form owns the organelle count, otherwise False is returned.

  • value – new value, coerced to the setting’s expected type before it is stored.

set_value_for_key(key: str, value: Any) → bool[source]

Write value into the widget bound to key (if present).

Used by the Live Preview’s “Propagate settings” toggle to push interactively-tuned values back into the main settings panel. Returns True if the key existed and was set.

Parameters:
  • key – setting key whose widget is updated; False is returned when no widget is bound to it.

  • value – new value, converted to what the widget takes (bool for a check box, int or float for a spin box, item data or text for a combo box, text for a line edit).

spawn_organelle_slots(count) → List[str][source]

Build the controls a raised number_of_organelles asks for.

A slot’s controls do not exist until the count says so, and rebuilding the whole screen to add them would discard what the user had typed. This builds ONLY the new slots’ controls, on the panel already on screen; every control that existed keeps its identity and whatever the user typed into it. The caller lays the new controls out.

EXISTING VALUES WIN. A slot above the count that a settings file carried is already in _defaults (see set_hidden_value()), and its control is built holding that value rather than the one the panel would invent.

Parameters:

count – the new number_of_organelles.

Returns:

the new settings keys with a control, in the order the panel declares them; empty when the count asks for nothing new.

tooltip_for(key: str) → str[source]

Return the HTML-formatted tooltip for a given setting key.

Parameters:

key – setting key whose description is looked up; an unknown key gets the generic fallback text.

spacr.qt.screens.settings_model.api_docs_url(app_key: str, key: str = '', language: str | None = None) → str[source]

Return the spaCR API URL for an app or shared setting.

Known app keys land on their module page. New or UI-only modules fall back to the generated API index rather than the documentation homepage. Shared batch-correction settings always land on their implementation, rather than whichever consumer app happens to display them.

Parameters:

app_key – application key; a plugin’s own docs_url wins, otherwise it selects the module page when key does not.

spacr.qt.screens.settings_model.attach_api_tooltip(widget: PySide6.QtWidgets.QWidget, app_key: str, key: str, description: str = '', _descriptions: Dict[str, str] | None = None) → str[source]

Attach typed, linked API help metadata to one setting widget.

Parameters:
  • widget – the setting widget that receives the tooltip and its settingsAppKey/settingKey/apiTooltip* properties.

  • app_key – application key of the module whose settings are shown; it selects the API documentation link.

  • key – setting key whose description, name and type hint are shown.

spacr.qt.screens.settings_model.categories_for_app(app_key: str, categories: Dict[str, List[str]]) → Dict[str, List[str]][source]

Return category keys after applying module-specific relocations.

Memoised per module against the shape of categories (each title and how many keys it holds) and the plugin registry’s answer, and returned as a fresh copy each time: a module’s first open asks this five or six times and each expansion walks every setting key spaCR has.

Map Barcodes previously showed an Advanced tab containing only n_jobs and a Model Training tab containing only test. Both controls belong to the sequencing run, but changing the global category table would also move training controls in unrelated modules.

Parameters:
  • app_key – application key of the module whose settings are shown; a plugin’s own categories replace categories entirely, and some built-in modules relocate keys.

  • categories – category title to ordered setting keys; it is copied, not modified.

spacr.qt.screens.settings_model.category_tooltip(app_key: str, title: str, language: str | None = None) → str[source]

Return the plain-language blurb for one settings category.

Resolution order: the module’s own override, then the shared table, then a generic sentence built from the title. The generic one is a visible fallback rather than an empty string so a brand-new category is never silently blank — tests/qt/test_category_tooltips.py fails on it.

Parameters:
  • app_key – module the category is being rendered for.

  • title – category title as shown on the header (any case).

  • language – optional language override; defaults to the UI language.

spacr.qt.screens.settings_model.category_tooltip_is_curated(app_key: str, title: str) → bool[source]

True when a category has a written blurb rather than the fallback.

Shares _category_blurb() with category_tooltip() rather than repeating the lookup: the two used to hold separate copies, so a lookup rule added to one would silently not apply to the other.

Parameters:
  • app_key – application key of the module whose settings are shown.

  • title – category (section) title looked up for a written blurb.

spacr.qt.screens.settings_model.essential_keys(app_key: str, categories: Dict[str, List[str]] | None = None) → List[str][source]

The settings a first-time user of app_key should meet first.

Progressive disclosure needs a defensible answer to “which of these 190 matter?”, and a hand-written list per module would rot the first time a layout changed. So it is derived: the first group of the module’s curated layout, which is always its inputs, plus whatever _APP_ESSENTIAL_EXTRAS adds for that module.

A module with no curated layout gets the first shared category, which is “Paths” — still the right answer, just a thinner one.

Classify’s first group is only the family switch, so its extras carry what training either family needs: the classes, the splits, the image model and its schedule, and the tabular algorithm and its main hyperparameters. The family switch greys whichever half does not apply.

Parameters:
  • app_key – the module’s app key.

  • categories – optional pre-computed categories_for_app() output, to save recomputing it.

Returns:

setting keys in display order, without duplicates.

spacr.qt.screens.settings_model.explainer_width() → int[source]

Return the minimum explainer width in monospace characters.

The width is derived from the longest unbreakable formula. Prose remains free to wrap to the available panel width. Computed once per UI language.

spacr.qt.screens.settings_model.format_tooltip(text: str, app_key: str, key: str = '', language: str | None = None) → str[source]

Return localized typed HTML with an unchanged API-document URL.

Parameters:
  • text – description of the setting; an empty value becomes a generic “Controls …” sentence.

  • app_key – application key of the module whose settings are shown; it selects the API documentation link.

spacr.qt.screens.settings_model.formula_for(term: str, *, plate_position: bool = False, random_row_column: bool = False) → str[source]

The formula actually fitted, for one model term and the plate settings.

The box must show the formula the run fits. Previously, regression_model_explainer took only (regression_type, level), so the three constants above were printed whatever the two plate settings said, and a user who turned plate position OFF still read + rowID + columnID.

That is the same class of failure as an axis that relabels itself without moving its dots: the display asserts something the code does not do, and nothing on screen says which to believe.

The three states produced by spacr.ml.prepare_formula() are no position terms when plate_position=False; fixed rowID and columnID effects when plate_position=True; and row/column variance components when random_row_column=True.

random_row_column implies the terms are present, so it wins over plate_position=False; that combination is refused upstream (_reconcile_random_row_column_effects) and this renders what the refusal would be about rather than inventing a fourth state.

Parameters:

term – the model part, e.g. "fraction:grna" or "gene_fraction:gene + (1 | gene/grna)".

spacr.qt.screens.settings_model.get_categories() → Dict[str, List[str]][source]

Return the {category_name: [setting keys]} mapping.

spacr.qt.screens.settings_model.get_tooltips() → Dict[str, str][source]

Return per-key tooltip text (spacr.settings.descriptions and .tooltips).

spacr.qt.screens.settings_model.has_csv_column_picker(app_key: str, key: str) → bool[source]

True when this module gives key a CSV picker of its own.

Read by the screen so it does not ALSO hang the measurements.db “SQL” button off the same field: two buttons that disagree about which file the column comes from is worse than the one wrong button this replaces.

Parameters:
  • app_key – application key looked up in CSV_COLUMN_SOURCES.

  • key – setting key checked against that module’s CSV column sources.

spacr.qt.screens.settings_model.has_curated_layout(app_key: str) → bool[source]

Return True when app_key’s settings panel has a layout of its own.

“Of its own” means somebody decided what this module’s groups are — a _APP_CATEGORY_SPECS entry, an inline regroup in categories_for_app(), or a plugin that shipped categories.

Falling back to the shared category map is not curated. That map is keyed by what a setting is (a path, a plot option, “Advanced”), not by what the module does with it, so a module that relies on it renders as however many buckets its keys happen to fall into — which for Cellpose Masks was thirteen knobs under one “Cellpose” heading.

Parameters:

app_key – the module’s app key.

spacr.qt.screens.settings_model.has_section_explainer(app_key: str, title: str) → bool[source]

Return whether a settings section begins with explanatory prose.

Parameters:
  • app_key – application key looked up in SECTION_EXPLAINERS.

  • title – settings section title checked against that module’s explainer sections.

spacr.qt.screens.settings_model.install_api_tooltips(owner: PySide6.QtWidgets.QWidget, app_key: str, widget_keys: Dict[PySide6.QtWidgets.QWidget, str] | None = None) → None[source]

Give every mapped/generated popup setting label consistent API help.

SettingsWidgets controls are discovered through their settingKey property. Hand-built Live/Crop/Search controls are supplied in widget_keys. Descriptive help belongs to the label, not the editable field, and the whole of it – description and API link both – is in the label’s hover text.

NOTHING IS DRAWN BESIDE THE LABEL. A teal link dot used to be, and three forms had already switched it off one at a time: 68 of them down the Mask live preview, twenty-six down the Annotate settings dialog, three in the figure dialog. A column of dots reads as texture rather than as one affordance per setting, and the API link was never in the dot alone – it is in the hover text, which is where it was being read from.

Parameters:
  • owner – the form widget whose children carrying a settingKey property get tooltips; it also owns the shared tooltip event filter.

  • app_key – application key of the module whose settings are shown; it selects the API documentation links.

spacr.qt.screens.settings_model.keys_hidden_by_their_object(keys, settings: Dict[str, Any]) → set[source]

Which of keys must not be on the form, because they do not apply.

Three reasons, in the order they are decided:

  • the slot is beyond number_of_organelles – and that takes the slot’s channel with it, because a slot the run does not have is not a slot with its channel left showing;

  • the object’s channel (or its mask plane) names no plane, so the run does not have that object at all;

  • the slot’s type puts it in one morphology and the setting belongs to a different one – a punctate organelle has no ridge filter;

  • it is a legacy Cellpose 3 setting and no object is segmented by a Cellpose 3 model (_cellpose3_rows_to_hide()).

Parameters:
  • keys – every setting this panel has a control for. WHAT THE PANEL HOLDS IS WHAT DECIDES WHAT MAY BE HIDDEN: a role is gated only when its switch is on the panel too, and a slot is gated by the count only when the count is. Hiding a row whose switch lives on another screen would leave the user a control they cannot bring back – _rules_for_this_panel refuses to grey one for the same reason.

  • settings – the panel’s current values. Only the switches, the count and the slots’ type, diameter and morphology are read.

Returns:

the keys whose rows are to be hidden.

spacr.qt.screens.settings_model.language_resolved_once()[source]

Cache language and translation lookups during one synchronous build.

Nested scopes share the outermost cache. The cache is discarded when the outermost scope exits so subsequent builds observe language or catalog changes.

spacr.qt.screens.settings_model.list_shape_for(key: str, default: Any) → Tuple[bool, bool, Any, Any] | None[source]

Decide whether key is a list setting, and of what shape.

Deliberately conservative. A key qualifies only when its default is already a list or tuple, or is None and the declared type admits nothing but a list. That keeps three groups of keys on their old widgets:

  • src and file_metadata, declared (str, list) – they are normally one path / one substring, and src in particular has to stay a QLineEdit for drag-and-drop, the empty-state banner and the column picker’s _settings_src_path;

  • count_data / score_data, declared list but shipped with the placeholder string 'list of paths';

  • sample, whose declared “type” is the value None.

Parameters:
  • key – setting key; its declared type in spacr.settings.expected_types is consulted.

  • default – the setting’s default value; a list or tuple (or None for a list-only declared type) qualifies, and its elements decide the element type.

Returns:

(nested_capable, allow_none, element_type, container) when the key holds a list, or None when it should keep its ordinary widget.

spacr.qt.screens.settings_model.maths_for(kind: str, *, plate_position: bool = False, random_row_column: bool = False) → List[str][source]

The statistical statement, as lines, for one model term.

THE MATHS AND THE CODE MUST AGREE, and this is the half that keeps them agreeing: it takes the same two plate arguments formula_for() does and reads them the same way. If the code line says + rowID + columnID, ρ and γ are here; if the plate-position toggle turns them off, BOTH lose them; if they are random effects, both say random. A box whose two formulas disagree is worse than a box with one.

Parameters:

kind – 'grna', 'gene' or 'mixed'.

Returns:

the response line first, then the distribution line(s).

spacr.qt.screens.settings_model.mixed_cost_note(language: str | None = None) → str[source]

What mixed costs, as one paragraph, built from the measurement.

Parameters:

language – UI language code. None uses the active language.

Returns:

Exact localized guidance when its catalog record is current; otherwise the canonical English paragraph.

ONE SOURCE FOR TWO PLACES. The model box states it before the user chooses, and the run states it again before it blocks; two hand-written copies of a measurement are two numbers that drift apart, and the second one to be edited is the one nobody believes afterwards.

A MEASURED RANGE, NOT “THIS MAY BE SLOW” – the digits are what make it actionable, and “may be slow” is what the console said by saying nothing.

(name, url) for one backend’s API, or ("", "").

A spaCR backend is named by its MODULE and resolved against the published API documentation, so group_lasso and rra get the same kind of link statsmodels does rather than a module path a user has to go and find.

Parameters:
  • regression_type – Backend key shown in the regression selector.

  • language – UI language code appended to spaCR documentation links. Third-party links are returned unchanged.

Returns:

Link label and absolute documentation URL.

spacr.qt.screens.settings_model.needs_curated_layout(app_key: str) → bool[source]

Return True when app_key has enough settings to need grouping.

Interactive modules whose settings dict is the {"src": ...} placeholder render a bespoke screen, not the shared form; they have nothing to group. CURATION_THRESHOLD draws the line.

Parameters:

app_key – the module’s app key.

spacr.qt.screens.settings_model.normalise_regression_level(level: Any) → str[source]

Return a supported regression level, defaulting to 'both'.

Missing or unrecognized values can occur in settings saved by older versions and are handled without interrupting panel rendering.

Parameters:

level – saved level, compared case-insensitively after stripping; 'both', 'grna' and 'gene' are kept and anything else becomes 'both'.

spacr.qt.screens.settings_model.object_of_setting(key: str) → str | None[source]

Which object a setting belongs to, or None for the great majority.

Organelle slots are resolved by spacr.organelle_types, which owns the slot naming: the prefixes are lettered – organelle, organelleb, … – and organelle is a prefix of every other one, so the match has to be longest-first and belongs where the names are generated rather than being written out a second time here.

Both spellings of the other three are understood, cell_min_size and remove_background_cell, the way spacr.settings.advanced_object_of understands them: spaCR is not consistent about which end of a key the object name goes on, and a rule that knew only one end would leave half a family on screen.

Parameters:

key – setting key; an organelle-slot prefix, or a cell/nucleus/pathogen prefix or suffix, names its object.

remove_background_organelle_7 is slot 7’s switch: its last token is a number, not a slot prefix.

spacr.qt.screens.settings_model.object_switch_keys(role: str) → Tuple[str, ...][source]

The keys that decide whether role is in the run.

Parameters:

role – object name, such as 'cell' or an organelle slot; the keys are <role>_channel and <role>_mask_dim.

spacr.qt.screens.settings_model.organelle_morphology_now(role: str, settings: Dict[str, Any]) → str | None[source]

Resolve the morphology currently applicable to an organelle slot.

The collected <role>_morphology is authoritative. Selecting a type writes its recommendation into that control, while a later explicit advanced choice must win over the preset and over its display inference. Only a sparse mapping with no morphology falls back to resolving <role>_type and <role>_diameter directly.

Parameters:
  • role – Prefix for the organelle-slot settings, such as organelle or organelleb.

  • settings – Current values keyed by setting name.

Returns:

"spots", "network", "irregular", "ring", or None when neither resolution path supplies a supported morphology.

spacr.qt.screens.settings_model.permutation_test_explainer(language: str | None = None) → str[source]

Return localized plain-text permutation-test guidance.

Parameters:

language (str, optional) – UI language. None uses the active language.

Returns:

str – Wrapped guidance, using canonical English when a complete translation is unavailable.

spacr.qt.screens.settings_model.permutation_test_explainer_html(palette: Dict[str, Any] | None = None, language: str | None = None) → str[source]

Render localized permutation-test guidance as HTML.

Parameters:
  • palette (dict, optional) – Resolved theme palette.

  • language (str, optional) – UI language. None uses the active language.

Returns:

str – Rich text with translated prose and unchanged formulas.

spacr.qt.screens.settings_model.plain_tooltip(text: str, app_key: str, key: str = '', language: str | None = None) → str[source]

Same content as format_tooltip but plain text — used by the hover-follows footer at the bottom of each AppScreen.

Parameters:
  • text – description of the setting; an empty value becomes a generic “Controls …” sentence.

  • app_key – application key of the module whose settings are shown; it selects the API documentation link.

spacr.qt.screens.settings_model.refresh_api_tooltips(root: PySide6.QtWidgets.QWidget, language: str | None = None) → None[source]

Refresh semantic setting help beneath root in language.

Canonical English prose is retained in apiTooltipDescriptionSource; only the presentation HTML/plain accessibility chrome is regenerated. A label showing why its setting is greyed keeps that reason after the regenerated help (_LABEL_NOTE_PROPERTY); before, the language pass dropped it, so a greyed row’s name never said why. Field widgets marked metadata stay quiet because their visible label owns hover help. API-dot destinations carry the selected documentation language while retaining the same module page.

Parameters:

root – widget whose own and descendant setting widgets (those with settingsAppKey and settingKey properties) are refreshed; None does nothing.

spacr.qt.screens.settings_model.regression_design_scan(settings) → dict[source]

How big the fit is about to be, read off its count input tables.

The design: “The useful line names the design – ‘fitting 389 genes and 823 guide random effects over 610 wells’ – because that is also the line that tells a user their filters did something unexpected.”

WHAT THIS IS AND IS NOT. It reads the sgRNA count inputs and nothing else, so it is the design AS THE INPUT FILES HOLD IT: before the merge with the score data, before fraction_threshold and before the well filters. That is deliberate – it is the number to compare the run’s own post-cleaning counts against, and comparing them is how a filter that did something unexpected becomes visible. Every caller says which it is.

NEVER RAISES. It runs to put a sentence in the console beside a fit that is already starting; a scan that threw would take the run’s own message with it. What it could not work out comes back as None with a note saying why.

Parameters:

settings – regression settings; count paths and optional count_table names come from paired_data, or paths from legacy count_data when there are no paired counts.

Returns:

{'genes', 'guides', 'wells', 'rows', 'files', 'note'}.

spacr.qt.screens.settings_model.regression_model_explainer(regression_type: Any, level: Any = 'both', plate_position: Any = False, random_row_column: Any = False, language: str | None = None, inference: Any = 'auto', analysis_mode: Any = '') → str[source]

Describe the regression formula selected in the settings panel.

Parameters:
  • regression_type (Any) – Requested regression backend, such as "ols" or "mixed".

  • level (Any, default="both") – Coefficient level to describe: "grna", "gene", or "both".

  • plate_position (Any, default=False) – Whether the formula includes row and column position terms.

  • random_row_column (Any, default=False) – Whether row and column terms are variance components instead of fixed effects.

  • language (str or None, default=None) – UI language code. None uses the active language. Only exact, source-current paragraph translations are used.

  • inference (Any, default='auto') – Selected inference mode.

  • analysis_mode (Any, default='') – Compatibility value used to identify permutation inference.

Returns:

str – Plain text containing the selected model, fitted formula, output, and interpretation notes. Unknown backends receive an explicit warning.

Notes

Guide and gene effects are described as separate fits. The retired design, y ~ fraction:grna + gene_fraction:gene + rowID + columnID, contains both guide fractions and their gene-level sums. It is rank deficient, so its individual coefficients are not uniquely interpretable. COLLINEAR_FORMULA stores the formula used by the compatibility checks.

spacr.qt.screens.settings_model.regression_model_explainer_html(regression_type: Any, level: Any = 'both', plate_position: Any = False, random_row_column: Any = False, palette: Dict[str, Any] | None = None, language: str | None = None, inference: Any = 'auto', analysis_mode: Any = '') → str[source]

Render localized model or inference guidance as HTML.

Parameters:
  • regression_type (Any) – Selected regression backend.

  • level (Any, default='both') – Coefficient level: guide, gene, or both.

  • plate_position (Any, default=False) – Include fixed plate-position terms when true.

  • random_row_column (Any, default=False) – Use row and column variance components when true.

  • palette (dict, optional) – Resolved theme palette. The active semantic color names are used when omitted.

  • language (str, optional) – UI language. Missing or stale translations fall back by whole sentence.

  • inference (Any, default='auto') – Selected inference mode.

  • analysis_mode (Any, default='') – Compatibility value used to identify permutation inference.

Returns:

str – Rich text describing the run that the current settings will execute.

spacr.qt.screens.settings_model.resolve_default_settings(app_key: str) → Dict[str, Any][source]

Return a fresh defaults dict for an app key, mirroring the Tk GUI dispatch in gui_core.setup_settings_panel.

Parameters:

app_key – application key; a registered plugin’s defaults are used first, then the registered or built-in defaults for that key, and an unknown key gets a minimal {'src': ...} dict.

spacr.qt.screens.settings_model.retarget_field_tooltips(root: PySide6.QtWidgets.QWidget) → int[source]

Move editor tooltips to the labels that identify their settings.

Parameters:

root (QWidget) – Constructed screen or dialog to inspect recursively.

Returns:

int – Number of tooltips moved.

Notes

Tooltips stay on editors that have no sibling label, whose label already has different help, or that carry DISABLED_REASON_TOOLTIP.

spacr.qt.screens.settings_model.section_explainer(app_key: str, title: str, settings: Dict[str, Any] | None = None, language: str | None = None) → str[source]

Return localized plain-text guidance for a settings section.

Parameters:
  • app_key (str) – Application whose section is rendered.

  • title (str) – Section heading.

  • settings (dict, optional) – Current values used to render formulas and selected inference.

  • language (str, optional) – UI language. None uses the active language.

Returns:

str – Guidance text, or "" when the section has no explainer.

spacr.qt.screens.settings_model.section_explainer_html(app_key: str, title: str, settings: Dict[str, Any] | None = None, palette: Dict[str, Any] | None = None, language: str | None = None) → str[source]

Return localized HTML guidance for a settings section.

Parameters:
  • app_key (str) – Application whose settings section is rendered.

  • title (str) – Canonical English section title.

  • settings (dict, optional) – Current values used to render formulas and selected inference.

  • palette (dict, optional) – Resolved theme palette.

  • language (str, optional) – UI language. None uses the active language.

Returns:

str – Rich text, or "" when the section has no explainer.

spacr.qt.screens.settings_model.section_shows_anything(section) → bool[source]

Report whether a settings section contains visible content.

A section whose setting rows and nested sections are all hidden should not leave an empty heading in the panel. This predicate reports whether content remains after row-level visibility rules have been applied; it does not change widget visibility itself.

Parameters:

section – A spacr.qt.widgets.section.Section.

Returns:

False only when a section owns rows or nested sections and all of them are hidden. Sections without setting rows remain visible.

spacr.qt.screens.settings_model.section_tooltip(app_key: str, section, language: str | None = None) → str[source]

Return the blurb for one heading of the settings TREE.

A nested heading is resolved by its SettingsSection.path, not by its title: “Cell” under “Object filtration” and the top-level “Cell” segmentation category are the same word for two different groups, and a title-only lookup would give the first one the second one’s help.

Parameters:
  • app_key – module the section is being rendered for.

  • section – a SettingsSection, or any (title, rows) pair – an un-nested pair resolves exactly as before.

  • language – optional language override; defaults to the UI language.

spacr.qt.screens.settings_model.section_tooltip_is_curated(app_key: str, section) → bool[source]

True when a tree heading has written help rather than the fallback.

Parameters:
  • app_key – application key of the module whose settings are shown.

  • section – tree heading: an object with title and path attributes, a tuple whose first item is the title, or anything converted to a title with str().

spacr.qt.screens.settings_model.timelapse_and_motility_keys() → set[source]

Every setting key owned by the Timelapse / Motility Assay modules.

Derived from the category lists in spacr.settings so the two never drift apart. Used to strip those keys out of the Mask module’s editable settings — they still exist in the pipeline defaults (spacr.object reads timelapse on every run and motility_analysis inside the timelapse branch), the Mask GUI just no longer offers them.

Nested helpers

SettingsWidgets._hide_the_headings_of_slots_the_run_lacks.absent(role) → bool

Whether the run has no role at all.

spacr/qt/screens/settings_model.py:11816

SettingsWidgets._refresh_mask_gpu_enablement.refresh()

Refresh surviving controls without extending the panel lifetime.

spacr/qt/screens/settings_model.py:10996

SettingsWidgets._route_control.class_editor()

The class editor, told which frame it is previewing.

spacr/qt/screens/settings_model.py:10306

_CloudBrowserDialog.open_address.work() → None

Read the listing and the description, never raising.

spacr/qt/screens/settings_model.py:6890

_TrainingFolderEdit.__init__.browse()

Set a chosen directory; cancelling leaves the existing setting intact.

spacr/qt/screens/settings_model.py:6770

_sibling_label_for._named(widget) → QWidget | None

The real label inside a row, unwrapping a host if there is one.

Section.add_row wraps the caption in a SettingLabelWithInfo whenever the row is right-aligned against its field – the form’s normal shape – so the layout hands back a plain QWidget and a bare isinstance rejects it. Measured on Mask: 1,541 of 1,657 rows kept their help on the FIELD for this reason alone, and only 13 labels had it.

spacr/qt/screens/settings_model.py:12268

regression_model_explainer.tx(source: str, **values: object) → str

Translate one source string into the explainer’s language.

spacr/qt/screens/settings_model.py:5966

regression_model_explainer_html.tx(source: str, **values: object) → str

Translate one source string into the explainer’s language.

spacr/qt/screens/settings_model.py:5640