from typing import Union, TYPE_CHECKING, Iterable
from pymodaq.utils.enums import BaseEnum
from pyqtgraph.graphicsItems import InfiniteLine, ROI
from qtpy import QtWidgets
from qtpy.QtCore import QObject, Signal, QRectF
from pymodaq.utils.data import DataToExport, DataWithAxes, DataDim, DataDistribution
from pymodaq.utils.exceptions import ViewerError
from pymodaq.utils.plotting.utils.plot_utils import RoiInfo
if TYPE_CHECKING:
from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
from pymodaq.utils.plotting.data_viewers.viewerND import ViewerND
class ViewersEnum(BaseEnum):
"""enum relating a given viewer with data type"""
Viewer0D = 'Data0D'
Viewer1D = 'Data1D'
Viewer2D = 'Data2D'
ViewerND = 'DataND'
ViewerSequential = 'DataSequential'
def get_dim(self):
return self.value.split('Data')[1].split('D')[0]
def increase_dim(self, ndim: int):
dim = self.get_dim()
if dim != 'N':
dim_as_int = int(dim) + ndim
if dim_as_int > 2:
dim = 'N'
else:
dim = str(dim_as_int)
else:
dim = 'N'
return ViewersEnum[f'Viewer{dim}D']
@classmethod
def from_n_axes(cls, n_axes: int):
if n_axes == 0:
return ViewersEnum['Viewer0D']
elif n_axes == 1:
return ViewersEnum['Viewer1D']
elif n_axes == 2:
return ViewersEnum['Viewer2D']
elif n_axes > 2:
return ViewersEnum['ViewerND']
@staticmethod
def get_viewers_enum_from_metadata(dim: DataDim,
distribution: DataDistribution,
n_nav_axes: int,
n_sig_indexes: int,
shape_len: int,
size: int) -> 'ViewersEnum':
if dim.name == 'Data0D':
viewer = 'Viewer0D'
elif dim.name == 'Data1D':
viewer = 'Viewer1D'
elif dim.name == 'Data2D':
viewer = 'Viewer2D'
else:
if distribution.name == 'uniform':
if shape_len < 3:
if shape_len == 1 and size == 1:
viewer = 'Viewer0D'
elif shape_len == 1 and size > 1:
viewer = 'Viewer1D'
elif shape_len == 2:
viewer = 'Viewer2D'
else:
viewer = 'ViewerND'
else:
viewer = 'ViewerND'
else:
if n_sig_indexes == 0:
if n_nav_axes == 1:
viewer = 'Viewer1D'
elif n_nav_axes == 2:
viewer = 'Viewer2D'
else:
viewer = 'ViewerND'
else:
viewer = 'ViewerND'
return ViewersEnum[viewer]
@staticmethod
def get_viewers_enum_from_data(dwa: DataWithAxes) -> 'ViewersEnum':
if dwa.dim.name == 'Data0D':
viewer = 'Viewer0D'
elif dwa.dim.name == 'Data1D':
viewer = 'Viewer1D'
elif dwa.dim.name == 'Data2D':
viewer = 'Viewer2D'
else:
if dwa.distribution.name == 'uniform':
if len(dwa.shape) < 3:
dwa.nav_indexes = ()
if len(dwa.shape) == 1 and dwa.size == 1:
viewer = 'Viewer0D'
elif len(dwa.shape) == 1 and dwa.size > 1:
viewer = 'Viewer1D'
elif len(dwa.shape) == 2:
viewer = 'Viewer2D'
else:
viewer = 'ViewerND'
else:
viewer = 'ViewerND'
else:
if len(dwa.sig_indexes) == 0:
if len(dwa.get_nav_axes()) == 1:
viewer = 'Viewer1D'
elif len(dwa.get_nav_axes()) == 2:
viewer = 'Viewer2D'
else:
viewer = 'ViewerND'
else:
viewer = 'ViewerND'
return ViewersEnum[viewer]
[docs]class ViewerBase(QObject):
"""Base Class for data viewers implementing all common functionalities
Parameters
----------
parent: QtWidgets.QWidget
title: str
Attributes
----------
view: QObject
Ui interface of the viewer
data_to_export_signal: Signal[DataToExport]
ROI_changed: Signal
crosshair_dragged: Signal[float, float]
crosshair_clicked: Signal[bool]
sig_double_clicked: Signal[float, float]
status_signal: Signal[str]
"""
data_to_export_signal = Signal(DataToExport)
_data_to_show_signal = Signal(DataWithAxes)
ROI_changed = Signal()
crosshair_dragged = Signal(float, float) # Crosshair position in units of scaled top/right axes
status_signal = Signal(str)
crosshair_clicked = Signal(bool)
sig_double_clicked = Signal(float, float)
ROI_select_signal = Signal(QRectF) # deprecated: use roi_select_signal
roi_select_signal = Signal(RoiInfo)
def __init__(self, parent: QtWidgets.QWidget = None, title=''):
super().__init__()
self.title = title if title != '' else self.__class__.__name__
self._raw_data = None
self.data_to_export: DataToExport = DataToExport(name=self.title)
self.view: Union[Viewer0D, Viewer1D, Viewer2D, ViewerND] = None
if parent is None:
parent = QtWidgets.QWidget()
parent.show()
self.parent = parent
self._display_temporary = False
@property
def has_action(self):
"""Convenience method"""
if hasattr(self.view, 'has_action'):
return self.view.has_action
@property
def is_action_checked(self):
"""Convenience method"""
if hasattr(self.view, 'is_action_checked'):
return self.view.is_action_checked
@property
def is_action_visible(self):
"""Convenience method"""
if hasattr(self.view, 'is_action_visible'):
return self.view.is_action_visible
@property
def set_action_checked(self):
"""Convenience method"""
if hasattr(self.view, 'set_action_checked'):
return self.view.set_action_checked
@property
def set_action_visible(self):
"""Convenience method"""
if hasattr(self.view, 'set_action_visible'):
return self.view.set_action_visible
@property
def get_action(self):
"""Convenience method"""
if hasattr(self.view, 'get_action'):
return self.view.get_action
@property
def toolbar(self):
"""Convenience property"""
if hasattr(self.view, 'toolbar'):
return self.view.toolbar
@property
def viewer_type(self):
"""str: the viewer data type see DATA_TYPES"""
return ViewersEnum[self.__class__.__name__].value
[docs] def show_data(self, data: DataWithAxes, **kwargs):
"""Entrypoint to display data into the viewer
Parameters
----------
data: data_mod.DataFromPlugins
"""
if len(data.shape) > 4:
raise ViewerError(f'Ndarray of dim: {len(data.shape)} cannot be plotted using a {self.viewer_type}')
self.data_to_export = DataToExport(name=self.title)
self._raw_data = data
self._display_temporary = False
self._show_data(data, **kwargs)
[docs] def show_data_temp(self, data: DataWithAxes, **kwargs):
"""Entrypoint to display temporary data into the viewer
No processed data signal is emitted from the viewer
Parameters
----------
data: data_mod.DataFromPlugins
"""
self._display_temporary = True
self.show_data(data, **kwargs)
def _show_data(self, data: DataWithAxes, *args, **kwargs):
"""Specific viewers should implement it"""
raise NotImplementedError
[docs] def add_attributes_from_view(self):
"""Convenience function to add attributes from the view to self"""
for attribute in self.convenience_attributes:
if hasattr(self.view, attribute):
setattr(self, attribute, getattr(self.view, attribute))
[docs] def trigger_action(self, action_name: str):
"""Convenience function to trigger programmatically one of the action of the related view"""
if self.has_action(action_name):
self.get_action(action_name).trigger()
[docs] def activate_roi(self, activate=True):
"""Activate the Roi manager using the corresponding action"""
raise NotImplementedError
[docs] def setVisible(self, show=True):
"""convenience method to show or hide the paretn widget"""
self.parent.setVisible(show)
@property
def roi_target(self) -> Union[InfiniteLine.InfiniteLine, ROI.ROI]:
"""To be implemented if necessary (Viewer1D and above)"""
return None
[docs] def move_roi_target(self, pos: Iterable[float] = None, **kwargs):
"""move a specific read only ROI at the given position on the viewer"""
...
[docs] def show_roi_target(self, show=True):
"""Show/Hide a specific read only ROI"""
if self.roi_target is not None:
self.roi_target.setVisible(show)