Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: wip on fpbase spectra viewer #388

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
395 changes: 395 additions & 0 deletions src/pymmcore_widgets/_vispy_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
from __future__ import annotations

Check warning on line 1 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L1

Added line #L1 was not covered by tests

from enum import Enum
from typing import TYPE_CHECKING, Any, Literal, cast

Check warning on line 4 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L3-L4

Added lines #L3 - L4 were not covered by tests

from vispy import scene

Check warning on line 6 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L6

Added line #L6 was not covered by tests

if TYPE_CHECKING:
# just here cause vispy has poor type hints
from collections.abc import Sequence
from typing import TypedDict, Unpack

import numpy as np

class WidgetKwargs(TypedDict, total=False):
pos: tuple[float, float]
size: tuple[float, float]
border_color: str
border_width: float
bgcolor: str
padding: float
margin: float

class TextVisualKwargs(TypedDict, total=False):
text: str
color: str
bold: bool
italic: bool
face: str
font_size: float
pos: tuple[float, float] | tuple[float, float, float]
rotation: float
method: Literal["cpu", "gpu"]
depth_test: bool

class AxisWidgetKwargs(TypedDict, total=False):
orientation: Literal["left", "bottom"]
tick_direction: tuple[int, int]
axis_color: str
tick_color: str
text_color: str
minor_tick_length: float
major_tick_length: float
tick_width: float
tick_label_margin: float
tick_font_size: float
axis_width: float
axis_label: str
axis_label_margin: float
axis_font_size: float
font_size: float # overrides tick_font_size and axis_font_size

class LineKwargs(TypedDict, total=False):
color: str
symbol: str
line_kind: Literal["-"]
width: float # if width == 0, lines will not be shown
marker_size: float
edge_color: str
face_color: str
edge_width: float
connect: str | np.ndarray

class Grid(scene.Grid):
def add_view(
self,
row: int | None = None,
col: int | None = None,
row_span: int = 1,
col_span: int = 1,
**kwargs: Unpack[WidgetKwargs],
) -> scene.ViewBox:
super().add_view(...)

def add_widget(
self,
widget: None | scene.Widget = None,
row: int | None = None,
col: int | None = None,
row_span: int = 1,
col_span: int = 1,
**kwargs: Unpack[WidgetKwargs],
) -> scene.Widget:
super().add_widget(...)


__all__ = ["PlotWidget"]

Check warning on line 87 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L87

Added line #L87 was not covered by tests


DEFAULT_AXIS_KWARGS: AxisWidgetKwargs = {

Check warning on line 90 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L90

Added line #L90 was not covered by tests
"text_color": "w",
"axis_color": "#AAAAAA",
"tick_color": "#AAAAAA",
"tick_width": 1,
"tick_font_size": 7,
"tick_label_margin": 18,
"axis_label_margin": 50,
"minor_tick_length": 4,
"major_tick_length": 7,
"axis_width": 1.5,
"axis_font_size": 10,
}


class Component(str, Enum):
PAD_LEFT = "pad_left"
PAD_RIGHT = "pad_right"
PAD_BOTTOM = "pad_bottom"
TITLE = "title"
CBAR_TOP = "cbar_top"
CBAR_LEFT = "cbar_left"
CBAR_RIGHT = "cbar_right"
CBAR_BOTTOM = "cbar_bottom"
YAXIS = "yaxis"
XAXIS = "xaxis"
XLABEL = "xlabel"
YLABEL = "ylabel"

Check warning on line 117 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L105-L117

Added lines #L105 - L117 were not covered by tests

def __str__(self) -> str:
return self.value

Check warning on line 120 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L119-L120

Added lines #L119 - L120 were not covered by tests


class PlotWidget(scene.Widget):

Check warning on line 123 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L123

Added line #L123 was not covered by tests
"""Widget to facilitate plotting.

Parameters
----------
fg_color : str
The default color for the plot.
xlabel : str
The x-axis label.
ylabel : str
The y-axis label.
title : str
The title of the plot.
lock_axis : {'x', 'y', None}
Prevent panning and zooming along a particular axis.
**widget_kwargs : dict
Keyword arguments to pass to the parent class.
"""

