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
@@ -1,56 +1,22 @@
1
- from typing import List, Union, TYPE_CHECKING, Iterable
1
+ from typing import List, Union
2
2
  import numpy as np
3
3
 
4
4
  from qtpy import QtWidgets
5
- from qtpy.QtCore import QObject, Signal, Slot
6
-
7
- from pyqtgraph.graphicsItems import InfiniteLine, ROI
8
5
 
9
6
  from pymodaq.utils.logger import set_logger, get_module_name
10
- from pymodaq.utils.data import DataToExport, DataRaw, DataWithAxes, Axis, DataSource
11
- from pymodaq.utils.exceptions import ViewerError
12
- from pymodaq.utils.enums import BaseEnum, enum_checker
13
- from pymodaq.utils.factory import ObjectFactory, BuilderBase
7
+ from pymodaq.utils.data import DataToExport, DataWithAxes, Axis, DataSource
8
+
9
+ from pymodaq.utils.enums import enum_checker
10
+ from pymodaq.utils.factory import ObjectFactory
14
11
  from pymodaq.utils.plotting import data_viewers
12
+ from pymodaq.utils.plotting.data_viewers.base import ViewerBase, ViewersEnum
15
13
  from pymodaq.utils.gui_utils import DockArea, Dock
16
- from pymodaq.utils.managers.parameter_manager import ParameterManager
17
14
 
18
- if TYPE_CHECKING:
19
- from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
20
- from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
21
- from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
22
- from pymodaq.utils.plotting.data_viewers.viewerND import ViewerND
23
-
24
- config_viewers = {
25
- }
15
+ config_viewers = {}
26
16
 
27
17
  logger = set_logger(get_module_name(__file__))
28
18
 
29
19
 
30
- class ViewersEnum(BaseEnum):
31
- """enum relating a given viewer with data type"""
32
- Viewer0D = 'Data0D'
33
- Viewer1D = 'Data1D'
34
- Viewer2D = 'Data2D'
35
- ViewerND = 'DataND'
36
- ViewerSequential = 'DataSequential'
37
-
38
- def get_dim(self):
39
- return self.value.split('Data')[1].split('D')[0]
40
-
41
- def increase_dim(self, ndim: int):
42
- dim = self.get_dim()
43
- if dim != 'N':
44
- dim_as_int = int(dim) + ndim
45
- if dim_as_int > 2:
46
- dim = 'N'
47
- else:
48
- dim = str(dim_as_int)
49
- else:
50
- dim = 'N'
51
- return ViewersEnum[f'Viewer{dim}D']
52
-
53
-
54
20
  def get_viewer_enum_from_axes(Naxes: int):
55
21
  if Naxes < 0:
56
22
  raise ValueError('Naxes could not be below 0')
@@ -104,176 +70,35 @@ def create_viewerND(parent: QtWidgets.QWidget, **_ignored):
104
70
  viewer_factory = ViewerFactory()
105
71
 
106
72
 
107
- class ViewerBase(QObject):
108
- """Base Class for data viewers implementing all common functionalities
73
+ class ViewerDispatcher:
74
+ """MixIn class to add easy control for adding multuiple data viewers in docks depending on
75
+ data to be plotted
109
76
 
110
77
  Parameters
111
78
  ----------
112
- parent: QtWidgets.QWidget
79
+ dockarea: DockArea
113
80
  title: str
81
+ next_to_dock: Dock
82
+ (deprecated) has no effect
83
+ direction: str
84
+ either 'right', 'left', 'bottom', 'top'.
114
85
 
