pymodaq 4.1.4__py3-none-any.whl → 4.2.0__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.

Files changed (80) hide show
  1. pymodaq/__init__.py +41 -4
  2. pymodaq/control_modules/daq_move.py +33 -74
  3. pymodaq/control_modules/daq_viewer.py +73 -98
  4. pymodaq/control_modules/daq_viewer_ui.py +2 -1
  5. pymodaq/control_modules/move_utility_classes.py +17 -7
  6. pymodaq/control_modules/utils.py +153 -5
  7. pymodaq/control_modules/viewer_utility_classes.py +31 -20
  8. pymodaq/dashboard.py +23 -5
  9. pymodaq/examples/tcp_client.py +97 -0
  10. pymodaq/extensions/__init__.py +4 -0
  11. pymodaq/extensions/bayesian/__init__.py +2 -0
  12. pymodaq/extensions/bayesian/bayesian_optimisation.py +673 -0
  13. pymodaq/extensions/bayesian/utils.py +403 -0
  14. pymodaq/extensions/daq_scan.py +4 -4
  15. pymodaq/extensions/daq_scan_ui.py +2 -1
  16. pymodaq/extensions/pid/pid_controller.py +12 -7
  17. pymodaq/extensions/pid/utils.py +9 -26
  18. pymodaq/extensions/utils.py +3 -0
  19. pymodaq/post_treatment/load_and_plot.py +42 -19
  20. pymodaq/resources/VERSION +1 -1
  21. pymodaq/resources/config_template.toml +9 -24
  22. pymodaq/resources/setup_plugin.py +1 -1
  23. pymodaq/utils/config.py +103 -5
  24. pymodaq/utils/daq_utils.py +37 -138
  25. pymodaq/utils/data.py +614 -95
  26. pymodaq/utils/enums.py +17 -1
  27. pymodaq/utils/factory.py +2 -2
  28. pymodaq/utils/gui_utils/custom_app.py +5 -2
  29. pymodaq/utils/gui_utils/dock.py +33 -4
  30. pymodaq/utils/gui_utils/file_io.py +3 -2
  31. pymodaq/utils/gui_utils/utils.py +14 -1
  32. pymodaq/utils/h5modules/backends.py +9 -1
  33. pymodaq/utils/h5modules/data_saving.py +254 -57
  34. pymodaq/utils/h5modules/saving.py +1 -0
  35. pymodaq/utils/leco/daq_move_LECODirector.py +172 -0
  36. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +170 -0
  37. pymodaq/utils/leco/desktop.ini +2 -0
  38. pymodaq/utils/leco/director_utils.py +58 -0
  39. pymodaq/utils/leco/leco_director.py +88 -0
  40. pymodaq/utils/leco/pymodaq_listener.py +279 -0
  41. pymodaq/utils/leco/utils.py +41 -0
  42. pymodaq/utils/managers/action_manager.py +20 -6
  43. pymodaq/utils/managers/parameter_manager.py +6 -4
  44. pymodaq/utils/managers/roi_manager.py +64 -55
  45. pymodaq/utils/math_utils.py +1 -1
  46. pymodaq/utils/plotting/data_viewers/__init__.py +3 -1
  47. pymodaq/utils/plotting/data_viewers/base.py +286 -0
  48. pymodaq/utils/plotting/data_viewers/viewer.py +29 -202
  49. pymodaq/utils/plotting/data_viewers/viewer0D.py +94 -47
  50. pymodaq/utils/plotting/data_viewers/viewer1D.py +341 -174
  51. pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py +1 -1
  52. pymodaq/utils/plotting/data_viewers/viewer2D.py +271 -181
  53. pymodaq/utils/plotting/data_viewers/viewerND.py +26 -22
  54. pymodaq/utils/plotting/items/crosshair.py +3 -3
  55. pymodaq/utils/plotting/items/image.py +2 -1
  56. pymodaq/utils/plotting/plotter/plotter.py +94 -0
  57. pymodaq/utils/plotting/plotter/plotters/__init__.py +0 -0
  58. pymodaq/utils/plotting/plotter/plotters/matplotlib_plotters.py +134 -0
  59. pymodaq/utils/plotting/plotter/plotters/qt_plotters.py +78 -0
  60. pymodaq/utils/plotting/utils/axes_viewer.py +1 -1
  61. pymodaq/utils/plotting/utils/filter.py +194 -147
  62. pymodaq/utils/plotting/utils/lineout.py +13 -11
  63. pymodaq/utils/plotting/utils/plot_utils.py +89 -12
  64. pymodaq/utils/scanner/__init__.py +0 -3
  65. pymodaq/utils/scanner/scan_config.py +1 -9
  66. pymodaq/utils/scanner/scan_factory.py +10 -36
  67. pymodaq/utils/scanner/scanner.py +3 -2
  68. pymodaq/utils/scanner/scanners/_1d_scanners.py +7 -5
  69. pymodaq/utils/scanner/scanners/_2d_scanners.py +36 -49
  70. pymodaq/utils/scanner/scanners/sequential.py +10 -4
  71. pymodaq/utils/scanner/scanners/tabular.py +10 -5
  72. pymodaq/utils/slicing.py +1 -1
  73. pymodaq/utils/tcp_ip/serializer.py +38 -5
  74. pymodaq/utils/tcp_ip/tcp_server_client.py +25 -17
  75. {pymodaq-4.1.4.dist-info → pymodaq-4.2.0.dist-info}/METADATA +4 -2
  76. {pymodaq-4.1.4.dist-info → pymodaq-4.2.0.dist-info}/RECORD +79 -64
  77. {pymodaq-4.1.4.dist-info → pymodaq-4.2.0.dist-info}/WHEEL +1 -1
  78. pymodaq/resources/config_scan_template.toml +0 -42
  79. {pymodaq-4.1.4.dist-info → pymodaq-4.2.0.dist-info}/entry_points.txt +0 -0
  80. {pymodaq-4.1.4.dist-info → pymodaq-4.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,28 +3,31 @@ import copy
3
3
  import datetime
4
4
  import numpy as np
5
5
  import sys
6
- from typing import Union, Iterable
6
+ from typing import Union, Iterable, List, Dict
7
7
 
8
8
  import pymodaq.utils.messenger
9
9
  from qtpy import QtCore, QtGui, QtWidgets
10
- from qtpy.QtCore import QObject, Slot, Signal
10
+ from qtpy.QtCore import QObject, Slot, Signal, Qt
11
11
  import pyqtgraph as pg
12
12
  from pyqtgraph.graphicsItems.GradientEditorItem import Gradients
13
13
  from pyqtgraph import ROI as pgROI
14
14
 
15
- from pymodaq.utils.data import Axis, DataToExport, DataFromRoi, DataFromPlugins, DataRaw, DataDistribution, DataWithAxes
15
+ from pymodaq.utils.data import (Axis, DataToExport, DataFromRoi, DataFromPlugins, DataRaw,
16
+ DataDistribution, DataWithAxes)
16
17
  from pymodaq.utils.logger import set_logger, get_module_name
17
18
  from pymodaq.utils.managers.roi_manager import ROIManager, SimpleRectROI
18
19
  from pymodaq.utils.managers.action_manager import ActionManager
19
20
  from pymodaq.utils.plotting.widgets import ImageWidget
20
21
 
21
22
  from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase
23
+ from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
24
+ from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
22
25
  from pymodaq.utils.plotting.items.image import UniformImageItem, SpreadImageItem
23
26
  from pymodaq.utils.plotting.items.axis_scaled import AXIS_POSITIONS, AxisItem_Scaled
24
27
  from pymodaq.utils.plotting.items.crosshair import Crosshair
25
28
  from pymodaq.utils.plotting.utils.filter import Filter2DFromCrosshair, Filter2DFromRois
26
29
  import pymodaq.utils.daq_utils as utils
27
- from pymodaq.utils.plotting.utils.lineout import LineoutPlotter, curve_item_factory
30
+ from pymodaq.utils.plotting.utils.plot_utils import make_dashed_pens, RoiInfo
28
31
 
29
32
 
30
33
  logger = set_logger(get_module_name(__file__))
@@ -36,17 +39,20 @@ Gradients.update(OrderedDict([
36
39
  ('spread', {'ticks': [(0.0, (0, 0, 0, 255)), (1.0, (255, 255, 255, 255))], 'mode': 'rgb'}),]))
37
40
 
38
41
  COLORS_DICT = dict(red=(255, 0, 0), green=(0, 255, 0), blue=(0, 0, 255), spread=(128, 128, 128))
39
- IMAGE_TYPES = ['red', 'green', 'blue']
40
42
 
43
+
44
+
45
+ IMAGE_TYPES = ['red', 'green', 'blue']
41
46
  COLOR_LIST = utils.plot_colors
47
+ crosshair_pens = make_dashed_pens(color=(255, 255, 0))
42
48
 
43
49
 
44
- def image_item_factory(item_type='uniform', axisOrder='row-major'):
50
+ def image_item_factory(item_type='uniform', axisOrder='row-major', pen='r'):
45
51
  if item_type == 'uniform':
46
- image = UniformImageItem()
52
+ image = UniformImageItem(pen=pen)
47
53
  image.setOpts(axisOrder=axisOrder)
48
54
  elif item_type == 'spread':
49
- image = SpreadImageItem()
55
+ image = SpreadImageItem(pen=pen)
50
56
  image.setCompositionMode(QtGui.QPainter.CompositionMode_Plus)
51
57
  return image
52
58
 
@@ -85,12 +91,22 @@ class ImageDisplayer(QObject):
85
91
  def __init__(self, plotitem, data_distribution: DataDistribution):
86
92
  super().__init__()
87
93
  self._plotitem = plotitem
94
+ self._plotitem.addLegend()
95
+ self.show_legend(False)
88
96
  self.display_type = data_distribution
89
97
  self._image_items = dict([])
90
98
  self._autolevels = False
99
+ self._data: DataWithAxes = None
91
100
 
92
101
  self.update_display_items()
93
102
 
103
+ def show_legend(self, show=True):
104
+ self.legend.setVisible(show)
105
+
106
+ @property
107
+ def legend(self):
108
+ return self._plotitem.legend
109
+
94
110
  def get_images(self):
95
111
  return self._image_items
96
112
 
@@ -100,6 +116,13 @@ class ImageDisplayer(QObject):
100
116
  else:
101
117
  return self._image_items[name]
102
118
 
119
+ @property
120
+ def labels(self):
121
+ if self._data is None:
122
+ return []
123
+ else:
124
+ return self._data.labels
125
+
103
126
  @property
104
127
  def autolevels(self):
105
128
  return self._autolevels
@@ -108,26 +131,36 @@ class ImageDisplayer(QObject):
108
131
  def set_autolevels(self, isautolevel):
109
132
  self._autolevels = isautolevel
110
133
 
111
- def update_data(self, data):
112
- if data.distribution != self.display_type:
113
- self.display_type = data.distribution
114
- self.update_display_items()
115
- for ind_data, data_array in enumerate(data.data):
134
+ def update_data(self, dwa: DataWithAxes):
135
+ if dwa.labels != self.labels:
136
+ self.update_display_items(dwa.labels)
137
+ if dwa.distribution != self.display_type:
138
+ self.display_type = dwa.distribution
139
+ self._data = dwa
140
+ for ind_data, data_array in enumerate(dwa.data):
116
141
  if data_array.size > 0:
117
142
  if self.display_type == 'uniform':
118
143
  self._image_items[IMAGE_TYPES[ind_data]].setImage(data_array, self.autolevels)
119
144
  else:
120
- nav_axes = data.get_nav_axes()
121
- data_array = np.stack((nav_axes[0].data, nav_axes[1].data, data_array), axis=0).T
145
+ nav_axes = dwa.get_nav_axes()
146
+ data_array = np.stack((nav_axes[0].get_data(),
147
+ nav_axes[1].get_data(),
148
+ data_array), axis=0).T
122
149
  self._image_items[IMAGE_TYPES[ind_data]].setImage(data_array, self.autolevels)
123
150
 
124
- def update_display_items(self):
151
+ def update_display_items(self, labels: List[str] = None):
125
152
  while len(self._image_items) > 0:
126
153
  self._plotitem.removeItem(self._image_items.pop(next(iter(self._image_items))))
154
+ if labels is None:
155
+ labels = []
156
+ while len(labels) != len(IMAGE_TYPES):
157
+ labels.append(IMAGE_TYPES[len(labels)])
127
158
 
128
- for img_key in IMAGE_TYPES:
129
- self._image_items[img_key] = image_item_factory(self.display_type)
159
+ for ind, img_key in enumerate(IMAGE_TYPES):
160
+ self._image_items[img_key] = image_item_factory(self.display_type, pen=img_key[0])
130
161
  self._plotitem.addItem(self._image_items[img_key])
162
+ if ind < len(labels):
163
+ self.legend.addItem(self._image_items[img_key], labels[ind])
131
164
  self.updated_item.emit(self._image_items)
132
165
 
133
166
  def update_image_visibility(self, are_items_visible):
@@ -257,56 +290,22 @@ class IsoCurver(QObject):
257
290
  self._isoLine.hide()
258
291
 
259
292
 
260
- class LineoutPlotter(LineoutPlotter):
261
- """class to manage and display data filtered out into lineouts (1D, 0D)
262
-
263
- Should be inherited and subclass some methods as appropriate
264
-
265
- Parameters
266
- ----------
267
- graph_widgets: OrderedDict
268
- Includes plotwidgets to display data
269
- roi_manager:
270
- The ROIManager to create ROIs and manage their properties
271
- crosshair:
272
- The Crosshair object
273
- """
274
- lineout_widgets = ['hor', 'ver', 'int']
275
-
276
- def __init__(self, graph_widgets: OrderedDict, roi_manager: ROIManager, crosshair: Crosshair):
277
- super().__init__(graph_widgets, roi_manager, crosshair)
278
-
279
- def plot_other_lineouts(self, roi_dicts):
280
- for roi_key, lineout_data in roi_dicts.items():
281
- if roi_key in self._roi_curves:
282
- if lineout_data.hor_data.size > 0:
283
- self._roi_curves[roi_key]['hor'].setData(lineout_data.hor_axis, lineout_data.hor_data)
284
- self._roi_curves[roi_key]['ver'].setData(lineout_data.ver_data, lineout_data.ver_axis)
285
-
286
- def plot_other_crosshair_lineouts(self, crosshair_dict):
287
- for data_key, lineout_data in crosshair_dict.items():
288
- if data_key in self._crosshair_curves:
289
- self._crosshair_curves[data_key]['hor'].setData(lineout_data.hor_axis, lineout_data.hor_data)
290
- self._crosshair_curves[data_key]['ver'].setData(lineout_data.ver_data, lineout_data.ver_axis)
291
-
292
- def setup_crosshair(self):
293
- for image_key in IMAGE_TYPES:
294
- self._crosshair_curves[image_key] = \
295
- {curv_key: curve_item_factory(image_key) for curv_key in self.lineout_widgets}
296
- self.add_lineout_items(self._crosshair_curves[image_key]['hor'], self._crosshair_curves[image_key]['ver'])
297
-
298
- def show_crosshair_curves(self, curve_key, show=True):
299
- for curve in self._crosshair_curves[curve_key].values():
300
- curve.setVisible(show)
301
-
302
-
303
293
  class View2D(ActionManager, QtCore.QObject):
304
294
 
295
+ lineout_types = ['hor', 'ver', 'int']
296
+
305
297
  def __init__(self, parent_widget=None):
306
298
  QtCore.QObject.__init__(self)
307
299
  ActionManager.__init__(self, toolbar=QtWidgets.QToolBar())
300
+
308
301
  self.ROIselect = SimpleRectROI([0, 0], [10, 10], centered=True, sideScalers=True)
309
302
 
303
+ self._lineout_widgets = {widg_key: QtWidgets.QWidget() for widg_key in self.lineout_types}
304
+ self.lineout_viewers: Dict[str, Viewer1D] = dict(hor=Viewer1D(self._lineout_widgets['hor'], show_toolbar=False, no_margins=True),
305
+ ver=Viewer1D(self._lineout_widgets['ver'], show_toolbar=False, no_margins=True,
306
+ flip_axes=True),
307
+ int=Viewer0D(self._lineout_widgets['int'], show_toolbar=False, no_margins=True))
308
+
310
309
  self.setup_actions()
311
310
 
312
311
  self.parent_widget = parent_widget
@@ -317,8 +316,7 @@ class View2D(ActionManager, QtCore.QObject):
317
316
  self.image_widget = ImageWidget()
318
317
  self.roi_manager = ROIManager(self.image_widget, '2D')
319
318
 
320
- self.roi_target = pgROI(pos=(0, 0), size=(20,20), movable=False, rotatable=False, resizable=False)
321
- self.roi_target.setVisible(False)
319
+ self.roi_target: Union[pgROI, Crosshair] = None
322
320
 
323
321
  self.setup_widgets()
324
322
 
@@ -327,7 +325,6 @@ class View2D(ActionManager, QtCore.QObject):
327
325
  self.isocurver: IsoCurver = None
328
326
 
329
327
  self.crosshair = Crosshair(self.image_widget)
330
- self.lineout_plotter = LineoutPlotter(self.graphical_widgets, self.roi_manager, self.crosshair)
331
328
 
332
329
  self.connect_things()
333
330
  self.prepare_ui()
@@ -346,36 +343,51 @@ class View2D(ActionManager, QtCore.QObject):
346
343
  self.clear_plot_item()
347
344
  self.data_displayer = ImageDisplayer(self.plotitem, data_distribution)
348
345
  self.isocurver = IsoCurver(self.data_displayer.get_image('red'), self.histogrammer.get_histogram('red'))
346
+ self.connect_action('isocurve', self.isocurver.show_hide_iso)
349
347
  self.data_displayer.updated_item.connect(self.histogrammer.affect_histo_to_imageitems)
350
348
  self.connect_action('autolevels', self.data_displayer.set_autolevels)
351
349
  for key in IMAGE_TYPES:
352
350
  self.connect_action(key, self.notify_visibility_data_displayer)
353
- self.connect_action('isocurve', self.isocurver.show_hide_iso)
351
+
354
352
  self.histogrammer.affect_histo_to_imageitems(self.data_displayer.get_images())
355
353
 
354
+ if data_distribution.name == 'uniform':
355
+ self.roi_target = pgROI(pos=(0, 0), size=(20, 20), movable=False, rotatable=False,
356
+ resizable=False)
357
+ self.plotitem.addItem(self.roi_target)
358
+
359
+ elif data_distribution.name == 'spread':
360
+ self.roi_target = Crosshair(self.image_widget, pen=(255, 255, 255))
361
+ self.roi_target.setVisible(False)
362
+
356
363
  def show_roi_target(self, show=True):
357
364
  self.roi_target.setVisible(show)
358
365
 
359
366
  def move_scale_roi_target(self, pos=None, size=None):
360
367
  """
361
- Move and scale the target ROI (used to display a particular area, for instance the currently scanned points
368
+ Move and scale the target ROI (used to display a particular area,
369
+ for instance the currently scanned points
362
370
  during a scan
363
371
  Parameters
364
372
  ----------
365
- pos: (iterable) precising the central position of the ROI in the view
366
- size: (iterable) precising the size of the ROI
373
+ pos: (iterable) setting the central position of the ROI in the view
374
+ size: (iterable) setting the size of the ROI
367
375
  """
368
- if size is not None:
369
- x_offset, x_scaling, y_offset, y_scaling = self._get_axis_scaling_offset()
370
- size = list(np.divide(list(size), [x_scaling, y_scaling]))
371
- if list(self.roi_target.size()) != size:
372
- self.roi_target.setSize(size, center=(0.5, 0.5))
373
-
374
- if pos is not None:
375
- pos = self.unscale_axis(*list(pos))
376
- pos = list(pos)
377
- if list(self.roi_target.pos()) != pos:
378
- self.roi_target.setPos(pos)
376
+ if isinstance(self.roi_target, pgROI):
377
+ if size is not None:
378
+ x_offset, x_scaling, y_offset, y_scaling = self._get_axis_scaling_offset()
379
+ size = list(np.divide(list(size), [x_scaling, y_scaling]))
380
+ if list(self.roi_target.size()) != size:
381
+ self.roi_target.setSize(size, center=(0.5, 0.5))
382
+
383
+ if pos is not None:
384
+ pos = self.unscale_axis(*list(pos))
385
+ pos = list(pos)
386
+ if list(self.roi_target.pos()) != pos:
387
+ self.roi_target.setPos(pos)
388
+
389
+ else:
390
+ self.roi_target.set_crosshair_position(*list(pos))
379
391
 
380
392
  def setup_widgets(self):
381
393
  vertical_layout = QtWidgets.QVBoxLayout()
@@ -392,11 +404,13 @@ class View2D(ActionManager, QtCore.QObject):
392
404
  splitter_vertical.addWidget(self.graphs_widget)
393
405
 
394
406
  self.plotitem.addItem(self.ROIselect)
395
- self.plotitem.addItem(self.roi_target)
396
407
 
397
408
  self.splitter_VLeft.splitterMoved[int, int].connect(self.move_right_splitter)
398
409
  self.splitter_VRight.splitterMoved[int, int].connect(self.move_left_splitter)
399
410
 
411
+ self.splitter_VLeft.setSizes([1, 0])
412
+ self.splitter_VRight.setSizes([1, 0])
413
+
400
414
  def setup_graphs(self, graphs_layout):
401
415
  self.splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
402
416
  graphs_layout.addWidget(self.splitter)
@@ -411,8 +425,6 @@ class View2D(ActionManager, QtCore.QObject):
411
425
  self.splitter.addWidget(self.splitter_VLeft)
412
426
  self.splitter.addWidget(self.splitter_VRight)
413
427
 
414
- self._lineout_widgets = {widg_key: pg.PlotWidget() for widg_key in LineoutPlotter.lineout_widgets}
415
- self.graphical_widgets = dict(lineouts=self._lineout_widgets, image=self.image_widget)
416
428
  self.splitter_VLeft.addWidget(self.image_widget)
417
429
  self.splitter_VLeft.addWidget(self._lineout_widgets['hor'])
418
430
  self.splitter_VRight.addWidget(self._lineout_widgets['ver'])
@@ -431,6 +443,10 @@ class View2D(ActionManager, QtCore.QObject):
431
443
  self.add_action('red', 'Red Channel', 'r_icon', tip='Show/Hide Red Channel', checkable=True)
432
444
  self.add_action('green', 'Green Channel', 'g_icon', tip='Show/Hide Green Channel', checkable=True)
433
445
  self.add_action('blue', 'Blue Channel', 'b_icon', tip='Show/Hide Blue Channel', checkable=True)
446
+ self.get_action('red').setChecked(True)
447
+ self.get_action('green').setChecked(True)
448
+ self.get_action('blue').setChecked(True)
449
+
434
450
  self.add_action('autolevels', 'AutoLevels', 'autoscale',
435
451
  tip='Scale Histogram to Min/Max intensity', checkable=True)
436
452
  self.add_action('auto_levels_sym', 'AutoLevels Sym.', 'autoscale',
@@ -449,25 +465,54 @@ class View2D(ActionManager, QtCore.QObject):
449
465
  tip='Flip the image left/right', checkable=True)
450
466
  self.add_action('rotate', 'Rotate', 'rotation2',
451
467
  tip='Rotate the image', checkable=True)
468
+ self.add_action('legend', 'Legend', 'RGB',
469
+ tip='Show the legend', checkable=True)
452
470
 
453
- def connect_things(self):
454
-
455
-
471
+ def update_colors(self, colors: list):
472
+ for ind, roi_name in enumerate(self.roi_manager.ROIs):
473
+ self.lineout_viewers['hor'].update_colors(make_dashed_pens(colors[ind]), displayer=roi_name)
474
+ self.lineout_viewers['ver'].update_colors(make_dashed_pens(colors[ind]), displayer=roi_name)
475
+ self.lineout_viewers['int'].update_colors(make_dashed_pens(colors[ind]), displayer=roi_name)
456
476
 
477
+ def connect_things(self):
457
478
  self.connect_action('histo', self.histogrammer.activated)
458
479
  self.connect_action('autolevels', self.histogrammer.set_autolevels)
459
-
480
+ self.roi_manager.new_ROI_signal.connect(self.update_roi_channels)
481
+ self.roi_manager.new_ROI_signal.connect(self.add_roi_displayer)
482
+ self.roi_manager.new_ROI_signal.connect(self.lineout_viewers['int'].get_action('clear').click)
483
+ self.roi_manager.remove_ROI_signal.connect(self.remove_roi_displayer)
484
+ self.roi_manager.color_signal.connect(self.update_colors)
460
485
  self.connect_action('isocurve', self.get_action('histo').trigger)
461
486
 
462
487
  self.connect_action('aspect_ratio', self.lock_aspect_ratio)
463
488
  self.connect_action('histo', self.show_hide_histogram)
464
- self.connect_action('roi', self.lineout_plotter.roi_clicked)
465
489
  self.connect_action('roi', self.show_lineout_widgets)
490
+ self.connect_action('roi', self.roi_clicked)
466
491
  self.connect_action('ROIselect', self.show_ROI_select)
467
492
  self.connect_action('crosshair', self.show_hide_crosshair)
468
493
  self.connect_action('crosshair', self.show_lineout_widgets)
469
- self.connect_action('crosshair', self.lineout_plotter.crosshair_clicked)
494
+ self.connect_action('legend', self.show_legend)
470
495
 
496
+ def show_legend(self, show=True):
497
+ self.data_displayer.show_legend(show)
498
+
499
+ @Slot(int, str, str)
500
+ def add_roi_displayer(self, index, roi_type='', roi_name=''):
501
+ color = self.roi_manager.ROIs[roi_name].color
502
+ self.lineout_viewers['hor'].view.add_data_displayer(roi_name, make_dashed_pens(color))
503
+ self.lineout_viewers['ver'].view.add_data_displayer(roi_name, make_dashed_pens(color))
504
+ self.lineout_viewers['int'].view.add_data_displayer(roi_name, make_dashed_pens(color))
505
+
506
+ @Slot(str)
507
+ def remove_roi_displayer(self, roi_name=''):
508
+ self.lineout_viewers['hor'].view.remove_data_displayer(roi_name)
509
+ self.lineout_viewers['ver'].view.remove_data_displayer(roi_name)
510
+ self.lineout_viewers['int'].view.remove_data_displayer(roi_name)
511
+
512
+ @Slot(int, str, str)
513
+ def update_roi_channels(self, index, roi_type=''):
514
+ """Update the use_channel setting each time a ROI is added"""
515
+ self.roi_manager.update_use_channel(self.data_displayer.labels.copy())
471
516
 
472
517
  def prepare_ui(self):
473
518
  self.ROIselect.setVisible(False)
@@ -480,26 +525,39 @@ class View2D(ActionManager, QtCore.QObject):
480
525
  if self.is_action_checked('isocurve'):
481
526
  self.isocurver.set_isocurve_data(datas.data[0])
482
527
 
483
- def display_roi_lineouts(self, roi_dict):
484
- self.lineout_plotter.plot_roi_lineouts(roi_dict)
485
-
486
- def display_crosshair_lineouts(self, crosshair_dict):
487
- self.lineout_plotter.plot_crosshair_lineouts(crosshair_dict)
528
+ def display_roi_lineouts(self, roi_dte: DataToExport):
529
+ if len(roi_dte) > 0:
530
+ for lineout_type in self.lineout_types:
531
+ for displayer_name in self.lineout_viewers[lineout_type].view.other_data_displayers:
532
+ dwa = roi_dte.get_data_from_name_origin(lineout_type, displayer_name)
533
+ if dwa is not None:
534
+ self.lineout_viewers[lineout_type].view.display_data(dwa.deepcopy(),
535
+ displayer=displayer_name)
536
+
537
+ def display_crosshair_lineouts(self, crosshair_dte: DataToExport):
538
+ for lineout_type in self.lineout_types:
539
+ dwa = crosshair_dte.get_data_from_name(lineout_type)
540
+ if dwa is not None:
541
+ self.lineout_viewers[lineout_type].view.display_data(dwa, displayer='crosshair')
488
542
 
489
543
  def show_lineout_widgets(self):
490
544
  state = self.is_action_checked('roi') or self.is_action_checked('crosshair')
491
- for lineout_name in LineoutPlotter.lineout_widgets:
492
- lineout = self.lineout_plotter.get_lineout_widget(lineout_name)
493
- lineout.setMouseEnabled(state, state)
494
- lineout.showAxis('left', state)
495
- lineout.setVisible(state)
496
- lineout.update()
497
- self.prepare_image_widget_for_lineouts()
545
+ if state:
546
+ self.prepare_image_widget_for_lineouts()
547
+ else:
548
+ self.prepare_image_widget_for_lineouts(1)
549
+
550
+ @Slot(bool)
551
+ def roi_clicked(self, isroichecked=True):
552
+ self.roi_manager.roiwidget.setVisible(isroichecked)
553
+
554
+ for k, roi in self.roi_manager.ROIs.items():
555
+ roi.setVisible(isroichecked)
498
556
 
499
557
  def get_visible_images(self):
500
558
  are_items_visible = []
501
559
  for key in IMAGE_TYPES:
502
- are_items_visible.append(self.is_action_checked(key))
560
+ are_items_visible.append(self.is_action_visible(key) and self.is_action_checked(key))
503
561
  return are_items_visible
504
562
 
505
563
  def notify_visibility_data_displayer(self):
@@ -519,6 +577,9 @@ class View2D(ActionManager, QtCore.QObject):
519
577
  self.splitter_VLeft.splitterMoved[int, int].emit(int(ratio * self.parent_widget.height()), 1)
520
578
  QtGui.QGuiApplication.processEvents()
521
579
 
580
+ def collapse_lineout_widgets(self):
581
+ self.prepare_image_widget_for_lineouts(ratio=1)
582
+
522
583
  def get_view_range(self):
523
584
  return self.image_widget.view.viewRange()
524
585
 
@@ -571,8 +632,15 @@ class View2D(ActionManager, QtCore.QObject):
571
632
  self.set_action_visible('position', show)
572
633
  self.crosshair.setVisible(show)
573
634
  if show:
635
+ self.lineout_viewers['hor'].view.add_data_displayer('crosshair', plot_colors=crosshair_pens)
636
+ self.lineout_viewers['ver'].view.add_data_displayer('crosshair', plot_colors=crosshair_pens)
637
+ self.lineout_viewers['int'].view.add_data_displayer('crosshair', plot_colors=crosshair_pens)
574
638
  range = self.get_view_range()
575
639
  self.set_crosshair_position(np.mean(np.array(range[0])), np.mean(np.array(range[0])))
640
+ else:
641
+ self.lineout_viewers['hor'].view.remove_data_displayer('crosshair')
642
+ self.lineout_viewers['ver'].view.remove_data_displayer('crosshair')
643
+ self.lineout_viewers['int'].view.remove_data_displayer('crosshair')
576
644
  logger.debug(f'Crosshair visible?: {self.crosshair.isVisible()}')
577
645
 
578
646
  def show_ROI_select(self):
@@ -581,6 +649,13 @@ class View2D(ActionManager, QtCore.QObject):
581
649
  self.ROIselect.setPos(rect.center()-QtCore.QPointF(rect.width() * 2 / 3, rect.height() * 2 / 3)/2)
582
650
  self.ROIselect.setSize(rect.size() * 2 / 3)
583
651
 
652
+ def set_image_labels(self, labels: List[str]):
653
+ action_names =['red', 'green', 'blue']
654
+ for action_name, label in zip(action_names[:len(labels)], labels):
655
+ self.get_action(action_name).setToolTip(f'{self.get_action(action_name).toolTip()}'
656
+ f' - '
657
+ f'{label}')
658
+
584
659
  def set_axis_label(self, position, label='', units=''):
585
660
  """
586
661
  Convenience method to set label and unit of any view axes
@@ -637,12 +712,11 @@ class View2D(ActionManager, QtCore.QObject):
637
712
  class Viewer2D(ViewerBase):
638
713
  """Object managing plotting and manipulation of 2D data using a View2D"""
639
714
 
640
- crosshair_clicked = Signal(bool)
641
- ROI_select_signal = Signal(QtCore.QRectF)
642
-
643
715
  def __init__(self, parent: QtWidgets.QWidget = None, title=''):
644
716
  super().__init__(parent, title)
645
717
 
718
+ self.just_init = True
719
+
646
720
  self._datas = None
647
721
  self.isdata = dict([])
648
722
  self._is_gradient_manually_set = False
@@ -696,8 +770,7 @@ class Viewer2D(ViewerBase):
696
770
  self.view.set_action_checked('roi', activate)
697
771
  self.view.get_action('roi').triggered.emit(activate)
698
772
 
699
- @Slot(dict)
700
- def roi_changed(self):
773
+ def roi_changed(self, *args, **kwargs):
701
774
  self.filter_from_rois.filter_data(self._datas)
702
775
 
703
776
  def crosshair_changed(self):
@@ -721,6 +794,7 @@ class Viewer2D(ViewerBase):
721
794
  if len(data) > 3:
722
795
  logger.warning('Cannot plot on 2D plot more than 3 channels')
723
796
  data.data = data.data[:3]
797
+ self.view.set_image_labels(data.labels)
724
798
  if data.distribution != self.view.data_displayer.display_type:
725
799
  self.view.set_image_displayer(data.distribution)
726
800
  self.filter_from_crosshair.set_graph_items(self.view.data_displayer.get_images())
@@ -731,11 +805,21 @@ class Viewer2D(ViewerBase):
731
805
  self.isdata['green'] = len(data) > 1
732
806
  self.isdata['blue'] = len(data) > 2
733
807
 
734
- self.set_visible_items()
735
808
  self.update_data()
809
+
810
+ self.set_visible_items()
736
811
  if not self.view.is_action_checked('roi'):
737
812
  self.data_to_export_signal.emit(self.data_to_export)
738
813
 
814
+ self.autolevels_first()
815
+
816
+ def autolevels_first(self):
817
+ if self.just_init and not self.is_action_checked('autolevels'):
818
+ self.get_action('autolevels').trigger()
819
+ self.update_data()
820
+ self.get_action('autolevels').trigger()
821
+ self.just_init = False
822
+
739
823
  def get_axes_from_view(self, data: DataWithAxes):
740
824
  """Obtain axes info from the view
741
825
 
@@ -796,15 +880,9 @@ class Viewer2D(ViewerBase):
796
880
 
797
881
  def set_visible_items(self):
798
882
  for key in IMAGE_TYPES:
799
- if self.view.is_action_checked(key) and not self.isdata[key]: # turn it off if it was on but there is no data
800
- self.view.set_action_checked(key, False)
801
- self.view.set_action_visible(key, False)
883
+ self.view.set_action_visible(key, self.isdata[key])
802
884
 
803
- elif self.isdata[key]:
804
- self.view.set_action_checked(key, True)
805
- self.view.set_action_visible(key, True)
806
-
807
- self.view.notify_visibility_data_displayer()
885
+ self.view.notify_visibility_data_displayer()
808
886
 
809
887
  def show_roi(self, show=True, show_roi_widget=True):
810
888
  """convenience function to control roi"""
@@ -813,24 +891,28 @@ class Viewer2D(ViewerBase):
813
891
 
814
892
  self.view.roi_manager.roiwidget.setVisible(show_roi_widget)
815
893
 
816
- def update_crosshair_data(self, crosshair_dict):
894
+ def update_crosshair_data(self, crosshair_dte: DataToExport):
817
895
  try:
818
896
  posx, posy = self.view.get_crosshair_position()
819
897
  (posx_scaled, posy_scaled) = self.view.scale_axis(posx, posy)
820
898
 
821
899
  dat = f'({posx_scaled:.1e}{posy_scaled:.1e})\n'
822
- for image_key in IMAGE_TYPES:
823
- if self.view.is_action_checked(image_key):
824
- dat += f' {image_key[0]}:{crosshair_dict[image_key].int_data:.1e}\n'
900
+ dwa_int = crosshair_dte.get_data_from_name('int')
901
+ if dwa_int is not None:
902
+ for ind_data in range(len(dwa_int)):
903
+ dat += f' {dwa_int.labels[ind_data]}:{float(dwa_int[ind_data][0]):.1e}\n'
825
904
 
826
- self.view.set_action_text('position', dat)
905
+ self.view.set_action_text('position', dat)
827
906
 
828
907
  except Exception as e:
829
- print(e)
908
+ logger.warning(str(e))
830
909
 
831
910
  def prepare_connect_ui(self):
832
911
  self.view.ROIselect.sigRegionChangeFinished.connect(self.selected_region_changed)
833
912
 
913
+ self.roi_manager.roi_changed.connect(self.roi_changed)
914
+ self.roi_manager.roi_value_changed.connect(self.roi_changed)
915
+
834
916
  self.view.connect_action('flip_ud', slot=self.update_data)
835
917
  self.view.connect_action('flip_lr', slot=self.update_data)
836
918
  self.view.connect_action('rotate', slot=self.update_data)
@@ -838,7 +920,7 @@ class Viewer2D(ViewerBase):
838
920
  self.view.connect_action('isocurve', slot=self.update_data)
839
921
  self.view.histogrammer.gradient_changed.connect(lambda: setattr(self, '_is_gradient_manually_set', True))
840
922
 
841
- self.view.lineout_plotter.roi_changed.connect(self.roi_changed)
923
+ # todo : self.view.lineout_plotter.roi_changed.connect(self.roi_changed)
842
924
  self.view.get_crosshair_signal().connect(self.crosshair_changed)
843
925
 
844
926
  self.view.get_double_clicked().connect(self.double_clicked)
@@ -847,7 +929,8 @@ class Viewer2D(ViewerBase):
847
929
  if self.view.is_action_checked('ROIselect'):
848
930
  pos = self.view.ROIselect.pos()
849
931
  size = self.view.ROIselect.size()
850
- self.ROI_select_signal.emit(QtCore.QRectF(pos[0], pos[1], size[0], size[1]))
932
+ # self.ROI_select_signal.emit(QtCore.QRectF(pos[0], pos[1], size[0], size[1]))
933
+ self.roi_select_signal.emit(RoiInfo.info_from_rect_roi(self.view.ROIselect))
851
934
 
852
935
  @Slot(float, float)
853
936
  def double_clicked(self, posx, posy):
@@ -879,55 +962,38 @@ class Viewer2D(ViewerBase):
879
962
  self.view.set_axis_scaling('right', scaling=axis.scaling, offset=axis.offset,
880
963
  label=axis.label, units=axis.units)
881
964
 
882
- def scale_lineout_dicts(self, lineout_dicts):
883
- for lineout_data in lineout_dicts.values():
884
- lineout_data.hor_axis, lineout_data.ver_axis = \
885
- self.view.scale_axis(lineout_data.hor_axis, lineout_data.ver_axis)
886
- return lineout_dicts
887
-
888
- @Slot(dict)
889
- def process_crosshair_lineouts(self, crosshair_dict):
890
- self.view.display_crosshair_lineouts(self.scale_lineout_dicts(crosshair_dict))
891
- self.update_crosshair_data(crosshair_dict)
965
+ @Slot(DataToExport)
966
+ def process_crosshair_lineouts(self, dte):
967
+ self.view.display_crosshair_lineouts(dte)
968
+ self.update_crosshair_data(dte)
892
969
  self.crosshair_dragged.emit(*self.view.scale_axis(*self.view.crosshair.get_positions()))
893
970
 
894
- @Slot(dict)
895
- def process_roi_lineouts(self, roi_dict):
896
- roi_dict = self.scale_lineout_dicts(roi_dict)
897
- self.view.display_roi_lineouts(roi_dict)
898
-
899
- self.measure_data_dict = dict([])
900
- for roi_key, lineout_data in roi_dict.items():
901
- if not self._display_temporary:
902
- self.data_to_export.append(
903
- DataFromRoi(name=f'Hlineout_{roi_key}', data=[lineout_data.hor_data],
904
- origin=self.title,
905
- axes=[Axis(data=lineout_data.hor_axis,
906
- units=self.x_axis.axis_units,
907
- label=self.x_axis.axis_label)]))
908
-
909
- self.data_to_export.append(
910
- DataFromRoi(name=f'Vlineout_{roi_key}', data=[lineout_data.ver_data],
911
- origin=self.title,
912
- axes=[Axis(data=lineout_data.ver_axis,
913
- units=self.y_axis.axis_units,
914
- label=self.y_axis.axis_label)]))
915
-
916
- self.data_to_export.append(DataFromRoi(name=f'Integrated_{roi_key}', data=[lineout_data.int_data],
917
- origin=self.title))
918
-
919
- if not isinstance(lineout_data.math_data, list):
920
- self.measure_data_dict[f'{roi_key}:'] = lineout_data.math_data
921
- else:
922
- for ind, dat in enumerate(lineout_data.math_data):
923
- self.measure_data_dict[f'{roi_key}/{ind:02d}:'] = dat
971
+ def process_roi_lineouts(self, roi_dte: DataToExport):
972
+ if len(roi_dte) > 0:
973
+ self.view.display_roi_lineouts(roi_dte)
974
+ roi_dte_bis = roi_dte.deepcopy()
975
+ for dwa in roi_dte_bis.data:
976
+ if dwa.name == 'hor':
977
+ dwa.name = f'Hlineout_{dwa.origin}'
978
+ elif dwa.name == 'ver':
979
+ dwa.name = f'Vlineout_{dwa.origin}'
980
+ elif dwa.name == 'int':
981
+ dwa.name = f'Integrated_{dwa.origin}'
982
+ self.data_to_export.append(roi_dte_bis)
924
983
 
925
- QtWidgets.QApplication.processEvents()
984
+ self.measure_data_dict = dict([])
926
985
 
927
- self.view.roi_manager.settings.child('measurements').setValue(self.measure_data_dict)
928
- if not self._display_temporary:
929
- self.data_to_export_signal.emit(self.data_to_export)
930
- self.ROI_changed.emit()
986
+ for roi_name in roi_dte_bis.get_origins():
987
+ dwa = roi_dte_bis.get_data_from_name_origin(f'Integrated_{roi_name}', roi_name)
988
+ for ind, data_array in enumerate(dwa.data):
989
+ self.measure_data_dict[f'{dwa.labels[ind]}:'] = float(data_array[0])
990
+
991
+ QtWidgets.QApplication.processEvents()
992
+
993
+ self.view.roi_manager.settings.child('measurements').setValue(self.measure_data_dict)
994
+ if not self._display_temporary:
995
+ self.data_to_export_signal.emit(self.data_to_export)
996
+ self.ROI_changed.emit()
931
997
 
932
998
 
933
999
  def main_spread():
@@ -961,29 +1027,24 @@ def main_spread():
961
1027
 
962
1028
  def main(data_distribution='uniform'):
963
1029
  """either 'uniform' or 'spread'"""
964
- from pymodaq.utils.math_utils import gauss2D
965
1030
 
966
1031
  app = QtWidgets.QApplication(sys.argv)
967
1032
  widget = QtWidgets.QWidget()
968
1033
 
1034
+ widget_button = QtWidgets.QWidget()
1035
+ widget_button.setLayout(QtWidgets.QHBoxLayout())
1036
+ button = QtWidgets.QPushButton('New Data')
1037
+ ndata = QtWidgets.QSpinBox()
1038
+ widget_button.layout().addWidget(button)
1039
+ widget_button.layout().addWidget(ndata)
1040
+
969
1041
  def print_data(data: DataToExport):
970
1042
  print(data)
971
1043
  print('******')
972
1044
  print(data.get_data_from_dim('Data1D'))
973
1045
 
974
1046
  if data_distribution == 'uniform':
975
- Nx = 100
976
- Ny = 2 * Nx
977
- data_random = np.random.normal(size=(Ny, Nx))
978
- x = 0.5 * np.linspace(-Nx / 2, Nx / 2 - 1, Nx)
979
- y = 0.2 * np.linspace(-Ny / 2, Ny / 2 - 1, Ny)
980
- data_red = 3 * np.sin(x/5)**2 * gauss2D(x, 5, Nx / 10, y, -1, Ny / 10, 1, 90) + 0.1 * data_random
981
- data_green = 10 * gauss2D(x, -20, Nx / 10, y, -10, Ny / 20, 1, 0)
982
- data_green[70:80, 7:12] = np.nan
983
-
984
- data_to_plot = DataFromPlugins(name='mydata', distribution='uniform', data=[data_red, data_green],
985
- axes=[Axis('xaxis', units='xpxl', data=x, index=1),
986
- Axis('yaxis', units='ypxl', data=y, index=0), ])
1047
+ data_to_plot = generate_uniform_data()
987
1048
 
988
1049
  elif data_distribution == 'spread':
989
1050
  data_spread = np.load('../../../resources/triangulation_data.npy')
@@ -1004,16 +1065,45 @@ def main(data_distribution='uniform'):
1004
1065
  prog.view.show_roi_target(True)
1005
1066
  prog.view.move_scale_roi_target((50, 40), (10, 20))
1006
1067
 
1068
+ button.clicked.connect(lambda: plot_data(prog, ndata.value()))
1069
+ widget_button.show()
1007
1070
  QtWidgets.QApplication.processEvents()
1008
1071
  sys.exit(app.exec_())
1009
1072
 
1010
1073
 
1074
+ def generate_uniform_data() -> DataFromPlugins:
1075
+ from pymodaq.utils.math_utils import gauss2D
1076
+ Nx = 100
1077
+ Ny = 2 * Nx
1078
+ data_random = np.random.normal(size=(Ny, Nx))
1079
+ x = 0.5 * np.linspace(-Nx / 2, Nx / 2 - 1, Nx)
1080
+ y = 0.2 * np.linspace(-Ny / 2, Ny / 2 - 1, Ny)
1081
+ data_red = 3 * np.sin(x / 5) ** 2 * gauss2D(x, 5, Nx / 10, y, -1, Ny / 10, 1, 90) + 0.2 * data_random
1082
+ data_green = 10 * gauss2D(x, -20, Nx / 10, y, -10, Ny / 20, 1, 0)
1083
+ data_green[70:80, 7:12] = np.nan
1084
+
1085
+ data_to_plot = DataFromPlugins(name='mydata', distribution='uniform',
1086
+ data=[data_red, data_green, data_red-data_green],
1087
+ labels = ['myreddata', 'mygreendata'],
1088
+ axes=[Axis('xaxis', units='xpxl', data=x, index=1),
1089
+ Axis('yaxis', units='ypxl', data=y, index=0), ])
1090
+ return data_to_plot
1091
+
1092
+
1093
+ def plot_data(viewer2D: Viewer2D, ndata: int = 2):
1094
+ if ndata > 0:
1095
+ dwa = generate_uniform_data()
1096
+ dwa.data = dwa.data[0:ndata]
1097
+ viewer2D.show_data(dwa)
1098
+
1099
+
1011
1100
  def print_roi_select(rect):
1012
1101
  print(rect)
1013
1102
 
1014
1103
 
1015
1104
  def main_view():
1016
1105
  app = QtWidgets.QApplication(sys.argv)
1106
+
1017
1107
  form = QtWidgets.QWidget()
1018
1108
  prog = View2D(form)
1019
1109
  form.show()