def __init__(

Check warning on line 142 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L142

Added line #L142 was not covered by tests
self,
fg_color: str = "k",
xlabel: str = "",
ylabel: str = "",
title: str = "",
lock_axis: Literal["x", "y", None] = None,
**widget_kwargs: Unpack[WidgetKwargs],
) -> None:
self._fg_color = fg_color
self._visuals: list[scene.VisualNode] = []
super().__init__(**widget_kwargs)
self.unfreeze()
self.grid = cast("Grid", self.add_grid(spacing=0, margin=10))

Check warning on line 155 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L151-L155

Added lines #L151 - L155 were not covered by tests

title_kwargs: TextVisualKwargs = {

Check warning on line 157 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L157

Added line #L157 was not covered by tests
"font_size": 14,
"color": "w",
"face": "Open Sans",
}
label_kwargs: TextVisualKwargs = {

Check warning on line 162 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L162

Added line #L162 was not covered by tests
"font_size": 10,
"color": "w",
"face": "Open Sans",
}
self._title = scene.Label(str(title), **title_kwargs)
self._xlabel = scene.Label(str(xlabel), **label_kwargs)
self._ylabel = scene.Label(str(ylabel), rotation=-90, **label_kwargs)

Check warning on line 169 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L167-L169

Added lines #L167 - L169 were not covered by tests

axis_kwargs: AxisWidgetKwargs = DEFAULT_AXIS_KWARGS
self.yaxis = scene.AxisWidget(orientation="left", **axis_kwargs)
self.xaxis = scene.AxisWidget(orientation="bottom", **axis_kwargs)

Check warning on line 173 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L171-L173

Added lines #L171 - L173 were not covered by tests

# 2D Plot layout:
#
# c0 c1 c2 c3 c4 c5 c6
# +----------+-------+-------+-------+---------+---------+-----------+
# r0 | | | title | | |
# | +-----------------------+---------+---------+ |
# r1 | | | cbar | | |
# |----------+-------+-------+-------+---------+---------+ ----------|
# r2 | pad_left | cbar | ylabel| yaxis | view | cbar | pad_right |
# |----------+-------+-------+-------+---------+---------+ ----------|
# r3 | | | xaxis | | |
# | +-----------------------+---------+---------+ |
# r4 | | | xlabel | | |
# | +-----------------------+---------+---------+ |
# r5 | | | cbar | | |
# |---------+------------------------+---------+---------+-----------|
# r6 | | pad_bottom | |
# +---------+------------------------+---------+---------+-----------+

self._grid_wdgs: dict[Component, scene.Widget] = {}
for name, row, col, widget in [

Check warning on line 195 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L194-L195

Added lines #L194 - L195 were not covered by tests
(Component.PAD_LEFT, 2, 0, None),
(Component.PAD_RIGHT, 2, 6, None),
(Component.PAD_BOTTOM, 6, 4, None),
(Component.TITLE, 0, 4, self._title),
(Component.CBAR_TOP, 1, 4, None),
(Component.CBAR_LEFT, 2, 1, None),
(Component.CBAR_RIGHT, 2, 5, None),
(Component.CBAR_BOTTOM, 5, 4, None),
(Component.YAXIS, 2, 3, self.yaxis),
(Component.XAXIS, 3, 4, self.xaxis),
(Component.XLABEL, 4, 4, self._xlabel),
(Component.YLABEL, 2, 2, self._ylabel),
]:
self._grid_wdgs[name] = wdg = self.grid.add_widget(widget, row=row, col=col)

Check warning on line 209 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L209

Added line #L209 was not covered by tests
# If we don't set max size, they will expand to fill the entire grid
# occluding pretty much everything else.
if str(name).startswith(("cbar", "pad")):
if name in {

Check warning on line 213 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L212-L213

Added lines #L212 - L213 were not covered by tests
Component.PAD_LEFT,
Component.PAD_RIGHT,
Component.CBAR_LEFT,
Component.CBAR_RIGHT,
}:
wdg.width_max = 2

Check warning on line 219 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L219

Added line #L219 was not covered by tests
else:
wdg.height_max = 2

Check warning on line 221 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L221

Added line #L221 was not covered by tests

# The main view into which plots are added
self._view = self.grid.add_view(row=2, col=4)

Check warning on line 224 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L224

Added line #L224 was not covered by tests