115
- Attributes
116
- ----------
117
- view: QObject
118
- Ui interface of the viewer
119
-
120
- data_to_export_signal: Signal[DataToExport]
121
- ROI_changed: Signal
122
- crosshair_dragged: Signal[float, float]
123
- crosshair_clicked: Signal[bool]
124
- sig_double_clicked: Signal[float, float]
125
- status_signal: Signal[str]
126
86
  """
127
- data_to_export_signal = Signal(DataToExport)
128
- _data_to_show_signal = Signal(DataWithAxes)
129
-
130
- ROI_changed = Signal()
131
- crosshair_dragged = Signal(float, float) # Crosshair position in units of scaled top/right axes
132
- status_signal = Signal(str)
133
- crosshair_clicked = Signal(bool)
134
- sig_double_clicked = Signal(float, float)
135
87
 
136
- def __init__(self, parent: QtWidgets.QWidget = None, title=''):
137
- super().__init__()
138
- self.title = title if title != '' else self.__class__.__name__
139
-
140
- self._raw_data = None
141
- self.data_to_export: DataToExport = DataToExport(name=self.title)
142
- self.view: Union[Viewer0D, Viewer1D, Viewer2D, ViewerND] = None
143
-
144
- if parent is None:
145
- parent = QtWidgets.QWidget()
146
- parent.show()
147
- self.parent = parent
148
-
149
- self._display_temporary = False
150
-
151
- @property
152
- def has_action(self):
153
- """Convenience method"""
154
- if hasattr(self.view, 'has_action'):
155
- return self.view.has_action
156
-
157
- @property
158
- def is_action_checked(self):
159
- """Convenience method"""
160
- if hasattr(self.view, 'is_action_checked'):
161
- return self.view.is_action_checked
162
-
163
- @property
164
- def is_action_visible(self):
165
- """Convenience method"""
166
- if hasattr(self.view, 'is_action_visible'):
167
- return self.view.is_action_visible
168
-
169
- @property
170
- def set_action_checked(self):
171
- """Convenience method"""
172
- if hasattr(self.view, 'set_action_checked'):
173
- return self.view.set_action_checked
174
-
175
- @property
176
- def set_action_visible(self):
177
- """Convenience method"""
178
- if hasattr(self.view, 'set_action_visible'):
179
- return self.view.set_action_visible
180
-
181
- @property
182
- def get_action(self):
183
- """Convenience method"""
184
- if hasattr(self.view, 'get_action'):
185
- return self.view.get_action
186
-
187
- @property
188
- def toolbar(self):
189
- """Convenience property"""
190
- if hasattr(self.view, 'toolbar'):
191
- return self.view.toolbar
192
-
193
- @property
194
- def viewer_type(self):
195
- """str: the viewer data type see DATA_TYPES"""
196
- return ViewersEnum[self.__class__.__name__].value
197
-
198
- def show_data(self, data: DataWithAxes, **kwargs):
199
- """Entrypoint to display data into the viewer
200
-
201
- Parameters
202
- ----------
203
- data: data_mod.DataFromPlugins
204
- """
205
- if len(data.shape) > 4:
206
- raise ViewerError(f'Ndarray of dim: {len(data.shape)} cannot be plotted using a {self.viewer_type}')
207
-
208
- self.data_to_export = DataToExport(name=self.title)
209
- self._raw_data = data
210
-
211
- self._display_temporary = False
212
-
213
- self._show_data(data, **kwargs)
214
-
215
- def show_data_temp(self, data: DataRaw, **kwargs):
216
- """Entrypoint to display temporary data into the viewer
217
-
218
- No processed data signal is emitted from the viewer
219
-
220
- Parameters
221
- ----------
222
- data: data_mod.DataFromPlugins
223
- """
224
- self._display_temporary = True
225
- self.show_data(data, **kwargs)
226
-
227
- def _show_data(self, data: DataRaw):
228
- """Specific viewers should implement it"""
229
- raise NotImplementedError
230
-
231
- def add_attributes_from_view(self):
232
- """Convenience function to add attributes from the view to self"""
233
- for attribute in self.convenience_attributes:
234
- if hasattr(self.view, attribute):
235
- setattr(self, attribute, getattr(self.view, attribute))
236
-
237
- def trigger_action(self, action_name: str):
238
- """Convenience function to trigger programmatically one of the action of the related view"""
239
- if self.has_action(action_name):
240
- self.get_action(action_name).trigger()
241
-
242
- def activate_roi(self, activate=True):
243
- """Activate the Roi manager using the corresponding action"""
244
- raise NotImplementedError
245
-
246
- def setVisible(self, show=True):
247
- """convenience method to show or hide the paretn widget"""
248
- self.parent.setVisible(show)
249
-
250
- @property
251
- def roi_target(self) -> Union[InfiniteLine.InfiniteLine, ROI.ROI]:
252
- """To be implemented if necessary (Viewer1D and above)"""
253
- return None
254
-
255
- def move_roi_target(self, pos: Iterable[float] = None, **kwargs):
256
- """move a specific read only ROI at the given position on the viewer"""
257
- ...
258
-
259
- def show_roi_target(self, show=True):
260
- """Show/Hide a specific read only ROI"""
261
- if self.roi_target is not None:
262
- self.roi_target.setVisible(show)
263
-
264
-
265
- class ViewerDispatcher:
266
- """MixIn class to add easy control for adding multuiple data viewers in docks depending on data to be plotted"""
267
-
268
- def __init__(self, dockarea: DockArea = None, title: str = '', next_to_dock: Dock = None):
88
+ def __init__(self, dockarea: DockArea = None, title: str = '', next_to_dock: Dock = None,
89
+ direction='right'):
269
90
  super().__init__()
270
91
  self._title = title
92
+
271
93
  self._next_to_dock = next_to_dock
94
+
272
95
  if dockarea is None:
273
96
  dockarea = DockArea()
274
97
  dockarea.show()
275
98
  self.dockarea = dockarea
276
99
 
100
+ self._direction = direction
101
+
277
102
  self._viewer_docks = []
278
103
  self._viewer_widgets = []
279
104
  self._viewer_types = []
@@ -337,9 +162,10 @@ class ViewerDispatcher:
337
162
  # self.dockarea.addDock(self.viewer_docks[-1])
338
163
  # else:
339
164
  # self.dockarea.addDock(self.viewer_docks[-1], 'right', self.viewer_docks[-2])
340
- self.dockarea.addDock(self.viewer_docks[-1], 'right')
165
+ self.dockarea.addDock(self.viewer_docks[-1], self._direction)
341
166
 
342
- def update_viewers(self, viewers_type: List[ViewersEnum], viewers_name: List[str] = None, force=False):
167
+ def update_viewers(self, viewers_type: List[Union[str, ViewersEnum]],
168
+ viewers_name: List[str] = None, force=False):
343
169
  """
