spacr.qt.widgets.umap_explorer

Interactive Image UMAP viewer with click, lasso, and DB annotation.

The lasso is also the cheapest way spaCR has of asking “and where are those cells on the plate?”, so the explorer joins the shared selection through spacr.qt.linked_selection.LinkedView: a lasso publishes the objects it caught, and a selection made anywhere else lights up the same points here.

The two directions are deliberately asymmetric, because a filter and a selection are not the same thing:

  • an incoming selection draws a ring around the matching points and changes nothing else. It never removes a point, and it never becomes the local lasso — “Label lasso selection” keeps meaning the lasso drawn here, or a highlight arriving from the database browser could write annotations the user never drew.

  • an incoming filter DIMS the points it excludes. Removing them would redraw the embedding around the survivors, and a UMAP whose axes move when you tick a checkbox is unreadable — the whole value of the projection is that a point stays where it was.

Classes

ImageUmapExplorer

Zoomable embedding: click a point, lasso a group, write labels.

UmapDisplaySettings

One window holding every Image UMAP display setting.

Functions

as_settings_keys(→ Dict)

Map Image UMAP display keys to their persisted setting names.

Module Contents

class spacr.qt.widgets.umap_explorer.ImageUmapExplorer(parent=None)[source]

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

Zoomable embedding: click a point, lasso a group, write labels.

Linked to the shared selection as "umap". See the module docstring for why an incoming selection highlights and an incoming filter dims.

Parameters:

parent – parent widget.

Build the explorer: the embedding, the gallery and the writers.

Parameters:

parent – parent widget.

apply_display(values: Dict) → bool[source]

Apply display settings to the figure that is already on screen.

Parameters:

values – {display key: value}; keys the explorer does not know and None values are skipped, and None for the whole mapping changes nothing.

Returns:

True when something changed and the canvas was redrawn.

The EMBEDDING is never touched. Only the keys in LIVE_DISPLAY_KEYS are honoured; anything else is stored for the next run and reported by the caller, because silently ignoring a setting the user just changed is worse than saying it needs a re-run.

closeEvent(event)[source]

Stop background work and unlink before going away.

Parameters:

event – the Qt close event.

display_settings() → Dict[source]

The current display values, as plain data.

linked_points() → numpy.ndarray[source]

Indices of the points a selection made elsewhere is highlighting.

on_linked_filter_changed(data_filter: spacr.selection.DataFilter) → None[source]

Re-draw for a filter another view has just set.

Parameters:

data_filter – the new shared filter.

on_linked_selection_changed(selection: spacr.selection.Selection) → None[source]

Ring the points somebody else selected. Nothing is hidden, and the local lasso — which is what the annotation buttons write — is left exactly as the user drew it.

Parameters:

selection – the Selection another view published; the points whose object keys it names are ringed, and an inactive selection rings none.

open_display_settings() → None[source]

Open the one window, apply what can apply, propagate all of it.

point_keys() → pandas.Index | None[source]

The object key of each point, or None when unidentifiable.

set_payload(payload: Dict) → None[source]

Load the arrays/records attached by generate_image_umap.

payload['frame'] is optional: a DataFrame with one row per point, carrying whatever the caller measured. Without it the explorer can still identify its points (from the records’ prcfo) and so still publishes and receives selections — but a filter on a measurement column has nothing here to test, and is reported as ignored rather than silently drawing everything as if it had applied.

Parameters:

payload – the dict attached by generate_image_umap: embedding (shape (N, 2)), labels and records of the same length, and optionally frame (a DataFrame with N rows) and display (initial display values). A wrong shape or length raises ValueError.

set_propagate_callback(callback) → None[source]

Register callback(dict) to push values into the settings panel.

Optional: the explorer is usable without one, and a widget built in a test has none.

Parameters:

callback – called with a dict of settings, keyed by their persisted names, after the display settings window is accepted; any exception it raises is logged and ignored.

show_point(index: int) → None[source]

Preview one point’s image and database identity.

Parameters:

index – position of the point in the payload’s records; converted to int, and an out-of-range index does nothing.

visible_points() → numpy.ndarray[source]

Boolean mask of the points the shared filter keeps.

class spacr.qt.widgets.umap_explorer.UmapDisplaySettings(values: Dict, parent=None)[source]

Bases: PySide6.QtWidgets.QDialog

One window holding every Image UMAP display setting.

Some apply to the figure on screen and some cannot, and the window says which rather than leaving the user to discover it. Asked for exactly that way: “the other settings can also be in the same settings window even though they cannot be live applied.”

The ones that cannot are not disabled – they are editable, saved, and take effect on the next run. A greyed control that holds a value the user wants to change is worse than a live one with a note beside it.

Parameters:
  • values – the settings to open on. Every key in FIELDS is looked up here; one that is absent falls back to its control’s own default rather than raising, so a settings file written before a field existed still opens.

  • parent – parent widget.

Build the display-settings dialog.

Parameters:
  • values – the settings to start from.

  • parent – parent widget.

live_values() → Dict[source]

Only the half that can reach the figure already on screen.

values() → Dict[source]

What the user set, keyed as the settings dict keys.

spacr.qt.widgets.umap_explorer.as_settings_keys(values: Dict) → Dict[source]

Map Image UMAP display keys to their persisted setting names.

Parameters:

values – display values keyed by display name; keys in SETTINGS_KEY_FOR_DISPLAY are renamed and the rest kept as they are. None gives an empty dict.

Nested helpers

ImageUmapExplorer._build_ui._OwnedTimerFigureCanvas.__init__(self, figure)

Wrap a figure in a canvas that owns its own redraw timer.

Parameters:

figure – the Matplotlib Figure to draw. The timer is a child of this canvas, so a queued redraw cannot outlive the object it would draw on – which is what the paragraph above means by owned.

spacr/qt/widgets/umap_explorer.py:440

ImageUmapExplorer._build_ui._OwnedTimerFigureCanvas._spacr_draw(self)

Draw once, if a draw is still pending.

The pending flag is cleared FIRST, so a draw that itself schedules another does not lose it.

spacr/qt/widgets/umap_explorer.py:459

ImageUmapExplorer._build_ui._OwnedTimerFigureCanvas.cancel_pending_draw(self)

Drop any queued redraw.

spacr/qt/widgets/umap_explorer.py:473

ImageUmapExplorer._build_ui._OwnedTimerFigureCanvas.draw_idle(self)

Queue a redraw on the canvas’s OWN timer.

spacr/qt/widgets/umap_explorer.py:453