# NOTE: this is a mess of hardcoded values... not sure whether they will work
# cross-platform. Note that `width_max` and `height_max` of 2 is actually
# *less* visible than 0 for some reason. They should also be extracted into
# some sort of `hide/show` logic for each component
self._grid_wdgs[Component.YAXIS].width_max = 30 # otherwise it takes too much
self._grid_wdgs[Component.PAD_LEFT].width_max = 20 # otherwise you get clipping
self._grid_wdgs[Component.XAXIS].height_max = 20 # otherwise it takes too much
self.ylabel = ylabel
self.xlabel = xlabel
self.title = title

Check warning on line 235 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L230-L235

Added lines #L230 - L235 were not covered by tests

# VIEWBOX (this has to go last, see vispy #1748)
self.camera = self._view.camera = PanZoom1DCamera(lock_axis)

Check warning on line 238 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L238

Added line #L238 was not covered by tests
# this has to come after camera is set
self.xaxis.link_view(self._view)
self.yaxis.link_view(self._view)
self.freeze()

Check warning on line 242 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L240-L242

Added lines #L240 - L242 were not covered by tests

@property
def title(self) -> str:

Check warning on line 245 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L244-L245

Added lines #L244 - L245 were not covered by tests
"""The title label."""
return self._title.text # type: ignore [no-any-return]

Check warning on line 247 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L247

Added line #L247 was not covered by tests

@title.setter
def title(self, text: str) -> None:

Check warning on line 250 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L249-L250

Added lines #L249 - L250 were not covered by tests
"""Set the title of the plot."""
self._title.text = text
wdg = self._grid_wdgs[Component.TITLE]
wdg.height_min = wdg.height_max = 30 if text else 2

Check warning on line 254 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L252-L254

Added lines #L252 - L254 were not covered by tests

@property
def xlabel(self) -> str:

Check warning on line 257 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L256-L257

Added lines #L256 - L257 were not covered by tests
"""The x-axis label."""
return self._xlabel.text # type: ignore [no-any-return]

Check warning on line 259 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L259

Added line #L259 was not covered by tests

@xlabel.setter
def xlabel(self, text: str) -> None:

Check warning on line 262 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L261-L262

Added lines #L261 - L262 were not covered by tests
"""Set the x-axis label."""
self._xlabel.text = text
wdg = self._grid_wdgs[Component.XLABEL]
wdg.height_min = wdg.height_max = 40 if text else 2

Check warning on line 266 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L264-L266

Added lines #L264 - L266 were not covered by tests

@property
def ylabel(self) -> str:

Check warning on line 269 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L268-L269

Added lines #L268 - L269 were not covered by tests
"""The y-axis label."""
return self._ylabel.text # type: ignore [no-any-return]

Check warning on line 271 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L271

Added line #L271 was not covered by tests

@ylabel.setter
def ylabel(self, text: str) -> None:

Check warning on line 274 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L273-L274

Added lines #L273 - L274 were not covered by tests
"""Set the x-axis label."""
self._ylabel.text = text
wdg = self._grid_wdgs[Component.YLABEL]
wdg.width_min = wdg.width_max = 20 if text else 2

Check warning on line 278 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L276-L278

Added lines #L276 - L278 were not covered by tests

def lock_axis(self, axis: Literal["x", "y", None]) -> None:

Check warning on line 280 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L280

Added line #L280 was not covered by tests
"""Prevent panning and zooming along a particular axis."""
self.camera._axis = axis

Check warning on line 282 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L282

Added line #L282 was not covered by tests
# self.camera.set_range()

def plot(self, data: Any, **kwargs: Unpack[LineKwargs]) -> scene.LinePlot:

Check warning on line 285 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L285

Added line #L285 was not covered by tests
"""Plot line data.

Parameters
----------
data : array-like
Arguments can be passed as `(Y,)`, `(X, Y)`, or `np.array((X, Y))`.
**kwargs : dict
Keyword arguments to pass to the `LinePlot` constructor.
"""
kwargs.setdefault("marker_size", 0)
kwargs.setdefault("width", 4)
kwargs.setdefault("color", "#1F77B4")
line = scene.LinePlot(data, **kwargs)
self._view.add(line)
self.camera.set_range()
self._visuals.append(line)
return line

Check warning on line 302 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L295-L302

Added lines #L295 - L302 were not covered by tests


class PanZoom1DCamera(scene.cameras.PanZoomCamera):