344
170
 
345
171
  Parameters
@@ -369,7 +195,8 @@ class ViewerDispatcher:
369
195
  ind_loop = 0
370
196
  while len(self.viewers) < len(viewers_type):
371
197
  self.add_viewer(viewers_type[Nviewers_to_leave + ind_loop],
372
- dock_name=viewers_name[Nviewers_to_leave + ind_loop] if viewers_name is not None else None)
198
+ dock_name=viewers_name[Nviewers_to_leave + ind_loop]
199
+ if viewers_name is not None else None)
373
200
  ind_loop += 1
374
201
  QtWidgets.QApplication.processEvents()
375
202
 
@@ -377,9 +204,9 @@ class ViewerDispatcher:
377
204
  for dock in self.viewer_docks:
378
205
  dock.close()
379
206
 
380
- def show_data(self, data: DataToExport):
207
+ def show_data(self, data: DataToExport, **kwargs):
381
208
  """ Convenience method. Display each dwa in a dedicated data viewer"""
382
- viewer_types = [ViewersEnum(dwa.dim.name) for dwa in data]
209
+ viewer_types = [ViewersEnum.get_viewers_enum_from_data(dwa) for dwa in data]
383
210
  if self.viewer_types != viewer_types:
384
211
  self.update_viewers(viewer_types)
385
212
  for viewer, dwa in zip(self.viewers, data):
@@ -426,7 +253,7 @@ if __name__ == '__main__':
426
253
  return dat1, dat2, data
427
254
 
428
255
  import sys
429
- import random
256
+
430
257
  app = QtWidgets.QApplication(sys.argv)
431
258
 
432
259
  dockarea = DockArea()
@@ -1,4 +1,4 @@
1
- from typing import List, Union
1
+ from typing import List, Union, Dict
2
2
  from numbers import Real
3
3
 
4
4
  from qtpy import QtWidgets, QtGui
@@ -19,7 +19,7 @@ from collections import OrderedDict
19
19
  import datetime
20
20
 
21
21
  logger = set_logger(get_module_name(__file__))
