Source code for plotpy.interfaces.items

# -*- coding: utf-8 -*-
#
# Licensed under the terms of the BSD 3-Clause
# (see plotpy/LICENSE for details)

"""
plotpy.widget.interfaces
-----------------------------

The `interfaces` module provides object interface classes for :mod:`plotpy`.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:
    import guidata.io
    from qtpy.QtCore import QPointF

    from plotpy.styles.base import ItemParameters


[docs] class IItemType: """Item types are used to categorized items in a broader way than objects obeying IBasePlotItem. The necessity arises from the fact that Plotpy Items can inherit from different base classes and still provide functionalities akin to a given ItemType the types() method of an item returns a list of interfaces this item supports """
class ITrackableItemType(IItemType): def get_closest_coordinates(self, x: float, y: float) -> tuple[float, float]: """ Get the closest coordinates to the given point Args: x: X coordinate y: Y coordinate Returns: tuple[float, float]: Closest coordinates """ def get_coordinates_label(self, x: float, y: float) -> str: """ Get the coordinates label for the given coordinates Args: x: X coordinate y: Y coordinate Returns: str: Coordinates label """ class IDecoratorItemType(IItemType): """represents a decorative item (usually not active) such as grid, or axes markers""" class ICurveItemType(IItemType): """A curve""" class IImageItemType(IItemType): """An image""" class IVoiImageItemType(IItemType): """An image with set_lut_range, get_lut_range""" def set_lut_range(self, lut_range: tuple[float, float]) -> None: """ Set the current active lut range Args: lut_range: Lut range, tuple(min, max) Example: >>> item.set_lut_range((0.0, 1.0)) """ def get_lut_range(self) -> tuple[float, float]: """Get the current active lut range Returns: tuple[float, float]: Lut range, tuple(min, max) """ return 0.0, 1.0 def get_lut_range_full(self) -> tuple[float, float]: """Return full dynamic range Returns: tuple[float, float]: Lut range, tuple(min, max) """ return 10.0, 20.0 def get_lut_range_max(self) -> tuple[float, float]: """Get maximum range for this dataset Returns: tuple[float, float]: Lut range, tuple(min, max) """ return 0.0, 255.0
[docs] class IColormapImageItemType(IItemType): """An image with an associated colormap"""
class IExportROIImageItemType(IItemType): """An image with export_roi""" def export_roi( self, src_rect: tuple[float, float, float, float], dst_rect: tuple[float, float, float, float], dst_image: np.ndarray, apply_lut: bool = False, apply_interpolation: bool = False, original_resolution: bool = False, force_interp_mode: str | None = None, force_interp_size: int | None = None, ) -> None: """ Export a rectangular area of the image to another image Args: src_rect: Source rectangle dst_rect: Destination rectangle dst_image: Destination image apply_lut: Apply lut (Default value = False) apply_interpolation: Apply interpolation (Default value = False) original_resolution: Original resolution (Default value = False) force_interp_mode: Force interpolation mode (Default value = None) force_interp_size: Force interpolation size (Default value = None) """ pass class ICSImageItemType(IItemType): """An image supporting X/Y cross sections""" def get_xsection(self, y0: float | int, apply_lut: bool = False) -> np.ndarray: """Return cross section along x-axis at y=y0 Args: y0: Y0 apply_lut: Apply lut (Default value = False) Returns: np.ndarray: Cross section along x-axis at y=y0 """ def get_ysection(self, x0: float | int, apply_lut: bool = False) -> np.ndarray: """Return cross section along y-axis at x=x0 Args: x0: X0 apply_lut: Apply lut (Default value = False) Returns: np.ndarray: Cross section along y-axis at x=x0 """ def get_average_xsection( self, x0: float, y0: float, x1: float, y1: float, apply_lut: bool = False ) -> np.ndarray: """Return average cross section along x-axis for the given rectangle Args: x0: X0 of top left corner y0: Y0 of top left corner x1: X1 of bottom right corner y1: Y1 of bottom right corner apply_lut: Apply lut (Default value = False) Returns: np.ndarray: Average cross section along x-axis """ def get_average_ysection( self, x0: float, y0: float, x1: float, y1: float, apply_lut: bool = False ) -> np.ndarray: """Return average cross section along y-axis Args: x0: X0 of top left corner y0: Y0 of top left corner x1: X1 of bottom right corner y1: Y1 of bottom right corner apply_lut: Apply lut (Default value = False) Returns: np.ndarray: Average cross section along y-axis """ class IShapeItemType(IItemType): """A shape (annotation)""" pass class ISerializableType(IItemType): """An item that can be serialized""" def serialize( self, writer: guidata.io.HDF5Writer | guidata.io.INIWriter | guidata.io.JSONWriter, ) -> None: """Serialize object to HDF5 writer Args: writer: HDF5, INI or JSON writer """ def deserialize( self, reader: guidata.io.HDF5Reader | guidata.io.INIReader | guidata.io.JSONReader, ) -> None: """Deserialize object from HDF5 reader Args: reader: HDF5, INI or JSON reader """
[docs] class IBasePlotItem: """ This is the interface that QwtPlotItem objects must implement to be handled by *BasePlot* widgets """ selected = False # True if this item is selected _readonly = False _private = False _can_select = True # Indicate this item can be selected _can_move = True _can_resize = True _can_rotate = True _icon_name = "" # Icon name (e.g. "point.png") def get_icon_name(self) -> str: """Return the icon name Returns: Icon name """ return self._icon_name def set_icon_name(self, icon_name: str) -> None: """Set the icon name Args: icon_name: Icon name """ self._icon_name = icon_name def set_selectable(self, state: bool) -> None: """Set item selectable state Args: state: True if item is selectable, False otherwise """ self._can_select = state def set_resizable(self, state: bool) -> None: """Set item resizable state (or any action triggered when moving an handle, e.g. rotation) Args: state: True if item is resizable, False otherwise """ self._can_resize = state def set_movable(self, state: bool) -> None: """Set item movable state Args: state: True if item is movable, False otherwise """ self._can_move = state def set_rotatable(self, state: bool) -> None: """Set item rotatable state Args: state: True if item is rotatable, False otherwise """ self._can_rotate = state def can_select(self) -> bool: """ Returns True if this item can be selected Returns: bool: True if item can be selected, False otherwise """ return self._can_select def can_resize(self) -> bool: """ Returns True if this item can be resized Returns: bool: True if item can be resized, False otherwise """ return self._can_resize def can_move(self) -> bool: """ Returns True if this item can be moved Returns: bool: True if item can be moved, False otherwise """ return self._can_move def can_rotate(self) -> bool: """ Returns True if this item can be rotated Returns: bool: True if item can be rotated, False otherwise """ return self._can_rotate def types(self) -> tuple[type[IItemType], ...]: """Returns a group or category for this item. This should be a tuple of class objects inheriting from IItemType Returns: tuple: Tuple of class objects inheriting from IItemType """ def set_readonly(self, state: bool) -> None: """Set object readonly state Args: state: True if object is readonly, False otherwise """ self._readonly = state def is_readonly(self) -> bool: """Return object readonly state Returns: bool: True if object is readonly, False otherwise """ return self._readonly def set_private(self, state: bool) -> None: """Set object as private Args: state: True if object is private, False otherwise """ self._private = state def is_private(self) -> bool: """Return True if object is private Returns: bool: True if object is private, False otherwise """ return self._private def select(self) -> None: """ Select the object and eventually change its appearance to highlight the fact that it's selected """ # should call plot.invalidate() or replot to force redraw def unselect(self) -> None: """ Unselect the object and eventually restore its original appearance to highlight the fact that it's not selected anymore """ # should call plot.invalidate() or replot to force redraw def hit_test(self, pos: QPointF) -> tuple[float, float, bool, None]: """Return a tuple (distance, attach point, inside, other_object) Args: pos: Position Returns: tuple: Tuple with four elements: (distance, attach point, inside, other_object). Description of the returned values: * distance: distance in pixels (canvas coordinates) to the closest attach point * attach point: handle of the attach point * inside: True if the mouse button has been clicked inside the object * other_object: if not None, reference of the object which will be considered as hit instead of self """ def update_item_parameters(self) -> None: """Update item parameters (dataset) from object properties""" def get_item_parameters(self, itemparams: ItemParameters) -> None: """ Appends datasets to the list of DataSets describing the parameters used to customize apearance of this item Args: itemparams: Item parameters """ def set_item_parameters(self, itemparams: ItemParameters) -> None: """ Change the appearance of this item according to the parameter set provided Args: itemparams: Item parameters """ def move_local_point_to(self, handle: int, pos: QPointF, ctrl: bool = None) -> None: """Move a handle as returned by hit_test to the new position Args: handle: Handle pos: Position ctrl: True if <Ctrl> button is being pressed, False otherwise """ def move_local_shape(self, old_pos: QPointF, new_pos: QPointF) -> None: """Translate the shape such that old_pos becomes new_pos in canvas coordinates Args: old_pos: Old position new_pos: New position """ def move_with_selection(self, delta_x: float, delta_y: float) -> None: """Translate the item together with other selected items Args: delta_x: Translation in plot coordinates along x-axis delta_y: Translation in plot coordinates along y-axis """
class IBaseImageItem: """ QwtPlotItem image objects handled by *BasePlot* widgets must implement _both_ the IBasePlotItem interface and this one """ _can_sethistogram = False # A levels histogram will be bound to image def can_sethistogram(self) -> bool: """ Returns True if this item can be associated with a levels histogram Returns: bool: True if item can be associated with a levels histogram, False otherwise """ return self._can_sethistogram class IHistDataSource: def get_histogram( self, nbins: int, drange: tuple[float, float] | None = None ) -> tuple[np.ndarray, np.ndarray]: """ Return a tuple (hist, bins) where hist is a list of histogram values Args: nbins: number of bins drange: lower and upper range of the bins. If not provided, range is simply (data.min(), data.max()). Values outside the range are ignored. Returns: Tuple (hist, bins) Example of implementation: def get_histogram(self, nbins, drange=None): data = self.get_data() return np.histogram(data, bins=nbins, range=drange) """ pass