from abc import ABCMeta, abstractmethod, abstractproperty
import sys
from typing import List, Tuple, Union
import numpy as np
try:
from scipy.spatial import QhullError # works for newer version of scipy
from scipy.spatial import Delaunay as Triangulation
except ImportError:
from scipy.spatial.qhull import QhullError # works for old version of scipy
from scipy.spatial.qhull import Delaunay as Triangulation
from qtpy import QtWidgets
from qtpy.QtCore import QObject, Slot, Signal, QRectF, QPointF
from pymodaq.utils.logger import set_logger, get_module_name
from pymodaq.utils.gui_utils.dock import DockArea, Dock
from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
from pymodaq.utils.plotting.utils.axes_viewer import AxesViewer
from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
import pymodaq.utils.daq_utils as utils
import pymodaq.utils.math_utils as mutils
from pymodaq.utils.data import DataRaw, Axis, DataDistribution, DataWithAxes, DataDim, DataCalculated
from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase
from pymodaq.utils.managers.action_manager import ActionManager
from pymodaq.utils.managers.parameter_manager import ParameterManager
from pymodaq.post_treatment.process_to_scalar import DataProcessorFactory
from pymodaq.utils.managers.roi_manager import SimpleRectROI, LinearROI
logger = set_logger(get_module_name(__file__))
data_processors = DataProcessorFactory()
DEBUG_VIEWER = False
class BaseDataDisplayer(QObject):
data_dim_signal = Signal(str)
processor_changed = Signal(object)
distribution: DataDistribution = abstractproperty()
def __init__(self, viewer0D: Viewer0D, viewer1D: Viewer1D, viewer2D: Viewer2D, navigator1D: Viewer1D,
navigator2D: Viewer2D, axes_viewer: AxesViewer):
super().__init__()
self._viewer0D = viewer0D
self._viewer1D = viewer1D
self._viewer2D = viewer2D
self._navigator1D = navigator1D
self._navigator2D = navigator2D
self._axes_viewer = axes_viewer
self._data: DataWithAxes = None
self._nav_limits: tuple = (0, 10, None, None)
self._signal_at: tuple = (0, 0)
self._filter_type: str = None
self._processor = None
self._show_nav_integration = False
@property
def data_shape(self):
return self._data.shape if self._data is not None else None
def update_filter(self, filter_type: str):
if filter_type in self._processor.functions:
self._filter_type = filter_type
self.update_nav_data(*self._nav_limits)
def update_processor(self, math_processor: DataProcessorFactory):
self._processor = math_processor
self.processor_changed.emit(math_processor)
def update_data(self, data: DataRaw, force_update=False):
if self._data is None or self._data.shape != data.shape or force_update:
self._data = data
self.init(data)
else:
self._data.data = data.data[0]
self.data_dim_signal.emit(self._data.get_data_dimension())
self.update_viewer_data(*self._signal_at)
self.update_nav_data(*self._nav_limits)
def show_nav_integration(self, show=True):
self._show_nav_integration = show
self.update_viewer_data(*self._signal_at)
@abstractmethod
def init_rois(self, data: DataRaw):
"""Init crosshairs and ROIs in viewers if needed"""
...
@abstractmethod
def init(self):
"""init viewers or postprocessing once new data are loaded"""
...
@abstractmethod
def update_viewer_data(self, **kwargs):
""" Update the signal display depending on the position of the crosshair in the navigation panels
"""
...
def updated_nav_integration(self):
""" Means the ROI select of the 2D viewer has been moved """
...
@abstractmethod
def update_nav_data(self, x, y, width=None, height=None):
"""Display navigator data potentially postprocessed from filters in the signal viewers"""
...
@abstractmethod
def get_nav_data(self, data: DataWithAxes, x, y, width=None, height=None) -> DataWithAxes:
"""Get filtered data"""
...
def update_nav_data_from_roi(self, roi: Union[SimpleRectROI, LinearROI]):
if isinstance(roi, LinearROI):
x, y = roi.getRegion()
self._nav_limits = (x, y, None, None)
elif isinstance(roi, SimpleRectROI):
x, y = roi.pos().x(), roi.pos().y()
width, height = roi.size().x(), roi.size().y()
self._nav_limits = (int(x), int(y), int(width), int(height))
self.update_nav_data(*self._nav_limits)
@staticmethod
def get_out_of_range_limits(x, y, width, height):
if x < 0:
width = width + x
x = 0
if y < 0:
height = height + y
y = 0
return x, y, width, height
def update_nav_indexes(self, nav_indexes: List[int]):
self._data.nav_indexes = nav_indexes
self.update_data(self._data, force_update=True)
def update_nav_limits(self, x, y, width=None, height=None):
self._nav_limits = x, y, width, height
class UniformDataDisplayer(BaseDataDisplayer):
"""Specialized object to filter and plot linearly spaced data in dedicated viewers
Meant for any navigation axes and up to signal data dimensionality of 2 (images)
"""
distribution = DataDistribution['uniform']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def init(self, data: DataRaw):
if len(data.nav_indexes) > 2:
self._axes_viewer.set_nav_viewers(self._data.get_nav_axes_with_data())
processor: DataProcessorFactory = data_processors
self.update_processor(processor)
def init_rois(self, nav_axes_limits: List[Tuple[float, float]] = None,
sig_axis_limits: List[Tuple[float, float]] = None):
if len(nav_axes_limits) == 1:
self._navigator1D.crosshair.set_crosshair_position(np.mean(nav_axes_limits[0]))
if len(nav_axes_limits) == 2:
self._navigator2D.crosshair.set_crosshair_position(
*self._navigator2D.view.unscale_axis(np.mean(nav_axes_limits[1]),
np.mean(nav_axes_limits[0]))
)
if len(sig_axis_limits) == 1:
self._viewer1D.roi.setPos((float(np.mean(sig_axis_limits[0]) -
np.abs(np.diff(sig_axis_limits[0]))[0] / 3),
float(np.mean(sig_axis_limits[0]) +
np.abs(np.diff(sig_axis_limits[0]))[0] / 3))
)
if len(sig_axis_limits) == 2:
scaled_axes = np.array(self._viewer2D.view.unscale_axis(np.array(sig_axis_limits[1]),
np.array(sig_axis_limits[0])))
self._viewer2D.roi.setSize(
float(np.diff(scaled_axes[0])[0]) / 3,
float(np.diff(scaled_axes[1])[0]) / 3)
self._viewer2D.roi.setPos(
float(np.mean(scaled_axes[0])) - float(np.diff(scaled_axes[0])[0]) / 6,
float(np.mean(scaled_axes[1])) - float(np.diff(scaled_axes[1])[0]) / 6)
def updated_nav_integration(self):
""" Means the ROI select of the 2D viewer has been moved """
self.update_viewer_data(*self._signal_at)
def update_viewer_data(self, posx=0, posy=0):
""" Update the signal display depending on the position of the crosshair in the navigation panels
Parameters
----------
posx: float
from the 1D or 2D Navigator crosshair or from one of the navigation axis viewer (in that case
nav_axis tells from which navigation axis the position comes from)
posy: float
from the 2D Navigator crosshair
"""
self._signal_at = posx, posy
if self._data is not None:
try:
if len(self._data.nav_indexes) == 0:
data = self._data
elif len(self._data.nav_indexes) == 1:
nav_axis = self._data.axes_manager.get_nav_axes()[0]
if posx < nav_axis.min() or posx > nav_axis.max():
return
ind_x = nav_axis.find_index(posx)
logger.debug(f'Getting the data at nav index {ind_x}')
data: DataCalculated = self._data.inav[ind_x]
if self._show_nav_integration:
if self._navigator1D.view.is_action_checked('ROIselect'):
x0, x1 = self._navigator1D.view.ROIselect.getRegion()
ind_x0 = max(0, int(nav_axis.find_index(x0)))
ind_x1 = min(int(nav_axis.max()), int(nav_axis.find_index(x1)))
data.append(self._data.inav[ind_x0:ind_x1].mean(axis=(nav_axis.index)))
else:
data.append(self._data.mean(axis=nav_axis.index))
elif len(self._data.nav_indexes) == 2:
nav_x = self._data.axes_manager.get_nav_axes()[1]
nav_y = self._data.axes_manager.get_nav_axes()[0]
if posx < nav_x.min() or posx > nav_x.max():
return
if posy < nav_y.min() or posy > nav_y.max():
return
ind_x = nav_x.find_index(posx)
ind_y = nav_y.find_index(posy)
logger.debug(f'Getting the data at nav indexes {ind_y} and {ind_x}')
data = self._data.inav[ind_y, ind_x]
if self._show_nav_integration:
if self._navigator2D.view.is_action_checked('ROIselect'):
ind_x0 = max(0, int(self._navigator2D.view.ROIselect.x()))
ind_y0 = max(0, int(self._navigator2D.view.ROIselect.y()))
ind_x1 = min(int(nav_x.max()), ind_x0 + int(self._navigator2D.view.ROIselect.size().x()))
ind_y1 = min(int(nav_y.max()), ind_y0 + int(self._navigator2D.view.ROIselect.size().y()))
data.append(self._data.inav[ind_y0:ind_y1, ind_x0:ind_x1].mean(axis=(nav_x.index, nav_y.index)))
else:
data.append(self._data.mean(axis=(nav_x.index, nav_y.index)))
else:
data = self._data.inav.__getitem__(self._axes_viewer.get_indexes())
if len(self._data.axes_manager.sig_shape) == 0: # means 0D data, plot on 0D viewer
self._viewer0D.show_data(data)
elif len(self._data.axes_manager.sig_shape) == 1: # means 1D data, plot on 1D viewer
self._viewer1D.show_data(data)
elif len(self._data.axes_manager.sig_shape) == 2: # means 2D data, plot on 2D viewer
self._viewer2D.show_data(data)
if DEBUG_VIEWER:
x, y, width, height = self.get_out_of_range_limits(*self._nav_limits)
_data_sig = data.isig[y: y + height, x: x + width]
self._debug_viewer_2D.show_data(_data_sig)
except Exception as e:
logger.exception(str(e))
def update_nav_data(self, x, y, width=None, height=None):
if self._data is not None and self._filter_type is not None and len(self._data.nav_indexes) != 0:
nav_data = self.get_nav_data(self._data, x, y, width, height)
if nav_data is not None:
nav_data.nav_indexes = () # transform nav axes in sig axes for plotting
if len(nav_data.shape) < 2:
self._navigator1D.show_data(nav_data)
elif len(nav_data.shape) == 2:
self._navigator2D.show_data(nav_data)
else:
self._axes_viewer.set_nav_viewers(self._data.get_nav_axes_with_data())
def get_nav_data(self, data: DataRaw, x, y, width=None, height=None):
try:
navigator_data = None
if len(data.axes_manager.sig_shape) == 0: # signal data is 0D
navigator_data = data.deepcopy()
elif len(data.axes_manager.sig_shape) == 1: # signal data is 1D
indx, indy = data.get_axis_from_index(data.sig_indexes[0])[0].find_indexes((x, y))
navigator_data = self._processor.get(self._filter_type).process(data.isig[indx:indy])
elif len(data.axes_manager.sig_shape) == 2: # signal data is 2D
x, y, width, height = self.get_out_of_range_limits(x, y, width, height)
if not (width is None or height is None or width < 2 or height < 2):
navigator_data = self._processor.get(self._filter_type).process(data.isig[y: y + height, x: x + width])
else:
navigator_data = None
else:
navigator_data = None
return navigator_data
except Exception as e:
logger.warning('Could not compute the mathematical function')
finally:
return navigator_data
class SpreadDataDisplayer(BaseDataDisplayer):
"""Specialized object to filter and plot non uniformly spaced data in dedicated viewers
Meant for any navigation axes and up to signal data dimensionality of 2 (images)
"""
distribution = DataDistribution['spread']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.triangulation = True
def init(self, data: DataWithAxes):
processor = data_processors # if len(data.axes_manager.sig_shape) > 1 else math_processors1D
self.update_processor(processor)
def init_rois(self, nav_axes_limits: List[Tuple[float, float]] = None,
sig_axis_limits: List[Tuple[float, float]] = None):
pass
def update_viewer_data(self, posx=0, posy=0):
""" Update the signal display depending on the position of the crosshair in the navigation panels
Spread data can be customly represented using:
if signal data is 0D:
* A viewer 1D with non-linearly spaced data points (for 1 navigation axis)
* A viewer 2D with its SpreadImage item (for 2 navigation axis)
* A double panel: viewer for signal data and viewer 1D for all nav axes as a function of index in the data
otherwise:
* A double panel: viewer for signal data and viewer 1D for all nav axes as a function of index in the data
series
Parameters
----------
posx: float
from the 1D or 2D Navigator crosshair or from one of the navigation axis viewer (in that case
nav_axis tells from which navigation axis the position comes from)
posy: float
from the 2D Navigator crosshair
"""
self._signal_at = posx, posy
if self._data is not None:
try:
nav_axes = sorted(self._data.get_nav_axes_with_data(), key=lambda axis: axis.spread_order)
if len(nav_axes) == 1:
# signal data plotted as a function of nav_axes[0] so get the index corresponding to
# the position posx
ind_nav = nav_axes[0].find_index(posx)
data = self._data.inav[ind_nav]
elif len(nav_axes) == 2 and self.triangulation:
# signal data plotted as a function of nav_axes[0] and nav_axes[1] so get the common
# index corresponding to the position posx and posy
ind_nav, x0, y0 = mutils.find_common_index(nav_axes[0].data, nav_axes[1].data, posx, posy)
data = self._data.inav[ind_nav]
else:
# navigation plotted as a function of index all nav_axes so get the index corresponding to
# the position posx
data = self._data.inav[int(posx)]
if len(self._data.axes_manager.sig_shape) == 0: # means 0D data, plot on 0D viewer
self._viewer0D.show_data(data)
elif len(self._data.axes_manager.sig_shape) == 1: # means 1D data, plot on 1D viewer
self._viewer1D.show_data(data)
elif len(self._data.axes_manager.sig_shape) == 2: # means 2D data, plot on 2D viewer
self._viewer2D.show_data(data)
if DEBUG_VIEWER:
x, y, width, height = self.get_out_of_range_limits(*self._nav_limits)
_data_sig = data.isig[y: y + height, x: x + width]
self._debug_viewer_2D.show_data(_data_sig)
except Exception as e:
logger.exception(str(e))
def update_nav_data(self, x, y, width=None, height=None):
if self._data is not None and self._filter_type is not None and len(self._data.nav_indexes) != 0:
nav_data = self.get_nav_data(self._data, x, y, width, height)
if nav_data is not None:
nav_axes = nav_data.get_nav_axes_with_data()
if len(nav_axes) < 2:
#nav_data.nav_indexes = ()
self._navigator1D.show_data(nav_data)
elif len(nav_axes) == 2:
try:
Triangulation(np.array([axis.get_data() for axis in nav_data.get_nav_axes()]))
self._navigator2D.show_data(nav_data)
except QhullError as e:
self.triangulation = False
self._navigator2D.setVisible(False)
self._navigator1D.setVisible(True)
data_arrays = [axis.get_data() for axis in nav_axes]
labels = [axis.label for axis in nav_axes]
nav_data = DataCalculated('nav', data=data_arrays, labels=labels)
self._navigator1D.show_data(nav_data)
else:
data_arrays = [axis.get_data() for axis in nav_axes]
labels = [axis.label for axis in nav_axes]
nav_data = DataCalculated('nav', data=data_arrays, labels=labels)
self._navigator1D.show_data(nav_data)
def get_nav_data(self, data: DataRaw, x, y, width=None, height=None):
try:
navigator_data = None
if len(data.axes_manager.sig_shape) == 0: # signal data is 0D
navigator_data = data
elif len(data.axes_manager.sig_shape) == 1: # signal data is 1D
ind_x = data.get_axis_from_index(data.sig_indexes[0])[0].find_index(x)
ind_y = data.get_axis_from_index(data.sig_indexes[0])[0].find_index(y)
navigator_data = self._processor.get(self._filter_type).process(data.isig[ind_x:ind_y])
elif len(data.axes_manager.sig_shape) == 2: # signal data is 2D
x, y, width, height = self.get_out_of_range_limits(x, y, width, height)
if not (width is None or height is None or width < 2 or height < 2):
navigator_data = self._processor.get(self._filter_type).process(data.isig[y: y + height, x: x + width])
else:
navigator_data = None
else:
navigator_data = None
return navigator_data
except Exception as e:
logger.warning('Could not compute the mathematical function')
finally:
return navigator_data
def get_nav_position(self, posx=0, posy=None):
"""
crosshair position from the "spread" data viewer. Should return scan index where the scan was closest to posx,
posy coordinates
Parameters
----------
posx
posy
See Also
--------
update_viewer_data
"""
# todo adapt to new layout
nav_axes = self.get_selected_axes()
if len(nav_axes) != 0:
if 'datas' in nav_axes[0]:
datas = nav_axes[0]['datas']
xaxis = datas[0]
if len(datas) > 1:
yaxis = datas[1]
ind_scan = utils.find_common_index(xaxis, yaxis, posx, posy)
else:
ind_scan = mutils.find_index(xaxis, posx)[0]
self.navigator1D.ui.crosshair.set_crosshair_position(ind_scan[0])
[docs]class ViewerND(ParameterManager, ActionManager, ViewerBase):
params = [
{'title': 'Set data spread 1D-0D', 'name': 'set_data_spread1D0D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 1D-1D', 'name': 'set_data_spread1D1D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 1D-2D', 'name': 'set_data_spread1D2D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 2D-0D', 'name': 'set_data_spread2D0D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 2D-1D', 'name': 'set_data_spread2D1D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 2D-2D', 'name': 'set_data_spread2D2D', 'type': 'action', 'visible': False},
{'title': 'Set data spread 3D-0D', 'name': 'set_data_spread3D0D', 'type': 'action', 'visible': False},
{'title': 'Set data 4D', 'name': 'set_data_4D', 'type': 'action', 'visible': False},
{'title': 'Set data 3D', 'name': 'set_data_3D', 'type': 'action', 'visible': False},
{'title': 'Set data 2D', 'name': 'set_data_2D', 'type': 'action', 'visible': False},
{'title': 'Set data 1D', 'name': 'set_data_1D', 'type': 'action', 'visible': False},
{'title': 'Signal shape', 'name': 'data_shape_settings', 'type': 'group', 'children': [
{'title': 'Initial Data shape:', 'name': 'data_shape_init', 'type': 'str', 'value': "",
'readonly': True},
{'title': 'Axes shape:', 'name': 'nav_axes_shapes', 'type': 'group', 'children': [],
'readonly': True},
{'title': 'Data shape:', 'name': 'data_shape', 'type': 'str', 'value': "", 'readonly': True},
{'title': 'Navigator axes:', 'name': 'navigator_axes', 'type': 'itemselect'},
{'title': 'Set Nav axes:', 'name': 'set_nav_axes', 'type': 'action', 'visible': True},
]},
]
def __init__(self, parent: QtWidgets.QWidget = None, title=''):
ViewerBase.__init__(self, parent, title=title)
ActionManager.__init__(self, toolbar=QtWidgets.QToolBar())
ParameterManager.__init__(self)
self._area = None
self._data = None
self.viewer0D: Viewer0D = None
self.viewer1D: Viewer1D = None
self.viewer2D: Viewer2D = None
self.navigator1D: Viewer1D = None
self.navigator2D: Viewer2D = None
self.axes_viewer: AxesViewer = None
self.setup_widgets()
self.data_displayer: BaseDataDisplayer = None
self.setup_actions()
self.connect_things()
self.prepare_ui()
def update_data_displayer(self, distribution: DataDistribution):
if distribution.name == 'uniform':
self.data_displayer = UniformDataDisplayer(self.viewer0D, self.viewer1D, self.viewer2D,
self.navigator1D, self.navigator2D,
self.axes_viewer)
else:
self.data_displayer = SpreadDataDisplayer(self.viewer0D, self.viewer1D, self.viewer2D,
self.navigator1D, self.navigator2D,
self.axes_viewer)
self.navigator1D.crosshair.crosshair_dragged.connect(self.data_displayer.update_viewer_data)
self.navigator1D.ROI_select_signal.connect(self.data_displayer.updated_nav_integration)
self.navigator2D.crosshair_dragged.connect(self.data_displayer.update_viewer_data)
self.navigator2D.ROI_select_signal.connect(self.data_displayer.updated_nav_integration)
self.axes_viewer.navigation_changed.connect(self.data_displayer.update_viewer_data)
self.data_displayer.data_dim_signal.connect(self.update_data_dim)
self.viewer1D.roi.sigRegionChanged.connect(self.data_displayer.update_nav_data_from_roi)
self.viewer2D.roi.sigRegionChanged.connect(self.data_displayer.update_nav_data_from_roi)
self.get_action('filters').currentTextChanged.connect(self.data_displayer.update_filter)
self.connect_action('integrate_nav', self.data_displayer.show_nav_integration)
self.data_displayer.processor_changed.connect(self.update_filters)
def _show_data(self, data: DataRaw, **kwargs):
force_update = False
self.settings.child('data_shape_settings', 'data_shape_init').setValue(str(data.shape))
self.settings.child('data_shape_settings', 'navigator_axes').setValue(
dict(all_items=[str(ax.index) for ax in data.axes],
selected=[str(ax.index) for ax in data.get_nav_axes()]))
if self._data is None or self._data.dim != data.dim or self._data.nav_indexes != data.nav_indexes:
force_update = True
if 'force_update' in kwargs:
force_update = kwargs['force_update']
if self.data_displayer is None or data.distribution != self.data_displayer.distribution:
self.update_data_displayer(data.distribution)
self.data_displayer.update_data(data, force_update=force_update)
self._data = data
if force_update:
self.update_widget_visibility(data)
self.data_displayer.init_rois(data.axes_limits(data.nav_indexes),
data.axes_limits(data.sig_indexes))
self.data_to_export_signal.emit(self.data_to_export)
def set_data_test(self, data_shape='3D'):
if 'spread' in data_shape:
data_tri = np.load('../../../resources/triangulation_data.npy')
axes = [Axis(data=data_tri[:, 0], index=0, label='x_axis', units='xunits', spread_order=0)]
if 'spread2D' in data_shape or 'spread3D' in data_shape:
axes.append(Axis(data=data_tri[:, 1], index=0, label='y_axis', units='yunits', spread_order=1))
if data_shape == 'spread2D0D':
data = data_tri[:, 2]
elif data_shape == 'spread2D1D':
x = np.linspace(-50, 50, 100)
data = np.zeros((data_tri.shape[0], len(x)))
for ind in range(data_tri.shape[0]):
data[ind, :] = data_tri[ind, 2] * mutils.gauss1D(x, 0.01*ind - 10, 20)
axes.append(Axis(data=x, index=1, label='sig_axis'))
elif data_shape == 'spread2D2D':
x = np.linspace(-50, 50, 100)
y = np.linspace(-200, 200, 75)
data = np.zeros((data_tri.shape[0], len(y), len(x)))
for ind in range(data_tri.shape[0]):
#data[ind, :] = data_tri[ind, 2] * mutils.gauss2D(0.01*x, 0.1*ind - 20, 20, y, 0.1*ind-20, 10)
data[ind, :] = mutils.gauss2D(x, 10*data_tri[ind, 0], 20, y, 20*data_tri[ind, 1], 30)
axes.append(Axis(data=x, index=1, label='sig_axis0'))
axes.append(Axis(data=y, index=2, label='sig_axis1'))
elif data_shape == 'spread3D0D':
if 'spread2D' in data_shape or 'spread3D' in data_shape:
axes.append(Axis(data=data_tri[:, 1], index=0, label='z_axis', units='zunits', spread_order=2))
data = data_tri[:, 2]
else:
if data_shape == 'spread1D0D':
data = data_tri[:, 2]
elif data_shape == 'spread1D1D':
x = np.linspace(-50, 50, 100)
data = np.zeros((data_tri.shape[0], len(x)))
for ind in range(data_tri.shape[0]):
data[ind, :] = data_tri[ind, 2] * mutils.gauss1D(x, 0.01 * ind - 10, 20)
axes.append(Axis(data=x, index=1, label='sig_axis'))
elif data_shape == 'spread1D2D':
x = np.linspace(-50, 50, 100)
y = np.linspace(-200, 200, 75)
data = np.zeros((data_tri.shape[0], len(y), len(x)))
for ind in range(data_tri.shape[0]):
# data[ind, :] = data_tri[ind, 2] * mutils.gauss2D(0.01*x, 0.1*ind - 20, 20, y, 0.1*ind-20, 10)
data[ind, :] = mutils.gauss2D(x, 10 * data_tri[ind, 0], 20, y, 20 * data_tri[ind, 1], 30)
axes.append(Axis(data=x, index=1, label='sig_axis0'))
axes.append(Axis(data=y, index=2, label='sig_axis1'))
dataraw = DataRaw('NDdata', distribution='spread', dim='DataND',
data=[data], nav_indexes=(0, ),
axes=axes)
else:
x = mutils.linspace_step(-10, 10, 0.2)
y = mutils.linspace_step(-30, 30, 2)
t = mutils.linspace_step(-200, 200, 2)
z = mutils.linspace_step(-50, 50, 0.5)
data = np.zeros((len(y), len(x), len(t), len(z)))
amp = mutils.gauss2D(x, 0, 5, y, 0, 4) + 0.1 * np.random.rand(len(y), len(x))
amp = np.ones((len(y), len(x), len(t), len(z)))
for indx in range(len(x)):
for indy in range(len(y)):
data[indy, indx, :, :] = amp[indy, indx] * (
mutils.gauss2D(z, -50 + indx * 1, 20,
t, 0 + 2 * indy, 30)
+ np.random.rand(len(t), len(z)) / 10)
if data_shape == '4D':
dataraw = DataRaw('NDdata', data=data, dim='DataND', nav_indexes=[0, 1],
axes=[Axis(data=y, index=0, label='y_axis', units='yunits'),
Axis(data=x, index=1, label='x_axis', units='xunits'),
Axis(data=t, index=2, label='t_axis', units='tunits'),
Axis(data=z, index=3, label='z_axis', units='zunits')])
elif data_shape == '3D':
data = [np.sum(data, axis=2)]
dataraw = DataRaw('NDdata', data=data, dim='DataND', nav_indexes=[0, 1],
axes=[Axis(data=y, index=0, label='y_axis', units='yunits'),
Axis(data=x, index=1, label='x_axis', units='xunits'),
Axis(data=t, index=2, label='t_axis', units='tunits')])
elif data_shape == '2D':
data = [np.sum(data, axis=(2, 3))]
dataraw = DataRaw('NDdata', data=data, dim='DataND', nav_indexes=[0],
axes=[Axis(data=y, index=0, label='y_axis', units='yunits'),
Axis(data=x, index=1, label='x_axis', units='xunits')],
)
elif data_shape == '1D':
data = [np.sum(data, axis=(0, 1, 2))]
dataraw = DataRaw('NDdata', data=data, dim='DataND', nav_indexes=[],
axes=[Axis(data=z, index=0, label='z_axis', units='zunits')])
self._show_data(dataraw, force_update=True)
def update_widget_visibility(self, data: DataRaw = None,
nav_indexes:Tuple[int] = None):
if data is None:
data = self._data
if nav_indexes is None:
nav_indexes = data.nav_indexes
self.viewer0D.setVisible(len(data.shape) - len(nav_indexes) == 0)
self.viewer1D.setVisible(len(data.shape) - len(nav_indexes) == 1)
self.viewer2D.setVisible(len(data.shape) - len(nav_indexes) == 2)
self.viewer1D.roi.setVisible(len(nav_indexes) != 0)
self.viewer2D.roi.setVisible(len(nav_indexes) != 0)
self._dock_navigation.setVisible(len(nav_indexes) != 0)
#nav_axes = data.get_nav_axes()
if data.distribution.name == 'uniform':
self.navigator1D.setVisible(len(nav_indexes) == 1)
self.navigator2D.setVisible(len(nav_indexes) == 2)
self.axes_viewer.setVisible(len(nav_indexes) > 2)
else:
self.navigator2D.setVisible(len(nav_indexes) == 2 and self.data_displayer.triangulation)
self.navigator1D.setVisible(len(nav_indexes) == 1 or len(nav_indexes) > 2 or
len(nav_indexes) == 2 and
not self.data_displayer.triangulation)
def update_filters(self, processor: DataProcessorFactory):
self.get_action('filters').clear()
self.get_action('filters').addItems(processor.functions_filtered('DataND'))
def show_settings(self, show: bool = True):
if show:
self.settings_tree.show()
else:
self.settings_tree.hide()
def prepare_ui(self):
self.navigator1D.setVisible(False)
self.viewer2D.setVisible(False)
self.navigator1D.setVisible(False)
self.viewer2D.setVisible(False)
[docs] def setup_actions(self):
self.add_action('setaxes', icon_name='cartesian', checkable=True, tip='Change navigation/signal axes')
self.add_widget('filters', QtWidgets.QComboBox, tip='Filter type to apply to signal data')
self.add_action('integrate_nav',icon_name='integrator', checkable=True,
tip='Integrate the navigation data')
def reshape_data(self):
_nav_indexes = [int(index) for index in
self.settings.child('data_shape_settings', 'navigator_axes').value()['selected']]
self.update_widget_visibility(nav_indexes=_nav_indexes)
self.data_displayer.update_nav_indexes(_nav_indexes)
def connect_things(self):
self.settings.child('set_data_1D').sigActivated.connect(lambda: self.set_data_test('1D'))
self.settings.child('set_data_2D').sigActivated.connect(lambda: self.set_data_test('2D'))
self.settings.child('set_data_3D').sigActivated.connect(lambda: self.set_data_test('3D'))
self.settings.child('set_data_4D').sigActivated.connect(lambda: self.set_data_test('4D'))
self.settings.child('set_data_spread2D0D').sigActivated.connect(lambda: self.set_data_test('spread2D0D'))
self.settings.child('set_data_spread2D1D').sigActivated.connect(lambda: self.set_data_test('spread2D1D'))
self.settings.child('set_data_spread2D2D').sigActivated.connect(lambda: self.set_data_test('spread2D2D'))
self.settings.child('set_data_spread1D0D').sigActivated.connect(lambda: self.set_data_test('spread1D0D'))
self.settings.child('set_data_spread1D1D').sigActivated.connect(lambda: self.set_data_test('spread1D1D'))
self.settings.child('set_data_spread1D2D').sigActivated.connect(lambda: self.set_data_test('spread1D2D'))
self.settings.child('set_data_spread3D0D').sigActivated.connect(lambda: self.set_data_test('spread3D0D'))
self.settings.child('data_shape_settings', 'set_nav_axes').sigActivated.connect(self.reshape_data)
self.navigator1D.get_action('crosshair').trigger()
self.connect_action('setaxes', self.show_settings)
def setup_widgets(self):
self.parent.setLayout(QtWidgets.QVBoxLayout())
self.parent.layout().addWidget(self.toolbar)
self._area = DockArea()
self.parent.layout().addWidget(self._area)
viewer0D_widget = QtWidgets.QWidget()
self.viewer0D = Viewer0D(viewer0D_widget)
viewer1D_widget = QtWidgets.QWidget()
self.viewer1D = Viewer1D(viewer1D_widget)
self.viewer1D.roi = LinearROI()
self.viewer1D.view.plotitem.addItem(self.viewer1D.roi)
viewer2D_widget = QtWidgets.QWidget()
self.viewer2D = Viewer2D(viewer2D_widget)
self.viewer2D.roi = SimpleRectROI(centered=True)
self.viewer2D.view.plotitem.addItem(self.viewer2D.roi)
self.viewer2D.set_action_visible('flip_ud', False)
self.viewer2D.set_action_visible('flip_lr', False)
self.viewer2D.set_action_visible('rotate', False)
self.viewer2D.get_action('autolevels').trigger()
self._dock_signal = Dock('Signal')
self._dock_signal.addWidget(viewer0D_widget)
self._dock_signal.addWidget(viewer1D_widget)
self._dock_signal.addWidget(viewer2D_widget)
navigator1D_widget = QtWidgets.QWidget()
self.navigator1D = Viewer1D(navigator1D_widget)
navigator2D_widget = QtWidgets.QWidget()
self.navigator2D = Viewer2D(navigator2D_widget)
self.navigator2D.get_action('autolevels').trigger()
self.navigator2D.get_action('crosshair').trigger()
nav_axes_widget = QtWidgets.QWidget()
nav_axes_widget.setVisible(False)
self.axes_viewer = AxesViewer(nav_axes_widget)
self._dock_navigation = Dock('Navigation')
self._dock_navigation.addWidget(navigator1D_widget)
self._dock_navigation.addWidget(navigator2D_widget)
self._dock_navigation.addWidget(nav_axes_widget)
self._area.addDock(self._dock_navigation)
self._area.addDock(self._dock_signal, 'right', self._dock_navigation)
def update_data_dim(self, dim: str):
self.settings.child('data_shape_settings', 'data_shape').setValue(dim)
def main():
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
prog = ViewerND(widget)
for child in prog.settings.children():
if 'set_data_' in child.name():
child.show(True)
prog.show_settings()
widget.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()