spacr.qt.widgets.gate_canvas

Axis-specific cutoff and transform controls for the Gate Editor.

axis_at() identifies the axis associated with a pointer position from its bounding box. AxisCutoffs stores display limits by measurement, and axis_menu_items() represents the corresponding menu as data so it can be tested without opening a graphical popup.

Cutoffs affect only the displayed range; they do not filter rows or change gate membership. Limits are keyed by measurement rather than by the current X/Y assignment, so they follow a measurement when axes are exchanged.

Classes

AxisCutoff

The lowest and highest value an axis shows. None means "the data".

AxisCutoffs

The cutoffs a session has set, keyed by measurement.

AxisMenuItem

One row of an axis menu, as data.

Functions

apply_cutoffs(→ Tuple[str, ...])

Narrow axes to the cutoffs set for the measurements it draws.

axis_at(→ Optional[str])

Which axis a click at point landed on: "x", "y" or None.

axis_menu_items(axis, column, *[, scale, cutoff, ...])

The menu behind a right-click on one axis, as a list of rows.

parse_cutoff(→ Optional[float])

A number typed into a cutoff box, or None for "leave this end".

Module Contents

class spacr.qt.widgets.gate_canvas.AxisCutoff[source]

The lowest and highest value an axis shows. None means “the data”.

Either end may be left open: cutting the bottom off a long tail while letting the top follow the data is the common case, and forcing both ends would make the user invent a number for the end they did not care about.

Raises:

CutoffError – when the low end is not below the high end. Equal ends give an axis with no extent, which matplotlib draws as a blank panel rather than as an error.

describe() str[source]

The cutoff as a user reads it: 10 500, 10, 500.

limits(low: float, high: float) Tuple[float, float][source]

(low, high) with the unpinned ends filled from the data.

property is_set: bool[source]

Whether either end has been pinned.

class spacr.qt.widgets.gate_canvas.AxisCutoffs(initial: Dict[str, AxisCutoff] | None = None)[source]

The cutoffs a session has set, keyed by measurement.

Empty cutoffs are removed rather than stored as (None, None). A measurement is therefore present only when at least one limit is active.

Parameters:

initial – cutoffs to start from, keyed by measurement. Copied, so the caller’s dict is not mutated as the session sets more.

Create the cutoff set, optionally seeded.

Parameters:

initial – cutoffs by column; copied, so the caller’s mapping is not adopted.

clear(column: str) bool[source]

Forget column’s cutoff. Returns whether there was one.

clear_all() int[source]

Forget every cutoff. Returns how many were dropped.

columns() Tuple[str, ...][source]

Every measurement that carries a cutoff, in the order they were set.

get(column: str | None) AxisCutoff[source]

The cutoff for column, or an empty one. Never None.

Callers ask this on every render, so returning an empty cutoff rather than None keeps the if cutoff is None branch out of the drawing path.

set(column: str, low: float | None = None, high: float | None = None) AxisCutoff[source]

Pin column between low and high. Returns what was stored.

Setting both ends to None clears the column rather than storing an empty cutoff, so a cleared measurement stops reporting as cut off.

class spacr.qt.widgets.gate_canvas.AxisMenuItem[source]

One row of an axis menu, as data.

Parameters:
  • label – what the row says, or None for a separator.

  • callback – what clicking it does. None for a row that is only there to be read.

  • checkedTrue/False for a row that shows a tick, None for one that is not checkable.

  • enabled – whether it can be clicked.

  • why – why not, when it cannot. A greyed row with no reason is a dead end that reads as a bug.

spacr.qt.widgets.gate_canvas.apply_cutoffs(axes, columns: Sequence[str | None], cutoffs: AxisCutoffs) Tuple[str, ...][source]

Narrow axes to the cutoffs set for the measurements it draws.

Parameters:
  • axes – a matplotlib Axes.

  • columns(x_column, y_column) – what is on each axis now.

  • cutoffs – the session’s cutoffs, keyed by measurement.

Returns:

the axes that were narrowed, e.g. ("x",).

The unpinned end of a one-sided cutoff is taken from the limits the data already produced, so cutting the bottom off leaves the top where the scatter put it rather than collapsing it onto the cut.

spacr.qt.widgets.gate_canvas.axis_at(point: Sequence[float], bbox: Sequence[float]) str | None[source]

Which axis a click at point landed on: "x", "y" or None.

Parameters:
  • point(x, y) in the figure’s display coordinates, which have their origin at the BOTTOM left – the convention every matplotlib bounding box uses, so the two never need converting between.

  • bbox – the plotting rectangle as (x0, y0, x1, y1).

Returns:

None inside the rectangle, where the plot’s own menu belongs, and for the margins that belong to neither axis.

The strip BELOW the rectangle is the x axis and the strip to its LEFT is the y axis – that is where the ticks and the axis label are drawn, so it is where a user aiming at “the axis” clicks. In the corner where the two strips overlap the further overshoot wins: well to the left and barely below is the y axis, and the other way round is the x axis.

spacr.qt.widgets.gate_canvas.axis_menu_items(axis: str, column: str | None, *, scale: str = 'linear', cutoff: AxisCutoff | None = None, positive: bool = True, on_scale: Callable[[str], None] | None = None, on_cutoffs: Callable[[], None] | None = None, on_clear: Callable[[], None] | None = None)[source]

The menu behind a right-click on one axis, as a list of rows.

Parameters:
  • axis"x" or "y".

  • column – the measurement on that axis, or None when the axis is empty – in which case there is nothing to lay out or cut off, and every row says so rather than being missing.

  • scale – the scale in force, ticked in the list.

  • cutoff – what is pinned now, shown on the clearing row so the menu says what it would undo.

  • positive – whether the measurement stays above zero. The scales in POSITIVE_ONLY draw an empty panel where it does not, so they are greyed with that reason instead of accepting a click that cannot take effect.

  • on_scale – called with the chosen scale.

  • on_cutoffs – called to ask for new cutoffs.

  • on_clear – called to drop the cutoffs on this axis.

spacr.qt.widgets.gate_canvas.parse_cutoff(text: str) float | None[source]

A number typed into a cutoff box, or None for “leave this end”.

Blank means the data decides that end. A blank box is the only way to say “cut the bottom off and let the top follow the data”, so it is a value rather than an error.

Raises:

CutoffError – for text that is neither blank nor a number, naming what was typed – a silent fall back to “the data decides” would look exactly like the cutoff having been applied and done nothing.