22
- PLOT_COLORS = utils.plot_colors
22
+ PLOT_COLORS = [dict(color=color) for color in utils.plot_colors]
23
23
 
24
24
 
25
25
  class DataDisplayer(QObject):
@@ -30,9 +30,10 @@ class DataDisplayer(QObject):
30
30
  updated_item = Signal(list)
31
31
  labels_changed = Signal(list)
32
32
 
33
- def __init__(self, plotitem: pyqtgraph.PlotItem):
33
+ def __init__(self, plotitem: pyqtgraph.PlotItem, plot_colors=PLOT_COLORS):
34
34
  super().__init__()
35
35
  self._plotitem = plotitem
36
+ self.colors = plot_colors
36
37
  self._plotitem.addLegend()
37
38
  self._plot_items: List[pyqtgraph.PlotDataItem] = []
38
39
  self._min_lines: List[pyqtgraph.InfiniteLine] = []
@@ -47,10 +48,18 @@ class DataDisplayer(QObject):
47
48
  axis = self._plotitem.getAxis('bottom')
48
49
  axis.setLabel(text='Samples', units='S')
49
50
 
51
+ def update_colors(self, colors: List[QtGui.QPen]):
52
+ self.colors[0:len(colors)] = colors
53
+ self.update_data(self._data.last_data, force_update=True)
54
+
50
55
  @property
51
- def legend(self):
56
+ def legend(self) -> pyqtgraph.LegendItem:
52
57
  return self._plotitem.legend
53
58
 
59
+ @property
60
+ def legend_names(self) -> List[str]:
61
+ return [item[1].text for item in self.legend.items]
62
+
54
63
  @property
55
64
  def axis(self):
56
65
  return self._data.xaxis
@@ -63,49 +72,58 @@ class DataDisplayer(QObject):
63
72
  def update_axis(self, history_length: int):
64
73
  self._data.length = history_length
65
74
 
66
- def update_data(self, data: data_mod.DataRaw):
67
- if len(data) != len(self._plot_items):
68
- self.update_display_items(data)
75
+ @property
76
+ def Ndata(self):
77
+ return len(self._data.last_data) if self._data.last_data is not None else 0
69
78
 
70
- self._data.add_datas(data)
71
- for ind, data_str in enumerate(self._data.datas):
72
- self._plot_items[ind].setData(self._data.xaxis, self._data.datas[data_str])
73
- if len(self._mins) != len(self._data.datas):
74
- self._mins = []
75
- self._maxs = []
79
+ def update_data(self, data: data_mod.DataWithAxes, force_update=False):
80
+ if data is not None:
81
+ if len(data) != len(self._plot_items) or force_update or data.labels != self.legend_names:
82
+ self.update_display_items(data)
76
83
 
77
- for ind, label in enumerate(self._data.datas):
84
+ self._data.add_datas(data)
85
+ for ind, data_str in enumerate(self._data.datas):
86
+ self._plot_items[ind].setData(self._data.xaxis, self._data.datas[data_str])
78
87
  if len(self._mins) != len(self._data.datas):
79
- self._mins.append(float(np.min(self._data.datas[label])))
80
- self._maxs.append(float(np.max(self._data.datas[label])))
81
- else:
82
- self._mins[ind] = min(self._mins[ind], float(np.min(self._data.datas[label])))
83
- self._maxs[ind] = max(self._maxs[ind], float(np.max(self._data.datas[label])))
84
- self._min_lines[ind].setValue(self._mins[ind])
85
- self._max_lines[ind].setValue(self._maxs[ind])
86
-
87
- def update_display_items(self, data: data_mod.DataRaw):
88
+ self._mins = []
89
+ self._maxs = []
90
+
91
+ for ind, label in enumerate(self._data.datas):
92
+ if len(self._mins) != len(self._data.datas):
93
+ self._mins.append(float(np.min(self._data.datas[label])))
94
+ self._maxs.append(float(np.max(self._data.datas[label])))
95
+ else:
96
+ self._mins[ind] = min(self._mins[ind], float(np.min(self._data.datas[label])))
97
+ self._maxs[ind] = max(self._maxs[ind], float(np.max(self._data.datas[label])))
98
+ self._min_lines[ind].setValue(self._mins[ind])
99
+ self._max_lines[ind].setValue(self._maxs[ind])
100
+
101
+ def update_display_items(self, data: data_mod.DataWithAxes = None):
88
102
  while len(self._plot_items) > 0:
