Source code for pymodaq_gui.plotting.data_viewers.viewer

from typing import List, Union
import numpy as np

from qtpy import QtWidgets

from pymodaq_utils.logger import set_logger, get_module_name
from pymodaq_data.data import DataToExport, DataWithAxes, Axis, DataSource

from pymodaq_utils.enums import enum_checker
from pymodaq_utils.factory import ObjectFactory
from pymodaq_gui.plotting import data_viewers
from pymodaq_gui.plotting.data_viewers.base import ViewerBase, ViewersEnum
from pymodaq_gui.utils import DockArea, Dock

config_viewers = {}

logger = set_logger(get_module_name(__file__))


[docs] def get_viewer_enum_from_axes(Naxes: int): if Naxes < 0: raise ValueError('Naxes could not be below 0') if Naxes == 0: viewer_enum = ViewersEnum['Viewer0D'] elif Naxes == 1: viewer_enum = ViewersEnum['Viewer1D'] elif Naxes == 2: viewer_enum = ViewersEnum['Viewer2D'] else: viewer_enum = ViewersEnum['ViewerND'] return viewer_enum
[docs] class ViewerFactory(ObjectFactory):
[docs] def get(self, viewer_name, **kwargs): if viewer_name not in ViewersEnum.names(): raise ValueError(f'{viewer_name} is not a valid PyMoDAQ Viewer: {ViewersEnum.names()}') return self.create(viewer_name, **kwargs)
@property def viewers(self): return self.keys
[docs] @ViewerFactory.register('Viewer0D') def create_viewer0D(parent: QtWidgets.QWidget = None, **_ignored): return data_viewers.viewer0D.Viewer0D(parent)
[docs] @ViewerFactory.register('Viewer1D') def create_viewer1D(parent: QtWidgets.QWidget, **_ignored): return data_viewers.viewer1D.Viewer1D(parent)
[docs] @ViewerFactory.register('Viewer2D') def create_viewer2D(parent: QtWidgets.QWidget, **_ignored): return data_viewers.viewer2D.Viewer2D(parent)
[docs] @ViewerFactory.register('ViewerND') def create_viewerND(parent: QtWidgets.QWidget, **_ignored): return data_viewers.viewerND.ViewerND(parent)
# @ViewerFactory.register('ViewerSequential') # def create_viewer_sequential(widget: QtWidgets.QWidget, **_ignored): # return data_viewers.viewer_sequential.ViewerSequential(widget) viewer_factory = ViewerFactory()
[docs] class ViewerDispatcher: """MixIn class to add easy control for adding multuiple data viewers in docks depending on data to be plotted Parameters ---------- dockarea: DockArea title: str next_to_dock: Dock (deprecated) has no effect direction: str either 'right', 'left', 'bottom', 'top'. """ def __init__(self, dockarea: DockArea = None, title: str = '', next_to_dock: Dock = None, direction='right'): super().__init__() self._title = title if title != '' else self.__class__.__name__ self._next_to_dock = next_to_dock if dockarea is None: dockarea = DockArea() dockarea.show() self.dockarea = dockarea self.dockarea.setWindowTitle(title) self._direction = direction self._viewer_docks = [] self._viewer_widgets = [] self._viewer_types = [] self._viewers = [] @property def viewers(self) -> List[ViewerBase]: return self._viewers @property def viewer_docks(self) -> List[Dock]: return self._viewer_docks @property def viewer_widgets(self) -> List[QtWidgets.QWidget]: return self._viewer_widgets @property def viewer_types(self) -> List[ViewersEnum]: return self._viewer_types
[docs] def remove_viewers(self, Nviewers_to_leave: int = 0): """Remove viewers from the list after index Nviewers_to_leave Parameters ---------- Nviewers Returns ------- """ while len(self.viewer_docks) > Nviewers_to_leave: widget = self.viewer_widgets.pop() widget.close() dock = self.viewer_docks.pop() dock.close() self.viewers.pop() self.viewer_types.pop() QtWidgets.QApplication.processEvents()
[docs] def add_viewer(self, viewer_type: ViewersEnum, dock_viewer=None, dock_name=None): viewer_type = enum_checker(ViewersEnum, viewer_type) if dock_viewer is None: if dock_name is None: dock_name = f'{self._title}_Viewer_{len(self.viewer_docks) + 1}' dock_viewer = Dock(dock_name, size=(350, 350), closable=False) self.viewer_docks.append(dock_viewer) self._viewer_widgets.append(QtWidgets.QWidget()) self.viewers.append(viewer_factory.get(viewer_type.name, parent=self._viewer_widgets[-1])) self.viewer_types.append(viewer_type) self.viewer_docks[-1].addWidget(self._viewer_widgets[-1]) # if len(self.viewer_docks) == 1: # if self._next_to_dock is not None: # self.dockarea.addDock(self.viewer_docks[-1], 'right', self._next_to_dock) # else: # self.dockarea.addDock(self.viewer_docks[-1]) # else: # self.dockarea.addDock(self.viewer_docks[-1], 'right', self.viewer_docks[-2]) self.dockarea.addDock(self.viewer_docks[-1], self._direction)
[docs] def update_viewers(self, viewers_type: List[Union[str, ViewersEnum]], viewers_name: List[str] = None, force=False): """ Parameters ---------- viewers_type: List[ViewersEnum] viewers_name: List[str] or None force: bool if True remove all viewers before update else check if new viewers type are compatible with old ones Returns ------- """ Nviewers_to_leave = 0 if not force: # check if viewers are compatible with new data dim for ind, viewer_type in enumerate(viewers_type): if len(self.viewer_types) > ind: if viewer_type == self.viewer_types[ind]: Nviewers_to_leave += 1 else: break else: break self.remove_viewers(Nviewers_to_leave) ind_loop = 0 while len(self.viewers) < len(viewers_type): self.add_viewer(viewers_type[Nviewers_to_leave + ind_loop], dock_name=viewers_name[Nviewers_to_leave + ind_loop] if viewers_name is not None else None) ind_loop += 1 QtWidgets.QApplication.processEvents()
[docs] def close(self): for dock in self.viewer_docks: dock.close()
[docs] def show_data(self, data: DataToExport, **kwargs): """ Convenience method. Display each dwa in a dedicated data viewer""" viewer_types = [ViewersEnum.get_viewers_enum_from_data(dwa) for dwa in data] viewer_names = [dwa.name for dwa in data] if self.viewer_types != viewer_types: self.update_viewers(viewer_types, viewer_names) for viewer, dwa in zip(self.viewers, data): if len(dwa.axes) != len(dwa.shape): dwa.create_missing_axes() viewer.show_data(dwa)
if __name__ == '__main__': LABEL = 'A Label' UNITS = 'units' OFFSET = -20.4 SCALING = 0.22 SIZE = 20 DATA = OFFSET + SCALING * np.linspace(0, SIZE - 1, SIZE) DATA0D = np.array([2.7]) DATA1D = np.arange(0, 10) DATA2D = np.arange(0, 5 * 6).reshape((5, 6)) DATAND = np.arange(0, 5 * 6 * 3).reshape((5, 6, 3)) Nn0 = 10 Nn1 = 5 def init_axis(data=None, index=0): if data is None: data = DATA return Axis(label=LABEL, units=UNITS, data=data, index=index) def init_data(data=None, Ndata=1, axes=[], name='myData', source=DataSource['raw'], labels=None) -> DataWithAxes: if data is None: data = DATA2D return DataWithAxes(name, source, data=[data for ind in range(Ndata)], axes=axes, labels=labels) def ini_data_to_export(): dat1 = init_data(data=DATA2D, Ndata=2, name='data2D') dat2 = init_data(data=DATA1D, Ndata=3, name='data1D') data = DataToExport(name='toexport', data=[dat1, dat2]) return dat1, dat2, data import sys app = QtWidgets.QApplication(sys.argv) dockarea = DockArea() prog = ViewerDispatcher(dockarea=dockarea, title='Dispatcher') dockarea.show() _, _, dte = ini_data_to_export() # N = 2 # viewers = ['Viewer0D', 'Viewer1D', 'Viewer2D'] # viewers = [ViewersEnum[random.choice(viewers)] for ind in range(N)] # viewers = ['Viewer2D', 'Viewer2D', ] # print(viewers) # prog.update_viewers(viewers) prog.show_data(dte) sys.exit(app.exec_())