spacr.qt.roi_tool

Draw an ROI on the layer canvas, and hand it to Measure.

The two halves of item B14. spacr.roi is the half that runs in a measurement worker — the geometry, the keep/drop rule and the environment route that gets both into a spawn pool. This is the half that needs a mouse: a pen that turns clicks into a spacr.layers.Shape, and a panel that turns the shapes layer into a spacr.roi.RoiSet and switches the measurement filter on.

The pen draws into the model, not onto the widget

Every vertex is a world coordinate taken from spacr.layers.Canvas.world_at(), and the polygon being drawn is a real Shape in a real ShapesLayer. So the half-finished ROI is rendered by the same compositor as everything else, it survives a zoom, and the finished ROI is already in the form spacr.roi.RoiSet.from_shapes_layer() reads. Nothing about the drawing is in pixels, which is what lets an ROI drawn on a downsampled preview name the same region on a full-resolution mask.

The panel says whether it will actually work

Switching the filter on is one call, and the thing worth putting on screen is not that it succeeded but whether it will reach the processes that do the measuring. A filter registered in the GUI process alone is a silent no-op in every spawn worker: the run finishes, the numbers are for the whole field, and nothing says so. spacr.roi.worker_delivery_status() answers that in advance and the panel shows its answer, warning-coloured when it is “no”.

Classes

RoiPanel

Draw an ROI, then measure only inside it.

RoiPen

Turns clicks on a LayerCanvas into a shape.

Module Contents

class spacr.qt.roi_tool.RoiPanel(canvas: spacr.qt.layer_viewer.LayerCanvas, parent=None, *, roi_path: str | None = None)[source]

Bases: PySide6.QtWidgets.QWidget

Draw an ROI, then measure only inside it.

Parameters:
  • canvas – the LayerCanvas to draw on. The panel adds its own shapes layer to that canvas’s stack the first time drawing starts.

  • roi_path – where the ROI is written. A worker reads it from there, so it has to be a real file; defaults to ./roi/measure_roi.json.

  • parent – parent widget; ownership only.

Build the ROI panel over a canvas.

Parameters:
  • canvas – the canvas whose shapes layer the ROI is drawn into.

  • parent – parent widget, or None.

  • roi_path – where the ROI is saved; defaults to roi/measure_roi.json under the working directory. It is a file because a worker process can only reach the ROI through the file system.

clear_rois() → int[source]

Remove every drawn ROI; returns how many went.

closeEvent(event) → None[source]

Take the pen off the canvas so it does not outlive this panel.

Parameters:

event – the close event; it is passed on to the base class unchanged.

disable() → bool[source]

Go back to measuring whole fields.

enable() → bool[source]

Register the drawn ROI as a Measure region filter.

Returns:

True when the filter was installed. A drawing mistake is reported in the status line rather than raised out of a click.

fields() → List[str][source]

The field stems the ROI is filed under, from the scope box.

roi_layer(create: bool = True) → spacr.layers.ShapesLayer | None[source]

The ROI shapes layer, adding it to the stack the first time.

Given the spacing of whatever 2-D layer is already in the stack, so an ROI drawn over a µm-calibrated image is stored in µm and one drawn over a pixel-spaced field is stored in pixels — the units the measurement will be compared against.

roi_set() → spacr.roi.RoiSet[source]

Build a spacr.roi.RoiSet from what has been drawn.

Raises:

spacr.roi.RoiError – if nothing closed has been drawn yet.

set_roi_path(path: str) → str[source]

Choose where the ROI file is written; returns the absolute path.

Parameters:

path – where the ROI file should be written; it is made absolute.

start_drawing() → RoiPen[source]

Attach a pen to the canvas and return it.

stop_drawing() → None[source]

Take the pen off the canvas, abandoning anything half-drawn.

property pen: RoiPen | None[source]

The pen while drawing is switched on, else None.

property roi_path: str[source]

Where the ROI is written for the workers to read.

property stack: spacr.layers.LayerStack[source]

The stack the canvas is showing.

class spacr.qt.roi_tool.RoiPen(layer: spacr.layers.ShapesLayer, *, kind: str = 'polygon', parent: PySide6.QtCore.QObject | None = None)[source]

Bases: spacr.qt.layer_viewer.CanvasTool, PySide6.QtCore.QObject

Turns clicks on a LayerCanvas into a shape.

Left click adds a vertex, double click (or Return) closes the polygon, Backspace takes the last vertex back and Escape abandons the whole thing. A rectangle and an ellipse take two clicks — opposite corners — and close themselves, because a third click on a two-corner shape has no meaning.

While it is being drawn the ROI is a path shape in the layer, so the user sees the outline they have so far; when it closes, the path is replaced by a real closed shape. Both are model objects: there is no parallel “rubber band” drawing that could disagree with what is stored.

Parameters:
  • layer – the ShapesLayer to draw into.

  • kind – 'polygon', 'rectangle' or 'ellipse'.

  • parent – parent object; ownership only.

Arm a pen that draws one closed shape into a shapes layer.

Parameters:
  • layer – the layer the finished ROI is added to.

  • kind – shape to draw – "polygon", "rectangle" or "ellipse".

  • parent – parent object, or None.

Raises:

LayerError – if layer is not a ShapesLayer, or if kind is not one of the three closed shapes – an ROI has an inside, so an open path cannot be one.

add_world(world: Dict[str, float]) → int[source]

Add one vertex given as {axis: world}; returns the vertex count.

A rectangle or an ellipse closes itself on the second vertex.

Parameters:

world – the vertex position as {axis: world coordinate}, converted to data coordinates through the layer’s spacing.

cancel() → None[source]

Abandon the shape being drawn.

close_shape() → int[source]

Finish the shape. Returns its index, or -1 if there was not one.

Too few vertices is not an error: a stray double click on an empty canvas should do nothing, not raise out of a mouse handler.

detach() → None[source]

Taken off the canvas: drop anything half-drawn.

double_click(view: spacr.qt.layer_viewer.LayerCanvas, world: Dict[str, float], event: Any) → bool[source]

Close the polygon. The second click of the pair is not a vertex.

Parameters:
  • view – the canvas that received the double-click; unused.

  • world – the click position as {axis: world coordinate}; unused, because the double-click only closes the shape.

  • event – the mouse event; unused.

key(view: spacr.qt.layer_viewer.LayerCanvas, event: Any) → bool[source]

Escape abandons, Return closes, Backspace undoes.

Parameters:
  • view – the canvas that received the key press; unused.

  • event – the key event; only its key() is read.

press(view: spacr.qt.layer_viewer.LayerCanvas, world: Dict[str, float], event: Any) → bool[source]

Place a vertex (left button) or take the last one back (right).

Parameters:
  • view – the canvas that received the press; unused.

  • world – the press position as {axis: world coordinate}, added as a vertex on a left-button press.

  • event – the mouse event; only its button() is read, and an event without one counts as a left click.

undo() → int[source]

Remove the most recent vertex; returns how many are left.

property kind: str[source]

The kind of shape being drawn.

property layer: spacr.layers.ShapesLayer[source]

The layer this pen draws into.

property pending: numpy.ndarray[source]

The vertices placed so far, (M, ndim) in DATA coordinates.