89
103
  plot_item = self._plotitem.removeItem(self._plot_items.pop(0))
90
104
  self.legend.removeItem(plot_item)
91
105
  self._plotitem.removeItem(self._max_lines.pop(0))
92
106
  self._plotitem.removeItem(self._min_lines.pop(0))
93
-
94
- for ind in range(len(data)):
95
- self._plot_items.append(pyqtgraph.PlotDataItem(pen=PLOT_COLORS[ind]))
96
- self._plotitem.addItem(self._plot_items[-1])
97
- self.legend.addItem(self._plot_items[-1], data.labels[ind])
98
- max_line = pyqtgraph.InfiniteLine(angle=0, pen=pyqtgraph.mkPen(color=PLOT_COLORS[ind], style=Qt.DashLine))
99
- min_line = pyqtgraph.InfiniteLine(angle=0, pen=pyqtgraph.mkPen(color=PLOT_COLORS[ind], style=Qt.DashLine))
100
- self._max_lines.append(max_line)
101
- self._min_lines.append(min_line)
102
- max_line.setVisible(self._show_lines)
103
- min_line.setVisible(self._show_lines)
104
- self._plotitem.addItem(self._max_lines[-1])
105
- self._plotitem.addItem(self._min_lines[-1])
106
-
107
- self.updated_item.emit(self._plot_items)
108
- self.labels_changed.emit(data.labels)
107
+ if data is not None:
108
+ for ind in range(len(data)):
109
+ self._plot_items.append(pyqtgraph.PlotDataItem(pen=self.colors[ind]))
110
+ self._plotitem.addItem(self._plot_items[-1])
111
+ self.legend.addItem(self._plot_items[-1], data.labels[ind])
112
+ max_line = pyqtgraph.InfiniteLine(angle=0,
113
+ pen=pyqtgraph.mkPen(color=self.colors[ind]['color'],
114
+ style=Qt.DashLine))
115
+ min_line = pyqtgraph.InfiniteLine(angle=0,
116
+ pen=pyqtgraph.mkPen(color=self.colors[ind]['color'],
117
+ style=Qt.DashLine))
118
+ self._max_lines.append(max_line)
119
+ self._min_lines.append(min_line)
120
+ max_line.setVisible(self._show_lines)
121
+ min_line.setVisible(self._show_lines)
122
+ self._plotitem.addItem(self._max_lines[-1])
123
+ self._plotitem.addItem(self._min_lines[-1])
124
+
125
+ self.updated_item.emit(self._plot_items)
126
+ self.labels_changed.emit(data.labels)
109
127
 
110
128
  def show_min_max(self, show=True):
111
129
  self._show_lines = show
@@ -116,11 +134,14 @@ class DataDisplayer(QObject):
116
134
 
117
135
 
118
136
  class View0D(ActionManager, QObject):
119
- def __init__(self, parent_widget: QtWidgets.QWidget = None):
137
+ def __init__(self, parent_widget: QtWidgets.QWidget = None, show_toolbar=True,
138
+ no_margins=False):
120
139
  QObject.__init__(self)
121
140
  ActionManager.__init__(self, toolbar=QtWidgets.QToolBar())
122
141
 
142
+ self.no_margins = no_margins
123
143
  self.data_displayer: DataDisplayer = None
144
+ self.other_data_displayers: Dict[str, DataDisplayer] = {}
124
145
  self.plot_widget: PlotWidget = PlotWidget()
125
146
  self.values_list = QtWidgets.QListWidget()
126
147
 
@@ -136,6 +157,8 @@ class View0D(ActionManager, QObject):
136
157
  self._setup_widgets()
137
158
  self._connect_things()
138
159
  self._prepare_ui()
160
+ if not show_toolbar:
161
+ self.splitter.setSizes([0,1])
139
162
 
140
163
  self.get_action('Nhistory').setValue(200) #default history length
141
164
 