Check warning on line 305 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L305

Added line #L305 was not covered by tests
"""Camera that allows panning and zooming along one axis only.

Parameters
----------
axis : {'x', 'y', None}
The axis along which to allow panning and zooming.
*args : tuple
Positional arguments to pass to the parent class.
**kwargs : dict
Keyword arguments to pass to the parent class.
"""

def __init__(

Check warning on line 318 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L318

Added line #L318 was not covered by tests
self, axis: Literal["x", "y", None] = None, *args: Any, **kwargs: Any
) -> None:
self._axis: Literal["x", "y", None] = axis
super().__init__(*args, **kwargs)

Check warning on line 322 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L321-L322

Added lines #L321 - L322 were not covered by tests

@property
def axis_index(self) -> Literal[0, 1, None]:

Check warning on line 325 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L324-L325

Added lines #L324 - L325 were not covered by tests
"""Return the index of the axis along which to pan and zoom."""
if self._axis in ("x", 0):
return 0
elif self._axis in ("y", 1):
return 1
return None

Check warning on line 331 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L327-L331

Added lines #L327 - L331 were not covered by tests

def zoom(

Check warning on line 333 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L333

Added line #L333 was not covered by tests
self,
factor: float | tuple[float, float],
center: tuple[float, ...] | None = None,
) -> None:
"""Zoom the camera by `factor` around `center`."""
if self.axis_index is None:
super().zoom(factor, center=center)
return

Check warning on line 341 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L339-L341

Added lines #L339 - L341 were not covered by tests

if isinstance(factor, float):
factor = (factor, factor)
_factor = list(factor)
_factor[self.axis_index] = 1
super().zoom(_factor, center=center)

Check warning on line 347 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L343-L347

Added lines #L343 - L347 were not covered by tests

def pan(self, pan: Sequence[float]) -> None:

Check warning on line 349 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L349

Added line #L349 was not covered by tests
"""Pan the camera by `pan`."""
if self.axis_index is None:
super().pan(pan)
return
_pan = list(pan)
_pan[self.axis_index] = 0
super().pan(*_pan)

Check warning on line 356 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L351-L356

Added lines #L351 - L356 were not covered by tests

def set_range(

Check warning on line 358 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L358

Added line #L358 was not covered by tests
self,
x: tuple | None = None,
y: tuple | None = None,
z: tuple | None = None,
margin: float = 0, # overriding to create a different default from super()
) -> None:
"""Reset the camera view to the specified range."""
super().set_range(x, y, z, margin)

Check warning on line 366 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L366

Added line #L366 was not covered by tests


class Figure(scene.SceneCanvas):

Check warning on line 369 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L369

Added line #L369 was not covered by tests
"""Create a figure window."""

def __init__(

Check warning on line 372 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L372

Added line #L372 was not covered by tests
self,
bgcolor: str = "k",
size: tuple[int, int] = (800, 600),
show: bool = True,
keys: str = "interactive",
**kwargs: Any,
) -> None:
self._plot_widgets: list[PlotWidget] = []
self._grid: scene.Grid = None # initialize before the freeze occurs
super().__init__(bgcolor=bgcolor, keys=keys, show=show, size=size, **kwargs)
self._grid = cast("scene.Grid", self.central_widget.add_grid())
self._grid._default_class = PlotWidget

Check warning on line 384 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L380-L384

Added lines #L380 - L384 were not covered by tests

@property
def plot_widgets(self) -> tuple[PlotWidget, ...]:

Check warning on line 387 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L386-L387

Added lines #L386 - L387 were not covered by tests
"""List of the associated PlotWidget instances."""
return tuple(self._plot_widgets)

Check warning on line 389 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L389

Added line #L389 was not covered by tests

def __getitem__(self, idxs: int | tuple[int, int]) -> PlotWidget:

Check warning on line 391 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L391

Added line #L391 was not covered by tests
"""Get an axis."""
pw = cast("PlotWidget", self._grid[idxs])
self._plot_widgets += [pw]
return pw

Check warning on line 395 in src/pymmcore_widgets/_vispy_plot.py

View check run for this annotation

Codecov / codecov/patch

src/pymmcore_widgets/_vispy_plot.py#L393-L395

Added lines #L393 - L395 were not covered by tests
Loading
Loading