Source code for anyplotlib.widgets._widgets1d

"""
widgets/_widgets1d.py
=====================
Interactive overlay widgets for 1-D line panels (Plot1D).

``VLineWidget`` and ``HLineWidget`` are also used by ``Plot2D``, where they
draw a full-height / full-width rule over the image.
"""

from __future__ import annotations
from anyplotlib.widgets._base import Widget

SNAP_DOC = """snap_values : sequence of float, optional
        Allowed positions.  While dragging, the widget follows the cursor but
        lands only on the nearest of these values — matplotlib's
        ``SpanSelector.snap_values``.  ``None`` (default) drags continuously.
        Set it later with ``widget.snap_values = [...]``."""


def _norm_snap_values(values):
    """Validate snap_values and return a plain list of floats (or None).

    A list is what crosses the wire, so numpy arrays have to be converted
    here — they are not JSON-serialisable and would break the panel push.
    """
    if values is None:
        return None
    out = [float(v) for v in values]
    if not out:
        return None
    return out


[docs] class VLineWidget(Widget): """Draggable vertical line overlay widget for 1-D plots. Allows interactive selection of a single x-axis value. The line can be dragged left/right to change the selected position. Parameters ---------- push_fn : Callable Update callback. x : float Initial x-position in data coordinates. color : str, optional CSS colour for the line. Default ``"#00e5ff"``. linewidth : float, optional Line stroke width in px. Default 2. """ #: Geometry roles, used when the line sits on a 2-D image panel. _GEOMETRY = {"x": "x", "snap_values": "x_list"} def __init__(self, push_fn, *, x, color="#00e5ff", linewidth=2, snap_values=None): super().__init__("vline", push_fn, x=float(x), color=color, linewidth=float(linewidth), snap_values=_norm_snap_values(snap_values))
[docs] class HLineWidget(Widget): """Draggable horizontal line overlay widget for bar charts. Allows interactive selection of a single y-axis value. The line can be dragged up/down to change the selected value. Parameters ---------- push_fn : Callable Update callback. y : float Initial y-position in data coordinates. color : str, optional CSS colour for the line. Default ``"#00e5ff"``. linewidth : float, optional Line stroke width in px. Default 2. """ #: Geometry roles, used when the line sits on a 2-D image panel. _GEOMETRY = {"y": "y", "snap_values": "y_list"} def __init__(self, push_fn, *, y, color="#00e5ff", linewidth=2, snap_values=None): super().__init__("hline", push_fn, y=float(y), color=color, linewidth=float(linewidth), snap_values=_norm_snap_values(snap_values))
[docs] class RangeWidget(Widget): """Draggable range selection widget. Two display styles are available: ``style='band'`` (default) Two connected vertical lines with a translucent fill band. Either line can be dragged independently; the whole band can be dragged by clicking inside it. ``style='fwhm'`` Two circular handles joined by a dashed horizontal line drawn at height *y* (the half-maximum level). Only the x-positions of the handles are draggable. Use this to show/edit a FWHM interval on a peak. With ``orientation='vertical'`` the band spans the plot width and selects a range on the *value* axis instead — for picking an intensity window rather than a spectral one. Parameters ---------- push_fn : Callable Update callback. x0, x1 : float The two edges of the range, in data coordinates **along the selection axis**: x positions when horizontal, y values when vertical. The names do not change with orientation, mirroring how matplotlib's ``SpanSelector.extents`` is read the same way for either ``direction``. color : str, optional CSS colour. Default ``"#00e5ff"``. style : {'band', 'fwhm'}, optional Visual style. Default ``"band"``. ``'fwhm'`` is horizontal-only. y : float, optional Y-position (data coordinates) for the connecting line when ``style='fwhm'``. Ignored for ``style='band'``. Default ``0.0``. orientation : {'horizontal', 'vertical'}, optional Which axis the range selects along. Default ``"horizontal"``. linewidth : float, optional Line stroke width in px. Default 2. max_extent : float, optional Maximum span width in DATA units. When set, the span physically stops growing at this width while dragging: the edge under the cursor is pinned and the opposite edge stays put, so the range never exceeds the cap and never jumps. ``None`` (default) leaves it unbounded. Use this when span width costs real work downstream — e.g. an integrating selector where the width is a number of frames to read. Enforcing it in the widget makes the limit visible (the edge simply stops) instead of applying a silent clamp after the fact. snap_values : sequence of float, optional Allowed edge positions. While dragging, each edge follows the cursor but lands only on the nearest of these values — matplotlib's ``SpanSelector.snap_values``. ``None`` (default) drags continuously. Set it later with ``widget.snap_values = [...]``. Raises ------ ValueError If *orientation* is not ``'horizontal'`` or ``'vertical'``, or if ``style='fwhm'`` is combined with a vertical orientation. """ def __init__(self, push_fn, *, x0, x1, color="#00e5ff", style: str = "band", y: float = 0.0, linewidth=2, max_extent=None, orientation: str = "horizontal", snap_values=None): if orientation not in ("horizontal", "vertical"): raise ValueError( f"orientation must be 'horizontal' or 'vertical', " f"got {orientation!r}" ) if orientation == "vertical" and style == "fwhm": raise ValueError("style='fwhm' is only defined for a horizontal range") super().__init__("range", push_fn, x0=float(x0), x1=float(x1), color=color, style=str(style), y=float(y), linewidth=float(linewidth), orientation=str(orientation), snap_values=_norm_snap_values(snap_values), max_extent=(None if max_extent is None else float(max_extent)))
[docs] class PointWidget(Widget): """Draggable point (control point) overlay widget for 1-D plots. A free-moving handle that can be dragged to any position within the plot area. Reports its data-space ``x`` and ``y`` coordinates back to Python via the standard callback hooks. Parameters ---------- push_fn : Callable Update callback. x : float Initial x position in data coordinates. y : float Initial y position in data coordinates (value axis). color : str, optional CSS colour for the handle. Default ``"#00e5ff"``. show_crosshair : bool, optional If ``True`` (default), draw dashed crosshair guide lines through the handle. Set to ``False`` for a bare draggable dot with no guides. linewidth : float, optional Guide-line stroke width in px. Default 2. """ def __init__(self, push_fn, *, x, y, color="#00e5ff", show_crosshair=True, linewidth=2): super().__init__("point", push_fn, x=float(x), y=float(y), color=color, show_crosshair=bool(show_crosshair), linewidth=float(linewidth))