@@ -149,11 +172,17 @@ class View0D(ActionManager, QObject):
149
172
  'If triggered, will display horizontal dashed lines for min/max of data', checkable=True)
150
173
 
151
174
  def _setup_widgets(self):
175
+ self.splitter = QtWidgets.QSplitter(Qt.Vertical)
152
176
  self.parent_widget.setLayout(QtWidgets.QVBoxLayout())
153
- self.parent_widget.layout().addWidget(self.toolbar)
177
+ if self.no_margins:
178
+ self.parent_widget.layout().setContentsMargins(0, 0, 0, 0)
179
+
180
+ self.parent_widget.layout().addWidget(self.splitter)
181
+ self.splitter.addWidget(self.toolbar)
182
+ self.splitter.setStretchFactor(0, 0)
154
183
 
155
184
  splitter_hor = QtWidgets.QSplitter(Qt.Horizontal)
156
- self.parent_widget.layout().addWidget(splitter_hor)
185
+ self.splitter.addWidget(splitter_hor)
157
186
 
158
187
  splitter_hor.addWidget(self.plot_widget)
159
188
  splitter_hor.addWidget(self.values_list)
@@ -179,8 +208,11 @@ class View0D(ActionManager, QObject):
179
208
  def plotitem(self):
180
209
  return self.plot_widget.plotItem
181
210
 
182
- def display_data(self, data: data_mod.DataRaw):
183
- self.data_displayer.update_data(data)
211
+ def display_data(self, data: data_mod.DataWithAxes, displayer: str = None, **kwargs):
212
+ if displayer is None:
213
+ self.data_displayer.update_data(data)
214
+ elif displayer in self.other_data_displayers:
215
+ self.other_data_displayers[displayer].update_data(data)
184
216
  if self.is_action_checked('show_data_as_list'):
185
217
  self.values_list.clear()
186
218
  self.values_list.addItems(['{:.03e}'.format(dat[0]) for dat in data])
@@ -191,6 +223,15 @@ class View0D(ActionManager, QObject):
191
223
  state = self.is_action_checked('show_data_as_list')
192
224
  self.values_list.setVisible(state)
193
225
 
226
+ def add_data_displayer(self, displayer_name: str, plot_colors=PLOT_COLORS):
227
+ self.other_data_displayers[displayer_name] = DataDisplayer(self.plotitem, plot_colors)
228
+ self.connect_action('clear', self.other_data_displayers[displayer_name].clear_data)
229
+
230
+ def remove_data_displayer(self, displayer_name: str):
231
+ displayer = self.other_data_displayers.pop(displayer_name, None)
232
+ if displayer is not None:
233
+ displayer.update_display_items()
234
+
194
235
 
195
236
  class Viewer0D(ViewerBase):
196
237
  """this plots 0D data on a plotwidget with history. Display as numbers in a table is possible.
@@ -198,11 +239,17 @@ class Viewer0D(ViewerBase):
198
239
  Datas and measurements are then exported with the signal data_to_export_signal
199
240
  """
200
241
 
201
- def __init__(self, parent=None, title=''):
242
+ def __init__(self, parent=None, title='', show_toolbar=True, no_margins=False):
202
243
  super().__init__(parent, title)
203
- self.view = View0D(self.parent)
244
+ self.view = View0D(self.parent, show_toolbar=show_toolbar, no_margins=no_margins)
204
245
  self._labels = []
205
246
 
247
+ def update_colors(self, colors: list, displayer=None):
248
+ if displayer is None:
249
+ self.view.data_displayer.update_colors(colors)
250
+ elif displayer in self.view.other_data_displayers:
251
+ self.view.other_data_displayers[displayer].update_colors(colors)
252
+
206
253
  @property
207
254
  def labels(self):
208
255
  return self._labels
@@ -230,7 +277,7 @@ def main_view():
230
277
  def main():
231
278
  app = QtWidgets.QApplication(sys.argv)
232
279
  widget = QtWidgets.QWidget()
233
- prog = Viewer0D(widget)
280
+ prog = Viewer0D(widget, show_toolbar=False)
234
281
  from pymodaq.utils.daq_utils import gauss1D
235
282
 
236
283
  x = np.linspace(0, 200, 201)