spacr.qt.settings_diff

Settings-diff viewer — spot what changed between two runs.

Given two settings dicts (or two run-folder paths, or two CSVs), show a color-coded diff so users can immediately see which knobs moved between “the run that worked” and “the run that didn’t”.

Public API:

from spacr.qt.settings_diff import (diff_settings, diff_settings_grouped,
                                    SettingsDiffDialog)

changes = diff_settings(a, b)      # → list of (key, a_val, b_val, kind)
grouped = diff_settings_grouped(a, b)   # the same, by settings category
SettingsDiffDialog(a, b, parent).exec()

Diff kinds:

  • "added" — key present in B but not A

  • "removed" — key present in A but not B

  • "changed" — key in both, value differs

  • "same" — a key both runs set to the same value. Never returned by diff_settings(); diff_settings_grouped() carries it only when asked, because the default view of a 200-key settings dict has to be the handful that moved.

Grouping uses the same spacr.settings.categories map both GUIs group their settings panels by, so “what changed” is read under the same headings the user set the values under. A key nobody categorised lands in UNCATEGORISED rather than being dropped — an unclassified knob is still a knob that moved.

Classes

CategoryDiff

Every row from one settings category, differences first.

DiffRow

One diff entry.

SettingsDiff

The whole settings comparison, grouped by category.

SettingsDiffDialog

Deferred: real Qt dialog is built on demand so this module can

Functions

diff_settings(→ List[DiffRow])

Return the list of keys that differ between a and b.

diff_settings_grouped(→ SettingsDiff)

Diff two settings dicts and group the result by settings category.

setting_category(→ str)

Return the settings heading key is grouped under.

Module Contents

class spacr.qt.settings_diff.CategoryDiff[source]

Every row from one settings category, differences first.

Parameters:
  • category – the heading, e.g. "Cellpose".

  • rows – the rows that differ, sorted by key.

  • same – the rows both runs agree on. Empty unless the caller asked for them; n_same counts them either way.

  • n_same – how many keys in this category matched, whether or not same was populated. It is what lets the collapsed view say “Cellpose: 2 changed, 14 unchanged” without carrying 14 rows.

__len__() → int[source]

Number of differing rows.

property n_added: int[source]

Keys only the second run set.

property n_changed: int[source]

Rows whose value moved.

property n_removed: int[source]

Keys only the first run set.

class spacr.qt.settings_diff.DiffRow[source]

One diff entry.

Parameters:
  • key – the settings key.

  • a_val – its value in run A, or None when the key is absent there.

  • b_val – its value in run B, or None when the key is absent there.

  • kind – "added", "removed", "changed" or "same", as defined in the module description.

property category: str[source]

The settings heading this key is grouped under.

class spacr.qt.settings_diff.SettingsDiff[source]

The whole settings comparison, grouped by category.

Parameters:
  • categories – one CategoryDiff per heading that has something to show, in spacr.settings.categories order.

  • include_same – whether unchanged keys were carried.

  • n_same – how many keys both runs set to the same value, over the whole comparison. A stored count and not a sum over categories, because the default view omits the categories in which nothing differs — and “17 settings matched” is still true, and still worth saying, when none of those 17 has a row.

__len__() → int[source]

Number of differing rows.

category(name: str) → CategoryDiff | None[source]

Return one category’s block, or None if it has nothing.

Parameters:

name – category heading to look up, compared exactly with CategoryDiff.category.

summary() → str[source]

One sentence: how much moved, and where.

property identical: bool[source]

True when nothing differs at all.

property n_added: int[source]

Keys only the second run set.

property n_changed: int[source]

Keys both runs set, to different values.

property n_removed: int[source]

Keys only the first run set.

property rows: Tuple[DiffRow, ...][source]

Every differing row, category order then key order.

class spacr.qt.settings_diff.SettingsDiffDialog[source]

Deferred: real Qt dialog is built on demand so this module can be imported (and diff_settings called) without needing PySide6.

Build and return the settings-diff dialog.

Qt is imported inside the call so the module can be used headlessly – diff_settings() is the part a report needs, and it must not drag a GUI toolkit in with it.

Parameters:
  • a – the settings on the left.

  • b – the settings on the right.

  • parent – parent widget, or None.

  • a_label – caption for the left side.

  • b_label – caption for the right side.

Returns:

the dialog, ready to exec.

__new__(a, b, parent=None, a_label='A', b_label='B')[source]

Build and return the settings-diff dialog.

Qt is imported inside the call so the module can be used headlessly – diff_settings() is the part a report needs, and it must not drag a GUI toolkit in with it.

Parameters:
  • a – the settings on the left.

  • b – the settings on the right.

  • parent – parent widget, or None.

  • a_label – caption for the left side.

  • b_label – caption for the right side.

Returns:

the dialog, ready to exec.

spacr.qt.settings_diff.diff_settings(a: Dict[str, Any], b: Dict[str, Any]) → List[DiffRow][source]

Return the list of keys that differ between a and b.

Sorted alphabetically by key. same-valued keys are omitted.

Parameters:
  • a – baseline settings dict.

  • b – comparison settings dict.

Returns:

list of DiffRow.

spacr.qt.settings_diff.diff_settings_grouped(a: Dict[str, Any], b: Dict[str, Any], *, include_same: bool = False) → SettingsDiff[source]

Diff two settings dicts and group the result by settings category.

A spaCR run carries around two hundred keys, so an ungrouped diff of two runs that differ in one Cellpose knob and one plate-map column reads as an undifferentiated list. Grouping under the same headings the settings panel uses makes it answerable at a glance: the change was in Cellpose.

Parameters:
  • a – baseline settings dict.

  • b – comparison settings dict.

  • include_same – also carry the keys both runs agree on, for the “show everything” toggle. Off by default — the point of the default view is that an unchanged setting is not in it.

Returns:

a SettingsDiff. Categories with nothing to show are absent; with include_same that means categories neither run mentions at all.

spacr.qt.settings_diff.setting_category(key: str) → str[source]

Return the settings heading key is grouped under.

Parameters:

key – a settings key, e.g. "cell_diameter".

Returns:

the category name, or UNCATEGORISED when the key is in no bucket (a plugin’s key, a run-journal bookkeeping column, or a knob nobody has filed yet).