spacr.qt.comparison_grid

B16 — N panels of the same field, panned and zoomed together.

Four channels of one field. The same well at four timepoints. The same field under four conditions. The comparison is only worth anything if the panels are looking at the same place at the same magnification, and doing that by hand — zoom each one, pan each one, hope — is how two panels end up half a cell out and a difference in framing is read as a difference in biology.

spacr.layers.CanvasLink is the model: N canvases sharing one world window, each keeping its own pixel size because they are different widgets. This module is the grid of widgets over it, plus the two things that only exist once there is more than one panel:

  • Selection reaches across. Clicking an object in one panel publishes its key through spacr.qt.linked_selection, so the same cell lights up in the other panels — and in the UMAP, the plate view and the annotation grid, which were already listening.

  • One panel can be let go. “Look closely at this one without losing the others’ place” is the ordinary next request, and it is a checkbox rather than a mode.

Classes

ComparisonGrid

N panels of the same field, locked together.

ComparisonPanel

One cell: a caption, a canvas and the checkbox that frees it.

Module Contents

class spacr.qt.comparison_grid.ComparisonGrid(panels: Any = None, parent=None, *, columns: int | None = None, titles: Dict[str, str] | None = None)[source]

Bases: spacr.qt.linked_selection.LinkedView, PySide6.QtWidgets.QWidget

N panels of the same field, locked together.

Parameters:
  • panels – [(key, stack), …] or {key: stack} — what each panel shows. Order is the order they are laid out in.

  • columns – how many panels per row; the default is the squarest grid that fits them.

  • titles – per-panel captions, defaulting to the keys.

  • parent – parent widget; ownership only.

Build the grid and add a panel per stack.

The canvas link is held as _canvas_link rather than _link: the latter name belongs to LinkedView and carries the process-wide selection bus, so shadowing it would leave the grid publishing selections into its own canvas link and hearing nothing from the app.

Parameters:
  • panels – the panels to build, as a {key: stack} mapping or an iterable of (key, stack) pairs; None starts empty.

  • parent – parent widget, or None.

  • columns – how many columns to lay out; None picks a roughly square grid for whatever is added.

  • titles – captions by key, for panels that want more than their key.

add_panel(key: str, stack: spacr.layers.LayerStack, *, title: str = '') → ComparisonPanel[source]

Add one panel; returns it.

A panel added to a grid the user has already zoomed into starts where the others are, not fitted to its own extent — otherwise adding a fifth channel throws away the view.

Parameters:
  • key – the panel’s name in this grid, converted to a string; a key already in the grid raises LayerError.

  • stack – the layer stack the new panel displays.

closeEvent(event) → None[source]

Leave the shared selection and let go of every panel’s model.

Parameters:

event – the close event, passed on to the base class after the grid unlinks and detaches its panels.

highlight(object_key: str) → List[str][source]

Select object_key in every panel that holds it; returns which.

The other half of the comparison: a cell picked in the DAPI panel is the same cell in the phalloidin panel, and saying so is what makes the four pictures one observation rather than four.

Parameters:

object_key – the object key to find, compared with each labels layer’s field object keys.

lock_all() → None[source]

Bring every panel back onto the shared window.

on_linked_selection_changed(selection) → None[source]

Another view selected something: show it in every panel.

Parameters:

selection – the shared selection; only one with exactly one key is shown.

remove_panel(key: str) → ComparisonPanel[source]

Take a panel out of the grid and return it.

Parameters:

key – the panel’s key; a key not in the grid raises LayerError.

reset_view() → None[source]

Fit every linked panel to its own stack again.

resizeEvent(event) → None[source]

A layout change resizes the cells; re-share the window afterwards.

Parameters:

event – the resize event, passed on to the base class before the view is re-shared.

The shared world window every locked panel is on.

Not called link: that is spacr.qt.linked_selection.LinkedView.link, the process-wide selection bus this grid also joins. Two different links, and confusing them is how a view ends up publishing selections to itself.

property panels: Dict[str, ComparisonPanel][source]

{key: panel}, in layout order.

class spacr.qt.comparison_grid.ComparisonPanel(key: str, stack: spacr.layers.LayerStack, parent=None, *, title: str = '')[source]

Bases: PySide6.QtWidgets.QWidget

One cell: a caption, a canvas and the checkbox that frees it.

Parameters:
  • key – the panel’s name in the CanvasLink.

  • stack – what this panel shows. Each panel has its OWN stack — that is the whole point, since they hold different channels, timepoints or conditions.

  • parent – parent widget; ownership only.

  • title – the caption over the panel. Empty falls back to key, which is a name the code chose, not one a reader picked – fine while the keys are the conditions, worth overriding once they are not.

Build one panel of the comparison grid.

Parameters:
  • key – identifies this panel in the grid and in every signal it emits.

  • stack – the layers this panel draws.

  • parent – parent widget, or None.

  • title – caption; an empty one falls back to key.

detach() → None[source]

Let go of the model. Call from the grid’s closeEvent.

property key: str[source]

This panel’s name in the link.

property stack: spacr.layers.LayerStack[source]

What this panel is showing.