spacr.qt.widgets.figure_grid_view

Display saved and interactive run figures in a scrollable grid.

Saved figures are placed in uniform grid cells and rendered at their original aspect ratios, preserving the geometry of views such as plate heatmaps. The number of columns follows the available panel width, and selecting a tile opens the corresponding figure in the full-size detail view.

Interactive pyqtgraph panels appear as snapshot thumbnails rather than live widgets. This keeps grid resizing responsive while the original widget retains its hover, selection, and restyling behavior on its own tab. Activating a live thumbnail raises that widget. Saved runs and interactive panels use the same collapsible-section and workspace-state mechanisms.

Classes

FigureGridView

Every figure at once, scrollable, each at its own aspect ratio.

Functions

cell_span(→ int)

Columns a figure occupies: one, whatever its shape.

cells_across(→ int)

How many cells fit across panel_width.

heading_style(→ str)

How the run headings are drawn, minus the colour.

live_tiles_from_panels(→ list)

Photograph each pyqtgraph panel, ready for set_live_tiles().

Module Contents

class spacr.qt.widgets.figure_grid_view.FigureGridView(parent=None)[source]

Bases: PySide6.QtWidgets.QScrollArea

Every figure at once, scrollable, each at its own aspect ratio.

Variables:
  • figure_activated – emitted with a figure’s index when its cell is clicked, so the caller can open it full size.

  • figure_menu_requested – emitted with (index, global position) when a cell is right-clicked. The grid holds pictures, not figures, so the menu itself is the caller’s to build – it is the one that still has the matplotlib object.

Parameters:

parent – parent widget.

Build the scrolling figure grid.

Horizontal scrolling is off: the grid re-flows to the width it is given, so a horizontal bar would mean the re-flow failed rather than that there is more to see.

Parameters:

parent – parent widget, or None.

apply_workspace_state(state) → bool[source]

Put the arrangement back. Returns whether anything applied.

Parameters:

state – dict as workspace_state() returns it, with "collapsed" (list of [label, start] section keys) and "cell_width" (pixels); a non-dict applies nothing.

clear() → None[source]

Drop every figure and release the pixmaps behind them.

is_live_section_collapsed() → bool[source]

Whether the pyqtgraph tiles are folded away.

Named rather than left to is_section_collapsed() with two magic arguments: the live section’s key is a constant of this module and a caller repeating (LIVE_SECTION_LABEL, LIVE_SECTION_START) is a caller who can get one of them wrong and silently ask about a section that does not exist.

is_section_collapsed(label, start) → bool[source]

Whether this run’s figures are folded away.

Parameters:
  • label – the run’s section heading text.

  • start – index of the run’s first figure; with label it identifies the section.

live_tile_keys() → list[source]

Return live-panel keys in display order.

Panels without a usable snapshot are omitted, matching set_live_tiles().

resizeEvent(event)[source]

Re-flow the grid for the new width.

Parameters:

event – the Qt resize event.

set_figures(pixmaps, titles=None, sections=None) → int[source]

Replace the grid contents and return the number of figures added.

Parameters:
  • pixmaps (iterable) – Figure images to display.

  • titles (iterable, optional) – Captions corresponding to pixmaps.

  • sections (iterable, optional) – (label, start, count) entries describing runs. Panel lettering restarts within each section.

set_live_tiles(tiles) → int[source]

Replace the foldable section of live-panel snapshots.

Parameters:

tiles (iterable) – (key, pixmap) or (key, pixmap, title) entries. Activating a tile emits its key through live_tile_activated.

Returns:

int – Number of snapshots retained on the grid.

Notes

Entries without a pixmap are omitted. Existing tile widgets are destroyed before the complete set is rebuilt so refreshes cannot stack stale snapshots or retain panels no longer available.

set_pinned(pixmap, title: str = '') → bool[source]

A tile that is always first and is not one of the run’s figures.

The tile is a snapshot of the interactive regression graph. Activating it opens the live widget. It occupies a separate slot from _cells so persisted figure indices remain aligned with FigureQueue.show_index(); activation uses pinned_activated instead of a sentinel figure index.

Replacing the pinned tile destroys the previous cell before relayout, preventing transparent snapshots from accumulating at the same grid position.

This method updates only the regression tile; other live-panel tiles remain in place. Use set_live_tiles() to replace the full live section.

Parameters:

pixmap – snapshot QPixmap of the regression graph; None or a null pixmap removes the pinned tile.

Returns:

True when a tile was pinned. A null or missing pixmap removes it.

set_section_collapsed(label, start, collapsed: bool = True) → None[source]

Fold a run’s figures away, or bring them back.

Parameters:
  • label – the run’s section heading text.

  • start – index of the run’s first figure; with label it identifies the section.

set_target_cell_width(pixels: int) → None[source]

How wide a single-width cell should be, before layout.

Parameters:

pixels – target width in pixels, clamped to MIN_CELL_PX-MAX_CELL_PX.

toggle_section(header) → bool[source]

Reach the run first; fold it away second. Returns the new state.

The console provides the interaction model. A heading that is not already at the top of the viewport is a request to GO THERE, whatever its state – a first click that hid the very section the user was reaching for spends the gesture on the opposite of what it looked like. Only a heading already at the top has nowhere left to navigate to, and there folding is the one thing the gesture can still mean, on a second click exactly where the user’s hand already is.

Parameters:

header – the clicked section heading; its section_key names the section. A header without one does nothing and returns True.

workspace_state() → dict[source]

How the grid is ARRANGED, not what is in it.

The figures themselves are files in the run’s own results folder, recorded by the sections that name that folder; copying seventeen PNGs in here would be a second copy of something the run already has. What dies with the process is the arrangement – the tile size the user settled on and which sections they folded away – and a sweep of sixty trials is unusable if that resets.

spacr.qt.widgets.figure_grid_view.cell_span(aspect: float) → int[source]

Columns a figure occupies: one, whatever its shape.

Parameters:

aspect – width / height. Accepted and deliberately ignored – see CELL_SPAN. Four plates take four slots.

spacr.qt.widgets.figure_grid_view.cells_across(panel_width: int, target: int = TARGET_CELL_PX) → int[source]

How many cells fit across panel_width.

Widening the window should show MORE figures, not bigger ones – the opposite of what a stretch-to-fit view does.

Parameters:

panel_width – available width in pixels; the result is between 1 and 6, and 1 for a width of 0 or less.

spacr.qt.widgets.figure_grid_view.heading_style() → str[source]

How the run headings are drawn, minus the colour.

One string, because the chevron and the label are two widgets that have to read as one heading.

A FUNCTION AND NOT A CONSTANT. A module-level string is built once, at import, and so pinned the size to whatever the scale was when the module first loaded – which is exactly the bug this is fixing. Asked for at each use, it follows the interface scale.

Returns:

the heading stylesheet, without a colour.

spacr.qt.widgets.figure_grid_view.live_tiles_from_panels(panels) → list[source]

Photograph each pyqtgraph panel, ready for set_live_tiles().

Parameters:

panels – [(key, title, widget)] – the live panels, in the order they should appear. The widget only has to answer snapshot().

Returns:

[(key, pixmap, title)] for the ones that photographed.

Panels whose snapshot() returns None are omitted rather than shown as empty, nonfunctional tiles. Snapshot errors also omit that panel for the current refresh so an optional preview cannot interrupt the screen.