pymodaq 4.1.5__py3-none-any.whl → 4.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +23 -4
- pymodaq/control_modules/daq_move.py +32 -73
- pymodaq/control_modules/daq_viewer.py +73 -98
- pymodaq/control_modules/daq_viewer_ui.py +2 -1
- pymodaq/control_modules/move_utility_classes.py +17 -7
- pymodaq/control_modules/utils.py +153 -5
- pymodaq/control_modules/viewer_utility_classes.py +31 -20
- pymodaq/dashboard.py +23 -5
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +4 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/bayesian_optimisation.py +673 -0
- pymodaq/extensions/bayesian/utils.py +403 -0
- pymodaq/extensions/daq_scan.py +4 -4
- pymodaq/extensions/daq_scan_ui.py +2 -1
- pymodaq/extensions/pid/pid_controller.py +12 -7
- pymodaq/extensions/pid/utils.py +9 -26
- pymodaq/extensions/utils.py +3 -0
- pymodaq/post_treatment/load_and_plot.py +42 -19
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +9 -24
- pymodaq/resources/setup_plugin.py +1 -1
- pymodaq/utils/config.py +103 -5
- pymodaq/utils/daq_utils.py +35 -134
- pymodaq/utils/data.py +614 -95
- pymodaq/utils/enums.py +17 -1
- pymodaq/utils/factory.py +2 -2
- pymodaq/utils/gui_utils/custom_app.py +5 -2
- pymodaq/utils/gui_utils/dock.py +33 -4
- pymodaq/utils/gui_utils/utils.py +14 -1
- pymodaq/utils/h5modules/backends.py +9 -1
- pymodaq/utils/h5modules/data_saving.py +254 -57
- pymodaq/utils/h5modules/saving.py +1 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +172 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +170 -0
- pymodaq/utils/leco/desktop.ini +2 -0
- pymodaq/utils/leco/director_utils.py +58 -0
- pymodaq/utils/leco/leco_director.py +88 -0
- pymodaq/utils/leco/pymodaq_listener.py +279 -0
- pymodaq/utils/leco/utils.py +41 -0
- pymodaq/utils/managers/action_manager.py +20 -6
- pymodaq/utils/managers/parameter_manager.py +6 -4
- pymodaq/utils/managers/roi_manager.py +63 -54
- pymodaq/utils/math_utils.py +1 -1
- pymodaq/utils/plotting/data_viewers/__init__.py +3 -1
- pymodaq/utils/plotting/data_viewers/base.py +286 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +29 -202
- pymodaq/utils/plotting/data_viewers/viewer0D.py +94 -47
- pymodaq/utils/plotting/data_viewers/viewer1D.py +341 -174
- pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py +1 -1
- pymodaq/utils/plotting/data_viewers/viewer2D.py +271 -181
- pymodaq/utils/plotting/data_viewers/viewerND.py +26 -22
- pymodaq/utils/plotting/items/crosshair.py +3 -3
- pymodaq/utils/plotting/items/image.py +2 -1
- pymodaq/utils/plotting/plotter/plotter.py +94 -0
- pymodaq/utils/plotting/plotter/plotters/__init__.py +0 -0
- pymodaq/utils/plotting/plotter/plotters/matplotlib_plotters.py +134 -0
- pymodaq/utils/plotting/plotter/plotters/qt_plotters.py +78 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +1 -1
- pymodaq/utils/plotting/utils/filter.py +194 -147
- pymodaq/utils/plotting/utils/lineout.py +13 -11
- pymodaq/utils/plotting/utils/plot_utils.py +89 -12
- pymodaq/utils/scanner/__init__.py +0 -3
- pymodaq/utils/scanner/scan_config.py +1 -9
- pymodaq/utils/scanner/scan_factory.py +10 -36
- pymodaq/utils/scanner/scanner.py +3 -2
- pymodaq/utils/scanner/scanners/_1d_scanners.py +7 -5
- pymodaq/utils/scanner/scanners/_2d_scanners.py +36 -49
- pymodaq/utils/scanner/scanners/sequential.py +10 -4
- pymodaq/utils/scanner/scanners/tabular.py +10 -5
- pymodaq/utils/slicing.py +1 -1
- pymodaq/utils/tcp_ip/serializer.py +38 -5
- pymodaq/utils/tcp_ip/tcp_server_client.py +25 -17
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/METADATA +4 -2
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/RECORD +79 -63
- pymodaq/resources/config_scan_template.toml +0 -42
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/WHEEL +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/entry_points.txt +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
1
2
|
import os
|
|
2
3
|
import sys
|
|
3
|
-
from typing import List, TYPE_CHECKING
|
|
4
|
+
from typing import List, TYPE_CHECKING, Tuple
|
|
4
5
|
import pymodaq.utils
|
|
5
6
|
from qtpy import QtCore, QtGui, QtWidgets
|
|
6
7
|
from qtpy.QtCore import QObject, Slot, Signal, QPointF
|
|
@@ -83,10 +84,25 @@ class ROIPositionMapper(QtWidgets.QWidget):
|
|
|
83
84
|
|
|
84
85
|
|
|
85
86
|
class ROI(pgROI):
|
|
86
|
-
|
|
87
|
+
index_signal = Signal(int)
|
|
88
|
+
|
|
89
|
+
def __init__(self, *args, index=0, name='roi', **kwargs):
|
|
87
90
|
super().__init__(*args, **kwargs)
|
|
91
|
+
self.name = name
|
|
92
|
+
self.index = index
|
|
88
93
|
self._menu = QtWidgets.QMenu()
|
|
89
94
|
self._menu.addAction('Set ROI positions', self.set_positions)
|
|
95
|
+
self.sigRegionChangeFinished.connect(self.emit_index_signal)
|
|
96
|
+
|
|
97
|
+
def emit_index_signal(self):
|
|
98
|
+
self.index_signal.emit(self.index)
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def color(self):
|
|
102
|
+
return self.pen.color()
|
|
103
|
+
|
|
104
|
+
def center(self):
|
|
105
|
+
return QPointF(self.pos().x() + self.size().x() / 2, self.pos().y() + self.size().y() / 2)
|
|
90
106
|
|
|
91
107
|
def set_positions(self):
|
|
92
108
|
mapper = ROIPositionMapper(self.pos(), self.size())
|
|
@@ -100,6 +116,12 @@ class ROI(pgROI):
|
|
|
100
116
|
if self._menu is not None:
|
|
101
117
|
self._menu.exec(event.screenPos())
|
|
102
118
|
|
|
119
|
+
def width(self) -> float:
|
|
120
|
+
return self.size().x()
|
|
121
|
+
|
|
122
|
+
def height(self) -> float:
|
|
123
|
+
return self.size().y()
|
|
124
|
+
|
|
103
125
|
|
|
104
126
|
class ROIBrushable(ROI):
|
|
105
127
|
def __init__(self, brush=None, *args, **kwargs):
|
|
@@ -133,20 +155,29 @@ class ROIBrushable(ROI):
|
|
|
133
155
|
class LinearROI(pgLinearROI):
|
|
134
156
|
index_signal = Signal(int)
|
|
135
157
|
|
|
136
|
-
def __init__(self, index=0, pos=[0, 10], **kwargs):
|
|
158
|
+
def __init__(self, index=0, pos=[0, 10], name = 'roi', **kwargs):
|
|
137
159
|
super().__init__(values=pos, **kwargs)
|
|
160
|
+
self.name = name
|
|
138
161
|
self.index = index
|
|
139
162
|
self.sigRegionChangeFinished.connect(self.emit_index_signal)
|
|
140
163
|
|
|
141
|
-
def pos(self):
|
|
164
|
+
def pos(self) -> Tuple[float, float]:
|
|
142
165
|
return self.getRegion()
|
|
143
166
|
|
|
144
|
-
def
|
|
167
|
+
def center(self) -> float:
|
|
168
|
+
pos = self.pos()
|
|
169
|
+
return (pos[0] + pos[1]) / 2
|
|
170
|
+
|
|
171
|
+
def setPos(self, pos: Tuple[int, int]):
|
|
145
172
|
self.setRegion(pos)
|
|
146
173
|
|
|
147
174
|
def setPen(self, color):
|
|
148
175
|
self.setBrush(color)
|
|
149
176
|
|
|
177
|
+
@property
|
|
178
|
+
def color(self):
|
|
179
|
+
return self.brush.color()
|
|
180
|
+
|
|
150
181
|
def emit_index_signal(self):
|
|
151
182
|
self.index_signal.emit(self.index)
|
|
152
183
|
|
|
@@ -164,23 +195,13 @@ class EllipseROI(ROI):
|
|
|
164
195
|
============== =============================================================
|
|
165
196
|
|
|
166
197
|
"""
|
|
167
|
-
|
|
198
|
+
|
|
168
199
|
|
|
169
200
|
def __init__(self, index=0, pos=[0, 0], size=[10, 10], **kwargs):
|
|
170
201
|
# QtGui.QGraphicsRectItem.__init__(self, 0, 0, size[0], size[1])
|
|
171
|
-
super().__init__(pos=pos, size=size, **kwargs)
|
|
202
|
+
super().__init__(pos=pos, size=size, index=index, **kwargs)
|
|
172
203
|
self.addRotateHandle([1.0, 0.5], [0.5, 0.5])
|
|
173
204
|
self.addScaleHandle([0.5 * 2. ** -0.5 + 0.5, 0.5 * 2. ** -0.5 + 0.5], [0.5, 0.5])
|
|
174
|
-
self.index = index
|
|
175
|
-
self.sigRegionChangeFinished.connect(self.emit_index_signal)
|
|
176
|
-
|
|
177
|
-
def center(self):
|
|
178
|
-
# Project width/height in rotated frame
|
|
179
|
-
width,height = rotate2D((0,0),(self.size().x(),self.size().y()),np.deg2rad(self.angle()))
|
|
180
|
-
return QPointF(self.pos().x() + width / 2, self.pos().y() + height / 2)
|
|
181
|
-
|
|
182
|
-
def emit_index_signal(self):
|
|
183
|
-
self.index_signal.emit(self.index)
|
|
184
205
|
|
|
185
206
|
def getArrayRegion(self, arr, img=None, axes=(0, 1), **kwds):
|
|
186
207
|
"""
|
|
@@ -211,9 +232,6 @@ class EllipseROI(ROI):
|
|
|
211
232
|
else:
|
|
212
233
|
return arr * mask
|
|
213
234
|
|
|
214
|
-
def height(self):
|
|
215
|
-
return self.size().y()
|
|
216
|
-
|
|
217
235
|
def paint(self, p, opt, widget):
|
|
218
236
|
r = self.boundingRect()
|
|
219
237
|
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
|
@@ -229,9 +247,6 @@ class EllipseROI(ROI):
|
|
|
229
247
|
self.path.addEllipse(self.boundingRect())
|
|
230
248
|
return self.path
|
|
231
249
|
|
|
232
|
-
def width(self):
|
|
233
|
-
return self.size().x()
|
|
234
|
-
|
|
235
250
|
|
|
236
251
|
class SimpleRectROI(ROI):
|
|
237
252
|
r"""
|
|
@@ -252,22 +267,10 @@ class SimpleRectROI(ROI):
|
|
|
252
267
|
|
|
253
268
|
|
|
254
269
|
class RectROI(ROI):
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def __init__(self, index=0, pos=[0, 0], size=[10, 10]):
|
|
258
|
-
super().__init__(pos=pos, size=size) # , scaleSnap=True, translateSnap=True)
|
|
270
|
+
def __init__(self, index=0, pos=[0, 0], size=[10, 10], **kwargs):
|
|
271
|
+
super().__init__(pos=pos, size=size, index=index, **kwargs) # , scaleSnap=True, translateSnap=True)
|
|
259
272
|
self.addScaleHandle([1, 1], [0, 0])
|
|
260
273
|
self.addRotateHandle([0, 0], [0.5, 0.5])
|
|
261
|
-
self.index = index
|
|
262
|
-
self.sigRegionChangeFinished.connect(self.emit_index_signal)
|
|
263
|
-
|
|
264
|
-
def center(self):
|
|
265
|
-
# Project width/height in rotated frame
|
|
266
|
-
width,height = rotate2D((0,0),(self.size().x(),self.size().y()),np.deg2rad(self.angle()))
|
|
267
|
-
return QPointF(self.pos().x() + width / 2, self.pos().y() + height / 2)
|
|
268
|
-
|
|
269
|
-
def emit_index_signal(self):
|
|
270
|
-
self.index_signal.emit(self.index)
|
|
271
274
|
|
|
272
275
|
|
|
273
276
|
ROI_NAME_PREFIX = 'ROI_'
|
|
@@ -298,7 +301,7 @@ class ROIScalableGroup(GroupParameter):
|
|
|
298
301
|
if self.roi_type == '2D':
|
|
299
302
|
children.extend([{'title': 'ROI Type', 'name': 'roi_type', 'type': 'str', 'value': typ, 'readonly': True},
|
|
300
303
|
{'title': 'Use channel', 'name': 'use_channel', 'type': 'list',
|
|
301
|
-
'limits': ['red', 'green', 'blue'
|
|
304
|
+
'limits': ['red', 'green', 'blue']}, ])
|
|
302
305
|
children.append({'title': 'Math type:', 'name': 'math_function', 'type': 'list',
|
|
303
306
|
'limits': data_processors.functions_filtered('Data2D')})
|
|
304
307
|
else:
|
|
@@ -332,15 +335,13 @@ class ROIScalableGroup(GroupParameter):
|
|
|
332
335
|
|
|
333
336
|
|
|
334
337
|
class ROIManager(QObject):
|
|
335
|
-
ROI_changed = Signal()
|
|
336
|
-
ROI_changed_finished = Signal()
|
|
337
338
|
|
|
338
|
-
new_ROI_signal = Signal(int, str)
|
|
339
|
+
new_ROI_signal = Signal(int, str, str)
|
|
339
340
|
remove_ROI_signal = Signal(str)
|
|
340
341
|
roi_value_changed = Signal(str, tuple)
|
|
341
|
-
|
|
342
|
+
color_signal = Signal(list)
|
|
342
343
|
roi_update_children = Signal(list)
|
|
343
|
-
|
|
344
|
+
roi_changed = Signal()
|
|
344
345
|
color_list = np.array(plot_colors)
|
|
345
346
|
|
|
346
347
|
def __init__(self, viewer_widget=None, ROI_type='1D'):
|
|
@@ -348,7 +349,7 @@ class ROIManager(QObject):
|
|
|
348
349
|
self.ROI_type = ROI_type
|
|
349
350
|
self.roiwidget = QtWidgets.QWidget()
|
|
350
351
|
self.viewer_widget = viewer_widget # either a PlotWidget or a ImageWidget
|
|
351
|
-
self._ROIs = OrderedDict([])
|
|
352
|
+
self._ROIs: OrderedDict[str, ROI] = OrderedDict([])
|
|
352
353
|
self.setupUI()
|
|
353
354
|
|
|
354
355
|
@staticmethod
|
|
@@ -374,6 +375,9 @@ class ROIManager(QObject):
|
|
|
374
375
|
else:
|
|
375
376
|
raise KeyError(f'{roi_key} is not a valid ROI identifier for {self.ROIs}')
|
|
376
377
|
|
|
378
|
+
def emit_colors(self):
|
|
379
|
+
self.color_signal.emit([self._ROIs[roi_key].color for roi_key in self._ROIs])
|
|
380
|
+
|
|
377
381
|
def add_roi_programmatically(self, roitype=ROI2D_TYPES[0]):
|
|
378
382
|
self.settings.child('ROIs').addNew(roitype)
|
|
379
383
|
|
|
@@ -420,15 +424,16 @@ class ROIManager(QObject):
|
|
|
420
424
|
else:
|
|
421
425
|
childName = param.name()
|
|
422
426
|
if change == 'childAdded': # new roi to create
|
|
423
|
-
par = data[0]
|
|
427
|
+
par: Parameter = data[0]
|
|
424
428
|
newindex = int(par.name()[-2:])
|
|
425
|
-
|
|
429
|
+
roi_type = ''
|
|
426
430
|
if par.child('type').value() == '1D':
|
|
427
431
|
roi_type = ''
|
|
428
432
|
|
|
429
433
|
pos = self.viewer_widget.plotItem.vb.viewRange()[0]
|
|
430
434
|
pos = pos[0] + np.diff(pos)*np.array([2,4])/6
|
|
431
435
|
newroi = LinearROI(index=newindex, pos=pos)
|
|
436
|
+
|
|
432
437
|
newroi.setZValue(-10)
|
|
433
438
|
newroi.setBrush(par.child('Color').value())
|
|
434
439
|
newroi.setOpacity(0.2)
|
|
@@ -443,14 +448,13 @@ class ROIManager(QObject):
|
|
|
443
448
|
|
|
444
449
|
if roi_type == 'RectROI':
|
|
445
450
|
newroi = RectROI(index=newindex, pos=pos,
|
|
446
|
-
size=[width, height])
|
|
451
|
+
size=[width, height], name=par.name())
|
|
447
452
|
else:
|
|
448
453
|
newroi = EllipseROI(index=newindex, pos=pos,
|
|
449
|
-
size=[width, height])
|
|
454
|
+
size=[width, height], name=par.name())
|
|
450
455
|
newroi.setPen(par['Color'])
|
|
451
456
|
|
|
452
|
-
newroi.
|
|
453
|
-
newroi.sigRegionChangeFinished.connect(lambda: self.ROI_changed_finished.emit())
|
|
457
|
+
newroi.sigRegionChangeFinished.connect(lambda: self.roi_changed.emit())
|
|
454
458
|
newroi.index_signal[int].connect(self.update_roi_tree)
|
|
455
459
|
try:
|
|
456
460
|
self.settings.sigTreeStateChanged.disconnect()
|
|
@@ -461,27 +465,31 @@ class ROIManager(QObject):
|
|
|
461
465
|
|
|
462
466
|
self._set_roi_from_index(newindex, newroi)
|
|
463
467
|
|
|
464
|
-
self.new_ROI_signal.emit(newindex, roi_type)
|
|
468
|
+
self.new_ROI_signal.emit(newindex, roi_type, par.name())
|
|
465
469
|
self.update_roi_tree(newindex)
|
|
470
|
+
self.emit_colors()
|
|
471
|
+
self.roi_changed.emit()
|
|
466
472
|
|
|
467
473
|
elif change == 'value':
|
|
468
474
|
if param.name() in putils.iter_children(self.settings.child('ROIs'), []):
|
|
469
475
|
parent_name = putils.get_param_path(param)[putils.get_param_path(param).index('ROIs')+1]
|
|
470
476
|
self.update_roi(parent_name, param)
|
|
471
477
|
self.roi_value_changed.emit(parent_name, (param, param.value()))
|
|
478
|
+
if param.name() == 'Color':
|
|
479
|
+
self.emit_colors()
|
|
472
480
|
|
|
473
481
|
elif change == 'parent':
|
|
474
482
|
if 'ROI' in param.name():
|
|
475
483
|
roi = self._ROIs.pop(param.name())
|
|
476
484
|
self.viewer_widget.plotItem.removeItem(roi)
|
|
477
485
|
self.remove_ROI_signal.emit(param.name())
|
|
478
|
-
|
|
479
|
-
self.ROI_changed_finished.emit()
|
|
486
|
+
self.emit_colors()
|
|
480
487
|
|
|
481
488
|
def update_use_channel(self, channels: List[str]):
|
|
489
|
+
channels.append('All')
|
|
482
490
|
for ind in range(len(self)):
|
|
483
491
|
val = self.settings['ROIs', self.roi_format(ind), 'use_channel']
|
|
484
|
-
self.settings.child('ROIs', self.roi_format(ind), 'use_channel').
|
|
492
|
+
self.settings.child('ROIs', self.roi_format(ind), 'use_channel').setLimits(channels)
|
|
485
493
|
if val not in channels:
|
|
486
494
|
self.settings.child('ROIs', self.roi_format(ind), 'use_channel').setValue(channels[0])
|
|
487
495
|
|
|
@@ -489,6 +497,7 @@ class ROIManager(QObject):
|
|
|
489
497
|
self._ROIs[roi_key].index_signal[int].disconnect()
|
|
490
498
|
if param.name() == 'Color':
|
|
491
499
|
self._ROIs[roi_key].setPen(param.value())
|
|
500
|
+
self.emit_colors()
|
|
492
501
|
elif param.name() == 'left' or param.name() == 'x':
|
|
493
502
|
pos = self._ROIs[roi_key].pos()
|
|
494
503
|
poss = [param.value(), pos[1]]
|
pymodaq/utils/math_utils.py
CHANGED
|
@@ -105,7 +105,7 @@ def linspace_step(start, stop, step):
|
|
|
105
105
|
scalar array
|
|
106
106
|
The computed distribution axis as an array.
|
|
107
107
|
"""
|
|
108
|
-
if np.
|
|
108
|
+
if np.sign(stop - start) != np.sign(step) or start == stop:
|
|
109
109
|
raise ValueError('Invalid value for one parameter')
|
|
110
110
|
Nsteps = int(np.ceil((stop - start) / step))
|
|
111
111
|
new_stop = start + (Nsteps - 1) * step
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from .base import ViewersEnum
|
|
2
|
+
|
|
1
3
|
from .viewer0D import Viewer0D
|
|
2
4
|
from .viewer1D import Viewer1D
|
|
3
5
|
from .viewer2D import Viewer2D
|
|
4
6
|
from .viewerND import ViewerND
|
|
5
7
|
from .viewer import ViewerDispatcher
|
|
6
|
-
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
DATA_TYPES = ['Data0D', 'Data1D', 'Data2D', 'DataND']
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Union, TYPE_CHECKING, Iterable
|
|
3
|
+
|
|
4
|
+
from pymodaq.utils.enums import BaseEnum
|
|
5
|
+
from pyqtgraph.graphicsItems import InfiniteLine, ROI
|
|
6
|
+
from qtpy import QtWidgets
|
|
7
|
+
from qtpy.QtCore import QObject, Signal, QRectF
|
|
8
|
+
|
|
9
|
+
from pymodaq.utils.data import DataToExport, DataWithAxes, DataDim, DataDistribution
|
|
10
|
+
from pymodaq.utils.exceptions import ViewerError
|
|
11
|
+
from pymodaq.utils.plotting.utils.plot_utils import RoiInfo
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
|
|
15
|
+
from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
|
|
16
|
+
from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
|
|
17
|
+
from pymodaq.utils.plotting.data_viewers.viewerND import ViewerND
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ViewersEnum(BaseEnum):
|
|
21
|
+
"""enum relating a given viewer with data type"""
|
|
22
|
+
Viewer0D = 'Data0D'
|
|
23
|
+
Viewer1D = 'Data1D'
|
|
24
|
+
Viewer2D = 'Data2D'
|
|
25
|
+
ViewerND = 'DataND'
|
|
26
|
+
ViewerSequential = 'DataSequential'
|
|
27
|
+
|
|
28
|
+
def get_dim(self):
|
|
29
|
+
return self.value.split('Data')[1].split('D')[0]
|
|
30
|
+
|
|
31
|
+
def increase_dim(self, ndim: int):
|
|
32
|
+
dim = self.get_dim()
|
|
33
|
+
if dim != 'N':
|
|
34
|
+
dim_as_int = int(dim) + ndim
|
|
35
|
+
if dim_as_int > 2:
|
|
36
|
+
dim = 'N'
|
|
37
|
+
else:
|
|
38
|
+
dim = str(dim_as_int)
|
|
39
|
+
else:
|
|
40
|
+
dim = 'N'
|
|
41
|
+
return ViewersEnum[f'Viewer{dim}D']
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_n_axes(cls, n_axes: int):
|
|
45
|
+
if n_axes == 0:
|
|
46
|
+
return ViewersEnum['Viewer0D']
|
|
47
|
+
elif n_axes == 1:
|
|
48
|
+
return ViewersEnum['Viewer1D']
|
|
49
|
+
elif n_axes == 2:
|
|
50
|
+
return ViewersEnum['Viewer2D']
|
|
51
|
+
elif n_axes > 2:
|
|
52
|
+
return ViewersEnum['ViewerND']
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def get_viewers_enum_from_metadata(dim: DataDim,
|
|
56
|
+
distribution: DataDistribution,
|
|
57
|
+
n_nav_axes: int,
|
|
58
|
+
n_sig_indexes: int,
|
|
59
|
+
shape_len: int,
|
|
60
|
+
size: int) -> 'ViewersEnum':
|
|
61
|
+
if dim.name == 'Data0D':
|
|
62
|
+
viewer = 'Viewer0D'
|
|
63
|
+
elif dim.name == 'Data1D':
|
|
64
|
+
viewer = 'Viewer1D'
|
|
65
|
+
elif dim.name == 'Data2D':
|
|
66
|
+
viewer = 'Viewer2D'
|
|
67
|
+
else:
|
|
68
|
+
if distribution.name == 'uniform':
|
|
69
|
+
if shape_len < 3:
|
|
70
|
+
if shape_len == 1 and size == 1:
|
|
71
|
+
viewer = 'Viewer0D'
|
|
72
|
+
elif shape_len == 1 and size > 1:
|
|
73
|
+
viewer = 'Viewer1D'
|
|
74
|
+
elif shape_len == 2:
|
|
75
|
+
viewer = 'Viewer2D'
|
|
76
|
+
else:
|
|
77
|
+
viewer = 'ViewerND'
|
|
78
|
+
else:
|
|
79
|
+
viewer = 'ViewerND'
|
|
80
|
+
else:
|
|
81
|
+
if n_sig_indexes == 0:
|
|
82
|
+
if n_nav_axes == 1:
|
|
83
|
+
viewer = 'Viewer1D'
|
|
84
|
+
elif n_nav_axes == 2:
|
|
85
|
+
viewer = 'Viewer2D'
|
|
86
|
+
else:
|
|
87
|
+
viewer = 'ViewerND'
|
|
88
|
+
else:
|
|
89
|
+
viewer = 'ViewerND'
|
|
90
|
+
return ViewersEnum[viewer]
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def get_viewers_enum_from_data(dwa: DataWithAxes) -> 'ViewersEnum':
|
|
94
|
+
if dwa.dim.name == 'Data0D':
|
|
95
|
+
viewer = 'Viewer0D'
|
|
96
|
+
elif dwa.dim.name == 'Data1D':
|
|
97
|
+
viewer = 'Viewer1D'
|
|
98
|
+
elif dwa.dim.name == 'Data2D':
|
|
99
|
+
viewer = 'Viewer2D'
|
|
100
|
+
else:
|
|
101
|
+
if dwa.distribution.name == 'uniform':
|
|
102
|
+
if len(dwa.shape) < 3:
|
|
103
|
+
dwa.nav_indexes = ()
|
|
104
|
+
if len(dwa.shape) == 1 and dwa.size == 1:
|
|
105
|
+
viewer = 'Viewer0D'
|
|
106
|
+
elif len(dwa.shape) == 1 and dwa.size > 1:
|
|
107
|
+
viewer = 'Viewer1D'
|
|
108
|
+
elif len(dwa.shape) == 2:
|
|
109
|
+
viewer = 'Viewer2D'
|
|
110
|
+
else:
|
|
111
|
+
viewer = 'ViewerND'
|
|
112
|
+
else:
|
|
113
|
+
viewer = 'ViewerND'
|
|
114
|
+
else:
|
|
115
|
+
if len(dwa.sig_indexes) == 0:
|
|
116
|
+
if len(dwa.get_nav_axes()) == 1:
|
|
117
|
+
viewer = 'Viewer1D'
|
|
118
|
+
elif len(dwa.get_nav_axes()) == 2:
|
|
119
|
+
viewer = 'Viewer2D'
|
|
120
|
+
else:
|
|
121
|
+
viewer = 'ViewerND'
|
|
122
|
+
else:
|
|
123
|
+
viewer = 'ViewerND'
|
|
124
|
+
return ViewersEnum[viewer]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ViewerBase(QObject):
|
|
128
|
+
"""Base Class for data viewers implementing all common functionalities
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
parent: QtWidgets.QWidget
|
|
133
|
+
title: str
|
|
134
|
+
|
|
135
|
+
Attributes
|
|
136
|
+
----------
|
|
137
|
+
view: QObject
|
|
138
|
+
Ui interface of the viewer
|
|
139
|
+
|
|
140
|
+
data_to_export_signal: Signal[DataToExport]
|
|
141
|
+
ROI_changed: Signal
|
|
142
|
+
crosshair_dragged: Signal[float, float]
|
|
143
|
+
crosshair_clicked: Signal[bool]
|
|
144
|
+
sig_double_clicked: Signal[float, float]
|
|
145
|
+
status_signal: Signal[str]
|
|
146
|
+
"""
|
|
147
|
+
data_to_export_signal = Signal(DataToExport)
|
|
148
|
+
_data_to_show_signal = Signal(DataWithAxes)
|
|
149
|
+
|
|
150
|
+
ROI_changed = Signal()
|
|
151
|
+
crosshair_dragged = Signal(float, float) # Crosshair position in units of scaled top/right axes
|
|
152
|
+
status_signal = Signal(str)
|
|
153
|
+
crosshair_clicked = Signal(bool)
|
|
154
|
+
sig_double_clicked = Signal(float, float)
|
|
155
|
+
ROI_select_signal = Signal(QRectF) # deprecated: use roi_select_signal
|
|
156
|
+
roi_select_signal = Signal(RoiInfo)
|
|
157
|
+
|
|
158
|
+
def __init__(self, parent: QtWidgets.QWidget = None, title=''):
|
|
159
|
+
super().__init__()
|
|
160
|
+
self.title = title if title != '' else self.__class__.__name__
|
|
161
|
+
|
|
162
|
+
self._raw_data = None
|
|
163
|
+
self.data_to_export: DataToExport = DataToExport(name=self.title)
|
|
164
|
+
self.view: Union[Viewer0D, Viewer1D, Viewer2D, ViewerND] = None
|
|
165
|
+
|
|
166
|
+
if parent is None:
|
|
167
|
+
parent = QtWidgets.QWidget()
|
|
168
|
+
parent.show()
|
|
169
|
+
self.parent = parent
|
|
170
|
+
|
|
171
|
+
self._display_temporary = False
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def has_action(self):
|
|
175
|
+
"""Convenience method"""
|
|
176
|
+
if hasattr(self.view, 'has_action'):
|
|
177
|
+
return self.view.has_action
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def is_action_checked(self):
|
|
181
|
+
"""Convenience method"""
|
|
182
|
+
if hasattr(self.view, 'is_action_checked'):
|
|
183
|
+
return self.view.is_action_checked
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def is_action_visible(self):
|
|
187
|
+
"""Convenience method"""
|
|
188
|
+
if hasattr(self.view, 'is_action_visible'):
|
|
189
|
+
return self.view.is_action_visible
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def set_action_checked(self):
|
|
193
|
+
"""Convenience method"""
|
|
194
|
+
if hasattr(self.view, 'set_action_checked'):
|
|
195
|
+
return self.view.set_action_checked
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def set_action_visible(self):
|
|
199
|
+
"""Convenience method"""
|
|
200
|
+
if hasattr(self.view, 'set_action_visible'):
|
|
201
|
+
return self.view.set_action_visible
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def get_action(self):
|
|
205
|
+
"""Convenience method"""
|
|
206
|
+
if hasattr(self.view, 'get_action'):
|
|
207
|
+
return self.view.get_action
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def toolbar(self):
|
|
211
|
+
"""Convenience property"""
|
|
212
|
+
if hasattr(self.view, 'toolbar'):
|
|
213
|
+
return self.view.toolbar
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def viewer_type(self):
|
|
217
|
+
"""str: the viewer data type see DATA_TYPES"""
|
|
218
|
+
return ViewersEnum[self.__class__.__name__].value
|
|
219
|
+
|
|
220
|
+
def show_data(self, data: DataWithAxes, **kwargs):
|
|
221
|
+
"""Entrypoint to display data into the viewer
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
data: data_mod.DataFromPlugins
|
|
226
|
+
"""
|
|
227
|
+
if len(data.shape) > 4:
|
|
228
|
+
raise ViewerError(f'Ndarray of dim: {len(data.shape)} cannot be plotted using a {self.viewer_type}')
|
|
229
|
+
|
|
230
|
+
self.data_to_export = DataToExport(name=self.title)
|
|
231
|
+
self._raw_data = data
|
|
232
|
+
|
|
233
|
+
self._display_temporary = False
|
|
234
|
+
|
|
235
|
+
self._show_data(data, **kwargs)
|
|
236
|
+
|
|
237
|
+
def show_data_temp(self, data: DataWithAxes, **kwargs):
|
|
238
|
+
"""Entrypoint to display temporary data into the viewer
|
|
239
|
+
|
|
240
|
+
No processed data signal is emitted from the viewer
|
|
241
|
+
|
|
242
|
+
Parameters
|
|
243
|
+
----------
|
|
244
|
+
data: data_mod.DataFromPlugins
|
|
245
|
+
"""
|
|
246
|
+
self._display_temporary = True
|
|
247
|
+
self.show_data(data, **kwargs)
|
|
248
|
+
|
|
249
|
+
def _show_data(self, data: DataWithAxes, *args, **kwargs):
|
|
250
|
+
"""Specific viewers should implement it"""
|
|
251
|
+
raise NotImplementedError
|
|
252
|
+
|
|
253
|
+
def add_attributes_from_view(self):
|
|
254
|
+
"""Convenience function to add attributes from the view to self"""
|
|
255
|
+
for attribute in self.convenience_attributes:
|
|
256
|
+
if hasattr(self.view, attribute):
|
|
257
|
+
setattr(self, attribute, getattr(self.view, attribute))
|
|
258
|
+
|
|
259
|
+
def trigger_action(self, action_name: str):
|
|
260
|
+
"""Convenience function to trigger programmatically one of the action of the related view"""
|
|
261
|
+
if self.has_action(action_name):
|
|
262
|
+
self.get_action(action_name).trigger()
|
|
263
|
+
|
|
264
|
+
def activate_roi(self, activate=True):
|
|
265
|
+
"""Activate the Roi manager using the corresponding action"""
|
|
266
|
+
raise NotImplementedError
|
|
267
|
+
|
|
268
|
+
def setVisible(self, show=True):
|
|
269
|
+
"""convenience method to show or hide the paretn widget"""
|
|
270
|
+
self.parent.setVisible(show)
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def roi_target(self) -> Union[InfiniteLine.InfiniteLine, ROI.ROI]:
|
|
274
|
+
"""To be implemented if necessary (Viewer1D and above)"""
|
|
275
|
+
return None
|
|
276
|
+
|
|
277
|
+
def move_roi_target(self, pos: Iterable[float] = None, **kwargs):
|
|
278
|
+
"""move a specific read only ROI at the given position on the viewer"""
|
|
279
|
+
...
|
|
280
|
+
|
|
281
|
+
def show_roi_target(self, show=True):
|
|
282
|
+
"""Show/Hide a specific read only ROI"""
|
|
283
|
+
if self.roi_target is not None:
|
|
284
|
+
self.roi_target.setVisible(show)
|
|
285
|
+
|
|
286
|
+
|