From f310f8d3a349182f2619f74d96cb08aac1e10ca8 Mon Sep 17 00:00:00 2001 From: kushalkolar Date: Sun, 31 Mar 2024 03:47:14 -0400 Subject: [PATCH] min version py3.10, cleanup type annotations --- fastplotlib/graphics/_base.py | 18 ++-- fastplotlib/graphics/line.py | 6 +- fastplotlib/graphics/line_collection.py | 77 +++++++++-------- fastplotlib/graphics/scatter.py | 6 +- fastplotlib/layouts/_defaults.py | 1 - .../layouts/_frame/_ipywidget_toolbar.py | 4 +- fastplotlib/layouts/_frame/_jupyter_output.py | 4 +- ...ods_mixin.py => _graphic_methods_mixin.py} | 82 ++++++++++--------- fastplotlib/layouts/_gridplot.py | 57 ++++++++----- fastplotlib/layouts/_plot_area.py | 77 +++++++++-------- fastplotlib/layouts/_record_mixin.py | 5 +- fastplotlib/layouts/_subplot.py | 30 ++++--- fastplotlib/layouts/_utils.py | 15 ++-- fastplotlib/legends/legend.py | 12 +-- fastplotlib/utils/functions.py | 7 +- fastplotlib/widgets/histogram_lut.py | 3 +- .../generate_add_graphic_methods.py | 2 +- setup.py | 2 +- 18 files changed, 218 insertions(+), 190 deletions(-) delete mode 100644 fastplotlib/layouts/_defaults.py rename fastplotlib/layouts/{graphic_methods_mixin.py => _graphic_methods_mixin.py} (87%) rename fastplotlib/utils/generate_add_methods.py => scripts/generate_add_graphic_methods.py (97%) diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index fdf120268..4442c851e 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -1,4 +1,4 @@ -from typing import * +from typing import Any, Literal import weakref from warnings import warn from abc import ABC, abstractmethod @@ -13,7 +13,7 @@ # dict that holds all world objects for a given python kernel/session # Graphic objects only use proxies to WorldObjects -WORLD_OBJECTS: Dict[str, WorldObject] = dict() #: {hex id str: WorldObject} +WORLD_OBJECTS: dict[str, WorldObject] = dict() #: {hex id str: WorldObject} PYGFX_EVENTS = [ @@ -87,7 +87,7 @@ def __init__( self._plot_area = None @property - def name(self) -> Union[str, None]: + def name(self) -> str | None: """str name reference for this item""" return self._name @@ -162,7 +162,7 @@ def visible(self, v: bool): self.world_object.visible = v @property - def children(self) -> List[WorldObject]: + def children(self) -> list[WorldObject]: """Return the children of the WorldObject.""" return self.world_object.children @@ -432,7 +432,7 @@ class PreviouslyModifiedData: indices: Any -COLLECTION_GRAPHICS: Dict[str, Graphic] = dict() +COLLECTION_GRAPHICS: dict[str, Graphic] = dict() class GraphicCollection(Graphic): @@ -440,7 +440,7 @@ class GraphicCollection(Graphic): def __init__(self, name: str = None): super().__init__(name) - self._graphics: List[str] = list() + self._graphics: list[str] = list() self._graphics_changed: bool = True self._graphics_array: np.ndarray[Graphic] = None @@ -548,7 +548,7 @@ class CollectionIndexer: def __init__( self, parent: GraphicCollection, - selection: List[Graphic], + selection: list[Graphic], ): """ @@ -605,7 +605,7 @@ def __repr__(self): class CollectionFeature: """Collection Feature""" - def __init__(self, selection: List[Graphic], feature: str): + def __init__(self, selection: list[Graphic], feature: str): """ selection: list of Graphics a list of the selected Graphics from the parent GraphicCollection based on the ``selection_indices`` @@ -618,7 +618,7 @@ def __init__(self, selection: List[Graphic], feature: str): self._selection = selection self._feature = feature - self._feature_instances: List[GraphicFeature] = list() + self._feature_instances: list[GraphicFeature] = list() if len(self._selection) > 0: for graphic in self._selection: diff --git a/fastplotlib/graphics/line.py b/fastplotlib/graphics/line.py index d76c8e704..f44347a58 100644 --- a/fastplotlib/graphics/line.py +++ b/fastplotlib/graphics/line.py @@ -18,10 +18,10 @@ def __init__( self, data: Any, thickness: float = 2.0, - colors: Union[str, np.ndarray, Iterable] = "w", + colors: str | np.ndarray | Iterable = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[np.ndarray, List] = None, + cmap_values: np.ndarray | Iterable = None, z_position: float = None, collection_index: int = None, *args, @@ -46,7 +46,7 @@ def __init__( apply a colormap to the line instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap alpha: float, optional, default 1.0 diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index bb7bb2444..8488ec15e 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -20,14 +20,14 @@ class LineCollection(GraphicCollection, Interaction): def __init__( self, data: List[np.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[np.ndarray], np.ndarray] = "w", + z_position: Iterable[float] | float = None, + thickness: float | Iterable[float] = 2.0, + colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", alpha: float = 1.0, - cmap: Union[List[str], str] = None, - cmap_values: Union[np.ndarray, List] = None, + cmap: Iterable[str] | str = None, + cmap_values: np.ndarray | List = None, name: str = None, - metadata: Union[list, tuple, np.ndarray] = None, + metadata: Iterable[Any] | np.ndarray = None, *args, **kwargs, ): @@ -36,39 +36,41 @@ def __init__( Parameters ---------- - data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + alpha: float, optional + alpha value for colors, if colors is a ``str`` + + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional name of the line collection - metadata: list, tuple, or array + metadata: Iterable or array metadata associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` @@ -235,7 +237,7 @@ def cmap_values(self) -> np.ndarray: return self._cmap_values @cmap_values.setter - def cmap_values(self, values: Union[np.ndarray, list]): + def cmap_values(self, values: np.ndarray | Iterable): colors = parse_cmap_values( n_colors=len(self), cmap_name=self.cmap, cmap_values=values ) @@ -477,13 +479,16 @@ class LineStack(LineCollection): def __init__( self, data: List[np.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[np.ndarray], np.ndarray] = "w", - cmap: Union[List[str], str] = None, - separation: float = 10, - separation_axis: str = "y", + z_position: Iterable[float] | float = None, + thickness: float | Iterable[float] = 2.0, + colors: str | Iterable[str] | np.ndarray | Iterable[np.ndarray] = "w", + alpha: float = 1.0, + cmap: Iterable[str] | str = None, + cmap_values: np.ndarray | List = None, name: str = None, + metadata: Iterable[Any] | np.ndarray = None, + separation: float = 10.0, + separation_axis: str = "y", *args, **kwargs, ): @@ -492,33 +497,37 @@ def __init__( Parameters ---------- - data: list of array-like + data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines - | if ``list`` of ``float``, each value will apply to individual lines + | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines - | is ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] - | if ``list`` of ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] + | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - name: str, optional - name of the line stack + cmap_values: 1D array-like or Iterable of numerical values, optional + if provided, these values are used to map the colors from the cmap + + metadata: Iterable or array + metadata associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 space in between each line graphic in the stack @@ -529,13 +538,9 @@ def __init__( name: str, optional name of the line stack - args - passed to LineCollection - kwargs passed to LineCollection - Features -------- @@ -549,8 +554,12 @@ def __init__( z_position=z_position, thickness=thickness, colors=colors, + alpha=alpha, cmap=cmap, + cmap_values=cmap_values, + metadata=metadata, name=name, + *args, **kwargs, ) diff --git a/fastplotlib/graphics/scatter.py b/fastplotlib/graphics/scatter.py index 3f04f644e..2557cd637 100644 --- a/fastplotlib/graphics/scatter.py +++ b/fastplotlib/graphics/scatter.py @@ -14,11 +14,11 @@ class ScatterGraphic(Graphic): def __init__( self, data: np.ndarray, - sizes: Union[int, float, np.ndarray, list] = 1, - colors: np.ndarray = "w", + sizes: float | np.ndarray | Iterable[float] = 1, + colors: str | np.ndarray | Iterable[str] = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[np.ndarray, List] = None, + cmap_values: np.ndarray | List = None, z_position: float = 0.0, *args, **kwargs, diff --git a/fastplotlib/layouts/_defaults.py b/fastplotlib/layouts/_defaults.py deleted file mode 100644 index 8b1378917..000000000 --- a/fastplotlib/layouts/_defaults.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py index 72976a445..5b42c8eab 100644 --- a/fastplotlib/layouts/_frame/_ipywidget_toolbar.py +++ b/fastplotlib/layouts/_frame/_ipywidget_toolbar.py @@ -4,8 +4,6 @@ from math import copysign from functools import partial from pathlib import Path -from typing import * - from ipywidgets.widgets import ( IntSlider, @@ -238,7 +236,7 @@ def __init__(self, iw): tooltip="reset vmin/vmax and reset histogram using current frame", ) - self.sliders: Dict[str, IntSlider] = dict() + self.sliders: dict[str, IntSlider] = dict() # only for xy data, no time point slider needed if self.iw.ndim == 2: diff --git a/fastplotlib/layouts/_frame/_jupyter_output.py b/fastplotlib/layouts/_frame/_jupyter_output.py index 786041bcf..9ebf0941d 100644 --- a/fastplotlib/layouts/_frame/_jupyter_output.py +++ b/fastplotlib/layouts/_frame/_jupyter_output.py @@ -1,5 +1,3 @@ -from typing import * - from ipywidgets import VBox, Widget from sidecar import Sidecar from IPython.display import display @@ -20,7 +18,7 @@ def __init__( make_toolbar: bool, use_sidecar: bool, sidecar_kwargs: dict, - add_widgets: List[Widget], + add_widgets: list[Widget], ): """ diff --git a/fastplotlib/layouts/graphic_methods_mixin.py b/fastplotlib/layouts/_graphic_methods_mixin.py similarity index 87% rename from fastplotlib/layouts/graphic_methods_mixin.py rename to fastplotlib/layouts/_graphic_methods_mixin.py index 0376fd777..a7acb5eec 100644 --- a/fastplotlib/layouts/graphic_methods_mixin.py +++ b/fastplotlib/layouts/_graphic_methods_mixin.py @@ -178,14 +178,14 @@ def add_image( def add_line_collection( self, data: List[numpy.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", + z_position: Union[Iterable[float], float] = None, + thickness: Union[float, Iterable[float]] = 2.0, + colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", alpha: float = 1.0, - cmap: Union[List[str], str] = None, + cmap: Union[Iterable[str], str] = None, cmap_values: Union[numpy.ndarray, List] = None, name: str = None, - metadata: Union[list, tuple, numpy.ndarray] = None, + metadata: Union[Iterable[Any], numpy.ndarray] = None, *args, **kwargs ) -> LineCollection: @@ -195,39 +195,41 @@ def add_line_collection( Parameters ---------- - data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + alpha: float, optional + alpha value for colors, if colors is a ``str`` + + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap name: str, optional name of the line collection - metadata: list, tuple, or array + metadata: Iterable or array metadata associated with this collection, this is for the user to manage. ``len(metadata)`` must be same as ``len(data)`` @@ -268,7 +270,7 @@ def add_line( colors: Union[str, numpy.ndarray, Iterable] = "w", alpha: float = 1.0, cmap: str = None, - cmap_values: Union[numpy.ndarray, List] = None, + cmap_values: Union[numpy.ndarray, Iterable] = None, z_position: float = None, collection_index: int = None, *args, @@ -294,7 +296,7 @@ def add_line( apply a colormap to the line instead of assigning colors manually, this overrides any argument passed to "colors" - cmap_values: 1D array-like or list of numerical values, optional + cmap_values: 1D array-like or Iterable of numerical values, optional if provided, these values are used to map the colors from the cmap alpha: float, optional, default 1.0 @@ -346,13 +348,16 @@ def add_line( def add_line_stack( self, data: List[numpy.ndarray], - z_position: Union[List[float], float] = None, - thickness: Union[float, List[float]] = 2.0, - colors: Union[List[numpy.ndarray], numpy.ndarray] = "w", - cmap: Union[List[str], str] = None, - separation: float = 10, - separation_axis: str = "y", + z_position: Union[Iterable[float], float] = None, + thickness: Union[float, Iterable[float]] = 2.0, + colors: Union[str, Iterable[str], numpy.ndarray, Iterable[numpy.ndarray]] = "w", + alpha: float = 1.0, + cmap: Union[Iterable[str], str] = None, + cmap_values: Union[numpy.ndarray, List] = None, name: str = None, + metadata: Union[Iterable[Any], numpy.ndarray] = None, + separation: float = 10.0, + separation_axis: str = "y", *args, **kwargs ) -> LineStack: @@ -362,33 +367,37 @@ def add_line_stack( Parameters ---------- - data: list of array-like + data: list of array-like or array List of line data to plot, each element must be a 1D, 2D, or 3D numpy array if elements are 2D, interpreted as [y_vals, n_lines] - z_position: list of float or float, optional + z_position: Iterable of float or float, optional | if ``float``, single position will be used for all lines - | if ``list`` of ``float``, each value will apply to individual lines + | if ``list`` of ``float``, each value will apply to the individual lines - thickness: float or list of float, default 2.0 + thickness: float or Iterable of float, default 2.0 | if ``float``, single thickness will be used for all lines | if ``list`` of ``float``, each value will apply to the individual lines - colors: str, RGBA array, list of RGBA array, or list of str, default "w" + colors: str, RGBA array, Iterable of RGBA array, or Iterable of str, default "w" | if single ``str`` such as "w", "r", "b", etc, represents a single color for all lines | if single ``RGBA array`` (tuple or list of size 4), represents a single color for all lines - | is ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] - | if ``list`` of ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line + | if ``list`` of ``str``, represents color for each individual line, example ["w", "b", "r",...] + | if ``RGBA array`` of shape [data_size, 4], represents a single RGBA array for each line - cmap: list of str or str, optional + cmap: Iterable of str or str, optional | if ``str``, single cmap will be used for all lines | if ``list`` of ``str``, each cmap will apply to the individual lines .. note:: ``cmap`` overrides any arguments passed to ``colors`` - name: str, optional - name of the line stack + cmap_values: 1D array-like or Iterable of numerical values, optional + if provided, these values are used to map the colors from the cmap + + metadata: Iterable or array + metadata associated with this collection, this is for the user to manage. + ``len(metadata)`` must be same as ``len(data)`` separation: float, default 10 space in between each line graphic in the stack @@ -399,13 +408,9 @@ def add_line_stack( name: str, optional name of the line stack - args - passed to LineCollection - kwargs passed to LineCollection - Features -------- @@ -421,10 +426,13 @@ def add_line_stack( z_position, thickness, colors, + alpha, cmap, + cmap_values, + name, + metadata, separation, separation_axis, - name, *args, **kwargs ) @@ -432,8 +440,8 @@ def add_line_stack( def add_scatter( self, data: numpy.ndarray, - sizes: Union[int, float, numpy.ndarray, list] = 1, - colors: numpy.ndarray = "w", + sizes: Union[float, numpy.ndarray, Iterable[float]] = 1, + colors: Union[str, numpy.ndarray, Iterable[str]] = "w", alpha: float = 1.0, cmap: str = None, cmap_values: Union[numpy.ndarray, List] = None, diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index fa987b661..5f7f3086d 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -1,6 +1,6 @@ from itertools import product, chain import numpy as np -from typing import * +from typing import Literal from inspect import getfullargspec from warnings import warn @@ -18,14 +18,23 @@ class GridPlot(Frame, RecordMixin): def __init__( self, - shape: Tuple[int, int], - cameras: Union[str, list, np.ndarray] = "2d", - controller_types: Union[str, list, np.ndarray] = None, - controller_ids: Union[str, list, np.ndarray] = None, - canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, + shape: tuple[int, int], + cameras: ( + Literal["2d", "3d"] + | list[Literal["2d", "3d"]] + | list[pygfx.PerspectiveCamera] + | np.ndarray + ) = "2d", + controller_types: ( + Literal["panzoom", "fly", "trackball", "orbit"] + | list[Literal["panzoom", "fly", "trackball", "orbit"]] + | np.ndarray + ) = None, + controller_ids: str | list[int] | np.ndarray | list[list[str]] = None, + canvas: str | WgpuCanvasBase | pygfx.Texture = None, renderer: pygfx.WgpuRenderer = None, - size: Tuple[int, int] = (500, 300), - names: Union[list, np.ndarray] = None, + size: tuple[int, int] = (500, 300), + names: list | np.ndarray = None, ): """ A grid of subplots. @@ -35,27 +44,31 @@ def __init__( shape: (int, int) (n_rows, n_cols) - cameras: str, list, or np.ndarray, optional - | One of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots + cameras: "2d", "3", list of "2d" | "3d", list of camera instances, or np.ndarray of "2d" | "3d", optional + | if str, one of ``"2d"`` or ``"3d"`` indicating 2D or 3D cameras for all subplots | list/array of ``2d`` and/or ``3d`` that specifies the camera type for each subplot | list/array of pygfx.PerspectiveCamera instances controller_types: str, list or np.ndarray, optional list or array that specifies the controller type for each subplot, or list/array of - pygfx.Controller instances + pygfx.Controller instances. Valid controller types: "panzoom", "fly", "trackball", "orbit". + If not specified a default controller is chosen based on the camera type. + Orthographic projections, i.e. "2d" cameras, use a "panzoom" controller by default. + Perspective projections with a FOV > 0, i.e. "3d" cameras, use a "fly" controller by default. - controller_ids: str, list or np.ndarray of int or str ids, optional + + controller_ids: str, list of int, np.ndarray of int, or list with sublists of subplot str names, optional | If `None` a unique controller is created for each subplot | If "sync" all the subplots use the same controller - | If ``numpy.array``, its shape must be the same as ``grid_shape``. + | If array/list it must be reshapeable to ``grid_shape``. This allows custom assignment of controllers | Example with integers: | sync first 2 plots, and sync last 2 plots: [[0, 0, 1], [2, 3, 3]] | Example with str subplot names: - | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b], [subplot_f, subplot_c]] - | this syncs subplot_a and subplot_b together; syncs subplot_f and subplot_c together + | list of lists of subplot names, each sublist is synced: [[subplot_a, subplot_b, subplot_e], [subplot_c, subplot_d]] + | this syncs subplot_a, subplot_b and subplot_e together; syncs subplot_c and subplot_d together canvas: WgpuCanvas, optional Canvas for drawing @@ -249,8 +262,8 @@ def __init__( name=name, ) - self._animate_funcs_pre: List[callable] = list() - self._animate_funcs_post: List[callable] = list() + self._animate_funcs_pre: list[callable] = list() + self._animate_funcs_post: list[callable] = list() self._current_iter = None @@ -269,12 +282,12 @@ def renderer(self) -> pygfx.WgpuRenderer: """The renderer associated to this GridPlot""" return self._renderer - def __getitem__(self, index: Union[Tuple[int, int], str]) -> Subplot: + def __getitem__(self, index: tuple[int, int] | str) -> Subplot: if isinstance(index, str): for subplot in self._subplots.ravel(): if subplot.name == index: return subplot - raise IndexError("no subplot with given name") + raise IndexError(f"no subplot with given name: {index}") else: return self._subplots[index[0], index[1]] @@ -291,7 +304,7 @@ def render(self): # call post-render animate functions self._call_animate_functions(self._animate_funcs_post) - def _call_animate_functions(self, funcs: Iterable[callable]): + def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: if len(getfullargspec(fn).args) > 0: @@ -307,7 +320,7 @@ def _call_animate_functions(self, funcs: Iterable[callable]): def add_animations( self, - *funcs: Iterable[callable], + *funcs: callable, pre_render: bool = True, post_render: bool = False, ): @@ -317,7 +330,7 @@ def add_animations( Parameters ---------- - *funcs: callable or iterable of callable + *funcs: callable(s) function(s) that are called on each render cycle pre_render: bool, default ``True``, optional keyword-only argument diff --git a/fastplotlib/layouts/_plot_area.py b/fastplotlib/layouts/_plot_area.py index 2c93d7e9e..299bc6e5d 100644 --- a/fastplotlib/layouts/_plot_area.py +++ b/fastplotlib/layouts/_plot_area.py @@ -1,5 +1,5 @@ from inspect import getfullargspec -from typing import * +from typing import TypeAlias, Literal, Union import weakref from warnings import warn @@ -9,7 +9,7 @@ from pylinalg import vec_transform, vec_unproject from wgpu.gui import WgpuCanvasBase -from ._utils import create_camera, create_controller +from ._utils import create_controller from ..graphics._base import Graphic from ..graphics.selectors._base_selector import BaseSelector from ..legends import Legend @@ -17,17 +17,18 @@ # dict to store Graphic instances # this is the only place where the real references to Graphics are stored in a Python session # {hex id str: Graphic} -GRAPHICS: Dict[str, Graphic] = dict() -SELECTORS: Dict[str, BaseSelector] = dict() +HexStr: TypeAlias = str +GRAPHICS: dict[HexStr, Graphic] = dict() +SELECTORS: dict[HexStr, BaseSelector] = dict() class PlotArea: def __init__( self, - parent, - position: Any, - camera: Union[pygfx.PerspectiveCamera], - controller: Union[pygfx.Controller], + parent: Union["PlotArea", "GridPlot"], + position: tuple[int, int] | str, + camera: pygfx.PerspectiveCamera, + controller: pygfx.Controller, scene: pygfx.Scene, canvas: WgpuCanvasBase, renderer: pygfx.WgpuRenderer, @@ -39,18 +40,18 @@ def __init__( Parameters ---------- - parent: PlotArea - parent class of subclasses will be a ``PlotArea`` instance + parent: PlotArea or GridPlot + parent object position: Any - typical use will be for ``subplots`` in a ``gridplot``, position would correspond to the ``[row, column]`` - location of the ``subplot`` in its ``gridplot`` + position of the plot area. In a ``subplot`` position would correspond to the ``[row, column]`` + index of the ``subplot``. In docks this would correspond to a str name, "top", "right", "bottom" or "left" camera: pygfx.PerspectiveCamera - Use perspective camera for both perspective and orthographic views. Set fov = 0 for orthographic mode. + Use perspective camera for both perspective and orthographic views. Set fov = 0 for orthographic projection controller: pygfx.Controller - One of the pygfx controllers, panzoom, fly, orbit, or trackball + One of the pygfx controllers: "panzoom", "fly", "trackball", "orbit" scene: pygfx.Scene represents the root of a scene graph, will be viewed by the given ``camera`` @@ -62,20 +63,17 @@ def __init__( renders the scene onto the canvas name: str, optional - name this ``subplot`` or ``plot`` + name this plot area """ - self._parent: PlotArea = parent + self._parent = parent self._position = position self._scene = scene self._canvas = canvas self._renderer = renderer - if parent is None: - self._viewport: pygfx.Viewport = pygfx.Viewport(renderer) - else: - self._viewport = pygfx.Viewport(parent.renderer) + self._viewport: pygfx.Viewport = pygfx.Viewport(renderer) self._camera = camera self._controller = controller @@ -85,18 +83,18 @@ def __init__( self.viewport, ) - self._animate_funcs_pre = list() - self._animate_funcs_post = list() + self._animate_funcs_pre: list[callable] = list() + self._animate_funcs_post: list[callable] = list() self.renderer.add_event_handler(self.set_viewport_rect, "resize") # list of hex id strings for all graphics managed by this PlotArea # the real Graphic instances are stored in the ``GRAPHICS`` dict - self._graphics: List[str] = list() + self._graphics: list[str] = list() # selectors are in their own list so they can be excluded from scene bbox calculations # managed similar to GRAPHICS for garbage collection etc. - self._selectors: List[str] = list() + self._selectors: list[str] = list() self._name = name @@ -108,11 +106,11 @@ def __init__( # several read-only properties @property def parent(self): - """A parent if relevant, used by individual Subplots in GridPlot""" + """A parent if relevant""" return self._parent @property - def position(self) -> Union[Tuple[int, int], Any]: + def position(self) -> tuple[int, int] | str: """Position of this plot area within a larger layout (such as GridPlot) if relevant""" return self._position @@ -142,7 +140,7 @@ def camera(self) -> pygfx.PerspectiveCamera: return self._camera @camera.setter - def camera(self, new_camera: Union[str, pygfx.PerspectiveCamera]): + def camera(self, new_camera: str | pygfx.PerspectiveCamera): # user wants to set completely new camera, remove current camera from controller if isinstance(new_camera, pygfx.PerspectiveCamera): self.controller.remove_camera(self._camera) @@ -178,7 +176,7 @@ def controller(self) -> pygfx.Controller: return self._controller @controller.setter - def controller(self, new_controller: Union[str, pygfx.Controller]): + def controller(self, new_controller: str | pygfx.Controller): new_controller = create_controller(new_controller, self._camera) cameras_list = list() @@ -206,7 +204,7 @@ def controller(self, new_controller: Union[str, pygfx.Controller]): self._controller = new_controller @property - def graphics(self) -> Tuple[Graphic, ...]: + def graphics(self) -> tuple[Graphic, ...]: """Graphics in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._graphics: @@ -218,7 +216,7 @@ def graphics(self) -> Tuple[Graphic, ...]: return tuple(proxies) @property - def selectors(self) -> Tuple[BaseSelector, ...]: + def selectors(self) -> tuple[BaseSelector, ...]: """Selectors in the plot area. Always returns a proxy to the Graphic instances.""" proxies = list() for loc in self._selectors: @@ -228,7 +226,7 @@ def selectors(self) -> Tuple[BaseSelector, ...]: return tuple(proxies) @property - def legends(self) -> Tuple[Legend, ...]: + def legends(self) -> tuple[Legend, ...]: """Legends in the plot area.""" proxies = list() for loc in self._graphics: @@ -253,7 +251,7 @@ def name(self, name: str): raise TypeError("PlotArea `name` must be of type ") self._name = name - def get_rect(self) -> Tuple[float, float, float, float]: + def get_rect(self) -> tuple[float, float, float, float]: """ Returns the viewport rect to define the rectangle occupied by the viewport w.r.t. the Canvas. @@ -267,7 +265,7 @@ def get_rect(self) -> Tuple[float, float, float, float]: raise NotImplementedError("Must be implemented in subclass") def map_screen_to_world( - self, pos: Union[Tuple[float, float], pygfx.PointerEvent] + self, pos: tuple[float, float] | pygfx.PointerEvent ) -> np.ndarray: """ Map screen position to world position @@ -316,7 +314,7 @@ def render(self): self._call_animate_functions(self._animate_funcs_post) - def _call_animate_functions(self, funcs: Iterable[callable]): + def _call_animate_functions(self, funcs: list[callable]): for fn in funcs: try: args = getfullargspec(fn).args @@ -337,7 +335,7 @@ def _call_animate_functions(self, funcs: Iterable[callable]): def add_animations( self, - *funcs: Iterable[callable], + *funcs: callable, pre_render: bool = True, post_render: bool = False, ): @@ -347,7 +345,7 @@ def add_animations( Parameters ---------- - *funcs: callable or iterable of callable + *funcs: callable(s) function(s) that are called on each render cycle pre_render: bool, default ``True``, optional keyword-only argument @@ -460,7 +458,7 @@ def _add_or_insert_graphic( self, graphic: Graphic, center: bool = True, - action: str = Union["insert", "add"], + action: str = Literal["insert", "add"], index: int = 0, ): """Private method to handle inserting or adding a graphic to a PlotArea.""" @@ -570,7 +568,7 @@ def center_scene(self, *, zoom: float = 1.35): def auto_scale( self, *, # since this is often used as an event handler, don't want to coerce maintain_aspect = True - maintain_aspect: Union[None, bool] = None, + maintain_aspect: None | bool = None, zoom: float = 0.8, ): """ @@ -650,6 +648,7 @@ def delete_graphic(self, graphic: Graphic): """ # TODO: proper gc of selectors, RAM is freed for regular graphics but not selectors # TODO: references to selectors must be lingering somewhere + # TODO: update March 2024, I think selectors are gc properly, should check # get location loc = graphic.loc @@ -718,7 +717,7 @@ def __getitem__(self, name: str): f"The current selectors are:\n {selector_names}" ) - def __contains__(self, item: Union[str, Graphic]): + def __contains__(self, item: str | Graphic): to_check = [*self.graphics, *self.selectors, *self.legends] if isinstance(item, Graphic): diff --git a/fastplotlib/layouts/_record_mixin.py b/fastplotlib/layouts/_record_mixin.py index e3bfdeba5..59a8e92e4 100644 --- a/fastplotlib/layouts/_record_mixin.py +++ b/fastplotlib/layouts/_record_mixin.py @@ -1,4 +1,3 @@ -from typing import * from pathlib import Path from multiprocessing import Queue, Process from time import time @@ -21,7 +20,7 @@ class VideoWriterAV(Process): def __init__( self, - path: Union[Path, str], + path: Path | str, queue: Queue, fps: int, width: int, @@ -115,7 +114,7 @@ def _record(self): def record_start( self, - path: Union[str, Path], + path: str | Path, fps: int = 25, codec: str = "mpeg4", pixel_format: str = "yuv420p", diff --git a/fastplotlib/layouts/_subplot.py b/fastplotlib/layouts/_subplot.py index 509840fa7..4b1e92c51 100644 --- a/fastplotlib/layouts/_subplot.py +++ b/fastplotlib/layouts/_subplot.py @@ -1,4 +1,4 @@ -from typing import * +from typing import Literal, Union import numpy as np @@ -9,18 +9,22 @@ from ..graphics import TextGraphic from ._utils import make_canvas_and_renderer, create_camera, create_controller from ._plot_area import PlotArea -from .graphic_methods_mixin import GraphicMethodsMixin +from ._graphic_methods_mixin import GraphicMethodsMixin class Subplot(PlotArea, GraphicMethodsMixin): def __init__( self, - parent: Any = None, - position: Tuple[int, int] = None, - parent_dims: Tuple[int, int] = None, - camera: Union[str, pygfx.PerspectiveCamera] = "2d", - controller: Union[str, pygfx.Controller] = None, - canvas: Union[str, WgpuCanvasBase, pygfx.Texture] = None, + parent: Union["GridPlot", None] = None, + position: tuple[int, int] = None, + parent_dims: tuple[int, int] = None, + camera: Literal["2d", "3d"] | pygfx.PerspectiveCamera = "2d", + controller: ( + Literal["panzoom", "fly", "trackball", "orbit"] | pygfx.Controller + ) = None, + canvas: ( + Literal["glfw", "jupyter", "qt", "wx"] | WgpuCanvasBase | pygfx.Texture + ) = None, renderer: pygfx.WgpuRenderer = None, name: str = None, ): @@ -33,7 +37,7 @@ def __init__( Parameters ---------- - parent: Any + parent: 'GridPlot' | None parent GridPlot instance position: (int, int), optional @@ -51,7 +55,7 @@ def __init__( | if ``str``, must be one of: `"panzoom", "fly", "trackball", or "orbit"`. | also accepts a pygfx.Controller instance - canvas: one of "jupyter", "glfw", "qt", WgpuCanvas, or pygfx.Texture, optional + canvas: one of "jupyter", "glfw", "qt", "ex, a WgpuCanvas, or a pygfx.Texture, optional Provides surface on which a scene will be rendered. Can optionally provide a WgpuCanvas instance or a str to force the PlotArea to use a specific canvas from one of the following options: "jupyter", "glfw", "qt". Can also provide a pygfx Texture to render to. @@ -113,11 +117,11 @@ def __init__( self.set_title(self.name) @property - def name(self) -> Any: + def name(self) -> str: return self._name @name.setter - def name(self, name: Any): + def name(self, name: str): self._name = name self.set_title(name) @@ -136,7 +140,7 @@ def docks(self) -> dict: """ return self._docks - def set_title(self, text: Any): + def set_title(self, text: str): """Sets the plot title, stored as a ``TextGraphic`` in the "top" dock area""" if text is None: return diff --git a/fastplotlib/layouts/_utils.py b/fastplotlib/layouts/_utils.py index 5ee930b67..6994838d5 100644 --- a/fastplotlib/layouts/_utils.py +++ b/fastplotlib/layouts/_utils.py @@ -1,15 +1,14 @@ -from typing import * import importlib import pygfx -from pygfx import WgpuRenderer, Texture +from pygfx import WgpuRenderer, Texture, Renderer from wgpu.gui import WgpuCanvasBase from ..utils import gui def make_canvas_and_renderer( - canvas: Union[str, WgpuCanvasBase, Texture, None], renderer: [WgpuRenderer, None] + canvas: str | WgpuCanvasBase | Texture | None, renderer: Renderer | None ): """ Parses arguments and returns the appropriate canvas and renderer instances @@ -22,19 +21,23 @@ def make_canvas_and_renderer( m = importlib.import_module("wgpu.gui." + canvas) canvas = m.WgpuCanvas(max_fps=60) elif not isinstance(canvas, (WgpuCanvasBase, Texture)): - raise ValueError( + raise TypeError( f"canvas option must either be a valid WgpuCanvas implementation, a pygfx Texture" f" or a str with the wgpu gui backend name." ) if renderer is None: renderer = WgpuRenderer(canvas, pixel_ratio=2) + elif not isinstance(renderer, Renderer): + raise TypeError( + f"renderer option must be a pygfx.Renderer instance such as pygfx.WgpuRenderer" + ) return canvas, renderer def create_camera( - camera_type: Union[pygfx.PerspectiveCamera, str], + camera_type: pygfx.PerspectiveCamera | str, ) -> pygfx.PerspectiveCamera: if isinstance(camera_type, pygfx.PerspectiveCamera): return camera_type @@ -61,7 +64,7 @@ def create_camera( def create_controller( - controller_type: Union[pygfx.Controller, None, str], + controller_type: pygfx.Controller | None | str, camera: pygfx.PerspectiveCamera, ) -> pygfx.Controller: """ diff --git a/fastplotlib/legends/legend.py b/fastplotlib/legends/legend.py index 291c25ff3..be90004aa 100644 --- a/fastplotlib/legends/legend.py +++ b/fastplotlib/legends/legend.py @@ -1,6 +1,6 @@ from functools import partial from collections import OrderedDict -from typing import * +from typing import Iterable import numpy as np import pygfx @@ -31,7 +31,7 @@ def __init__( class LineLegendItem(LegendItem): def __init__( - self, parent, graphic: LineGraphic, label: str, position: Tuple[int, int] + self, parent, graphic: LineGraphic, label: str, position: tuple[int, int] ): """ @@ -142,7 +142,7 @@ class Legend(Graphic): def __init__( self, plot_area, - highlight_color: Union[str, tuple, np.ndarray] = "w", + highlight_color: str | tuple | np.ndarray = "w", max_rows: int = 5, *args, **kwargs, @@ -161,7 +161,7 @@ def __init__( maximum number of rows allowed in the legend """ - self._graphics: List[Graphic] = list() + self._graphics: list[Graphic] = list() # hex id of Graphic, i.e. graphic.loc are the keys self._items: OrderedDict[str:LegendItem] = OrderedDict() @@ -204,7 +204,7 @@ def __init__( self._row_counter = 0 self._col_counter = 0 - def graphics(self) -> Tuple[Graphic, ...]: + def graphics(self) -> tuple[Graphic, ...]: return tuple(self._graphics) def _check_label_unique(self, label): @@ -235,7 +235,7 @@ def add_graphic(self, graphic: Graphic, label: str = None): # get x position offset for this new column of LegendItems # start by getting the LegendItems in the previous column - prev_column_items: List[LegendItem] = list(self._items.values())[ + prev_column_items: list[LegendItem] = list(self._items.values())[ -self._max_rows : ] # x position of LegendItems in previous column diff --git a/fastplotlib/utils/functions.py b/fastplotlib/utils/functions.py index 8d1e8694f..da781b521 100644 --- a/fastplotlib/utils/functions.py +++ b/fastplotlib/utils/functions.py @@ -1,5 +1,4 @@ from collections import OrderedDict -from typing import * from pathlib import Path import numpy as np @@ -165,7 +164,7 @@ def make_colors_dict(labels: iter, cmap: str, **kwargs) -> OrderedDict: return OrderedDict(zip(labels, colors)) -def quick_min_max(data: np.ndarray) -> Tuple[float, float]: +def quick_min_max(data: np.ndarray) -> tuple[float, float]: """ Adapted from pyqtgraph.ImageView. Estimate the min/max values of *data* by subsampling. @@ -220,7 +219,7 @@ def make_pygfx_colors(colors, n_colors): return colors_array -def calculate_gridshape(n_subplots: int) -> Tuple[int, int]: +def calculate_gridshape(n_subplots: int) -> tuple[int, int]: """ Returns ``(n_rows, n_cols)`` from given number of subplots ``n_subplots`` """ @@ -240,7 +239,7 @@ def normalize_min_max(a): def parse_cmap_values( n_colors: int, cmap_name: str, - cmap_values: Union[np.ndarray, List[Union[int, float]]] = None, + cmap_values: np.ndarray | list[int | float] = None, ) -> np.ndarray: """ diff --git a/fastplotlib/widgets/histogram_lut.py b/fastplotlib/widgets/histogram_lut.py index 31f6ab8e9..43f2b48b3 100644 --- a/fastplotlib/widgets/histogram_lut.py +++ b/fastplotlib/widgets/histogram_lut.py @@ -1,4 +1,3 @@ -from typing import * import weakref import numpy as np @@ -112,7 +111,7 @@ def __init__( self.image_graphic.cmap.add_event_handler(self._image_cmap_handler) - def _get_vmin_vmax_str(self) -> Tuple[str, str]: + def _get_vmin_vmax_str(self) -> tuple[str, str]: if self.vmin < 0.001 or self.vmin > 99_999: vmin_str = f"{self.vmin:.2e}" else: diff --git a/fastplotlib/utils/generate_add_methods.py b/scripts/generate_add_graphic_methods.py similarity index 97% rename from fastplotlib/utils/generate_add_methods.py rename to scripts/generate_add_graphic_methods.py index 100ad7757..2a480d884 100644 --- a/fastplotlib/utils/generate_add_methods.py +++ b/scripts/generate_add_graphic_methods.py @@ -4,7 +4,7 @@ import black root = pathlib.Path(__file__).parent.parent.resolve() -filename = root.joinpath("layouts/graphic_methods_mixin.py") +filename = root.joinpath("fastplotlib", "layouts", "_graphic_methods_mixin.py") # if there is an existing mixin class, replace it with an empty class # so that fastplotlib will import diff --git a/setup.py b/setup.py index e8f2613d9..b50a6a9bf 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ license="Apache 2.0", author="Kushal Kolar, Caitlin Lewis", author_email="", - python_requires=">=3.9", + python_requires=">=3.10", install_requires=install_requires, extras_require=extras_require, include_package_data=True, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy