pymodaq 3.6.13__py3-none-any.whl → 4.0.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.

Files changed (233) hide show
  1. pymodaq/__init__.py +13 -6
  2. pymodaq/control_modules/__init__.py +0 -7
  3. pymodaq/control_modules/daq_move.py +965 -2
  4. pymodaq/control_modules/daq_move_ui.py +319 -0
  5. pymodaq/control_modules/daq_viewer.py +1573 -3
  6. pymodaq/control_modules/daq_viewer_ui.py +393 -0
  7. pymodaq/control_modules/mocks.py +51 -0
  8. pymodaq/control_modules/move_utility_classes.py +709 -8
  9. pymodaq/control_modules/utils.py +256 -0
  10. pymodaq/control_modules/viewer_utility_classes.py +663 -6
  11. pymodaq/daq_utils.py +89 -0
  12. pymodaq/dashboard.py +91 -72
  13. pymodaq/examples/custom_app.py +12 -11
  14. pymodaq/examples/custom_viewer.py +10 -10
  15. pymodaq/examples/function_plotter.py +16 -13
  16. pymodaq/examples/nonlinearscanner.py +8 -6
  17. pymodaq/examples/parameter_ex.py +7 -7
  18. pymodaq/examples/preset_MockCamera.xml +1 -0
  19. pymodaq/extensions/__init__.py +16 -0
  20. pymodaq/extensions/console.py +76 -0
  21. pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
  22. pymodaq/extensions/daq_scan.py +1339 -0
  23. pymodaq/extensions/daq_scan_ui.py +240 -0
  24. pymodaq/extensions/h5browser.py +23 -0
  25. pymodaq/{pid → extensions/pid}/__init__.py +4 -2
  26. pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
  27. pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
  28. pymodaq/{pid → extensions/pid}/utils.py +52 -6
  29. pymodaq/extensions/utils.py +40 -0
  30. pymodaq/post_treatment/__init__.py +6 -0
  31. pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
  32. pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
  33. pymodaq/post_treatment/load_and_plot.py +219 -0
  34. pymodaq/post_treatment/process_to_scalar.py +263 -0
  35. pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
  36. pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
  37. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
  38. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
  39. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
  40. pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
  41. pymodaq/resources/VERSION +1 -1
  42. pymodaq/resources/config_template.toml +32 -13
  43. pymodaq/resources/preset_default.xml +1 -1
  44. pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
  45. pymodaq/utils/__init__.py +0 -29
  46. pymodaq/utils/abstract/__init__.py +48 -0
  47. pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
  48. pymodaq/utils/array_manipulation.py +379 -8
  49. pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
  50. pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
  51. pymodaq/utils/config.py +448 -0
  52. pymodaq/utils/conftests.py +5 -0
  53. pymodaq/utils/daq_utils.py +828 -8
  54. pymodaq/utils/data.py +1873 -7
  55. pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
  56. pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
  57. pymodaq/{daq_utils → utils}/enums.py +12 -7
  58. pymodaq/utils/exceptions.py +37 -0
  59. pymodaq/utils/factory.py +82 -0
  60. pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
  61. pymodaq/utils/gui_utils/custom_app.py +129 -0
  62. pymodaq/utils/gui_utils/file_io.py +66 -0
  63. pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
  64. pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
  65. pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
  66. pymodaq/utils/gui_utils/widgets/label.py +24 -0
  67. pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
  68. pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
  69. pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
  70. pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
  71. pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
  72. pymodaq/utils/h5modules/__init__.py +1 -0
  73. pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
  74. pymodaq/utils/h5modules/browsing.py +683 -0
  75. pymodaq/utils/h5modules/data_saving.py +839 -0
  76. pymodaq/utils/h5modules/h5logging.py +110 -0
  77. pymodaq/utils/h5modules/module_saving.py +350 -0
  78. pymodaq/utils/h5modules/saving.py +914 -0
  79. pymodaq/utils/h5modules/utils.py +85 -0
  80. pymodaq/utils/logger.py +64 -6
  81. pymodaq/utils/managers/action_manager.py +460 -0
  82. pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
  83. pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
  84. pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
  85. pymodaq/utils/managers/parameter_manager.py +110 -0
  86. pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
  87. pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
  88. pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
  89. pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
  90. pymodaq/utils/math_utils.py +546 -10
  91. pymodaq/{daq_utils → utils}/messenger.py +5 -1
  92. pymodaq/utils/parameter/__init__.py +2 -15
  93. pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
  94. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
  95. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
  96. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
  97. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
  98. pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
  99. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
  100. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
  101. pymodaq/utils/parameter/utils.py +206 -11
  102. pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
  103. pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
  104. pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
  105. pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
  106. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
  107. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
  108. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
  109. pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
  110. pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
  111. pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
  112. pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
  113. pymodaq/utils/plotting/navigator.py +355 -0
  114. pymodaq/utils/plotting/scan_selector.py +480 -0
  115. pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
  116. pymodaq/utils/plotting/utils/filter.py +538 -0
  117. pymodaq/utils/plotting/utils/lineout.py +224 -0
  118. pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
  119. pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
  120. pymodaq/utils/plotting/widgets.py +76 -0
  121. pymodaq/utils/scanner/__init__.py +10 -0
  122. pymodaq/utils/scanner/scan_factory.py +204 -0
  123. pymodaq/utils/scanner/scanner.py +271 -0
  124. pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
  125. pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
  126. pymodaq/utils/scanner/scanners/sequential.py +192 -0
  127. pymodaq/utils/scanner/scanners/tabular.py +294 -0
  128. pymodaq/utils/scanner/utils.py +83 -0
  129. pymodaq/utils/slicing.py +47 -0
  130. pymodaq/utils/svg/__init__.py +6 -0
  131. pymodaq/utils/svg/svg_renderer.py +20 -0
  132. pymodaq/utils/svg/svg_view.py +35 -0
  133. pymodaq/utils/svg/svg_viewer2D.py +51 -0
  134. pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
  135. pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
  136. pymodaq/utils/units.py +216 -0
  137. pymodaq-4.0.1.dist-info/METADATA +159 -0
  138. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
  140. pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
  141. pymodaq/daq_move/daq_move_gui.py +0 -279
  142. pymodaq/daq_move/daq_move_gui.ui +0 -534
  143. pymodaq/daq_move/daq_move_main.py +0 -1042
  144. pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
  145. pymodaq/daq_move/utility_classes.py +0 -686
  146. pymodaq/daq_scan.py +0 -2160
  147. pymodaq/daq_utils/array_manipulation.py +0 -386
  148. pymodaq/daq_utils/config.py +0 -273
  149. pymodaq/daq_utils/conftests.py +0 -7
  150. pymodaq/daq_utils/custom_parameter_tree.py +0 -9
  151. pymodaq/daq_utils/daq_enums.py +0 -133
  152. pymodaq/daq_utils/daq_utils.py +0 -1402
  153. pymodaq/daq_utils/exceptions.py +0 -71
  154. pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
  155. pymodaq/daq_utils/gui_utils/file_io.py +0 -75
  156. pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
  157. pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
  158. pymodaq/daq_utils/h5exporters.py +0 -242
  159. pymodaq/daq_utils/h5modules.py +0 -1559
  160. pymodaq/daq_utils/h5utils.py +0 -241
  161. pymodaq/daq_utils/managers/action_manager.py +0 -236
  162. pymodaq/daq_utils/managers/parameter_manager.py +0 -57
  163. pymodaq/daq_utils/math_utils.py +0 -705
  164. pymodaq/daq_utils/parameter/__init__.py +0 -1
  165. pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
  166. pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
  167. pymodaq/daq_utils/parameter/utils.py +0 -136
  168. pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
  169. pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
  170. pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
  171. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
  172. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
  173. pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
  174. pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
  175. pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
  176. pymodaq/daq_utils/plotting/items/__init__.py +0 -0
  177. pymodaq/daq_utils/plotting/navigator.py +0 -500
  178. pymodaq/daq_utils/plotting/scan_selector.py +0 -289
  179. pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
  180. pymodaq/daq_utils/plotting/utils/filter.py +0 -236
  181. pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
  182. pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
  183. pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
  184. pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
  185. pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
  186. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
  187. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
  188. pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
  189. pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
  190. pymodaq/daq_utils/scanner.py +0 -1289
  191. pymodaq/daq_utils/tree_layout/__init__.py +0 -0
  192. pymodaq/daq_viewer/__init__.py +0 -0
  193. pymodaq/daq_viewer/daq_gui_settings.py +0 -237
  194. pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
  195. pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
  196. pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
  197. pymodaq/daq_viewer/utility_classes.py +0 -673
  198. pymodaq/examples/logger_image/__init__.py +0 -0
  199. pymodaq/examples/logger_image/logger_displayer.py +0 -121
  200. pymodaq/examples/logger_image/setup.svg +0 -3119
  201. pymodaq/examples/logger_image/setup_svg.py +0 -114
  202. pymodaq/h5browser.py +0 -39
  203. pymodaq/utils/scanner.py +0 -15
  204. pymodaq-3.6.13.dist-info/METADATA +0 -39
  205. pymodaq-3.6.13.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.13.dist-info/top_level.txt +0 -1
  207. /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
  208. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
  209. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
  210. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
  211. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
  212. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
  213. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
  214. /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
  215. /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
  216. /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
  217. /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
  218. /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
  219. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
  220. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
  221. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
  222. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
  223. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
  224. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
  225. /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
  226. /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
  227. /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
  228. /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
  229. /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
  230. /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
  231. /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
  232. /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
  233. {pymodaq-3.6.13.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,393 @@
1
+ from typing import List, Union, TYPE_CHECKING, Iterable
2
+
3
+ from qtpy import QtWidgets
4
+ from qtpy.QtCore import QObject, Signal, Slot
5
+
6
+ from pyqtgraph.graphicsItems import InfiniteLine, ROI
7
+
8
+ from pymodaq.utils.logger import set_logger, get_module_name
9
+ from pymodaq.utils.data import DataToExport, DataRaw, DataWithAxes
10
+ from pymodaq.utils.exceptions import ViewerError
11
+ from pymodaq.utils.enums import BaseEnum, enum_checker
12
+ from pymodaq.utils.factory import ObjectFactory, BuilderBase
13
+ from pymodaq.utils.plotting import data_viewers
14
+ from pymodaq.utils.gui_utils import DockArea, Dock
15
+ from pymodaq.utils.managers.parameter_manager import ParameterManager
16
+
17
+ if TYPE_CHECKING:
18
+ from pymodaq.utils.plotting.data_viewers.viewer0D import Viewer0D
19
+ from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
20
+ from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
21
+ from pymodaq.utils.plotting.data_viewers.viewerND import ViewerND
22
+
23
+ config_viewers = {
24
+ }
25
+
26
+ logger = set_logger(get_module_name(__file__))
27
+
28
+
29
+ class ViewersEnum(BaseEnum):
30
+ """enum relating a given viewer with data type"""
31
+ Viewer0D = 'Data0D'
32
+ Viewer1D = 'Data1D'
33
+ Viewer2D = 'Data2D'
34
+ ViewerND = 'DataND'
35
+ ViewerSequential = 'DataSequential'
36
+
37
+ def get_dim(self):
38
+ return self.value.split('Data')[1].split('D')[0]
39
+
40
+ def increase_dim(self, ndim: int):
41
+ dim = self.get_dim()
42
+ if dim != 'N':
43
+ dim_as_int = int(dim) + ndim
44
+ if dim_as_int > 2:
45
+ dim = 'N'
46
+ else:
47
+ dim = str(dim_as_int)
48
+ else:
49
+ dim = 'N'
50
+ return ViewersEnum[f'Viewer{dim}D']
51
+
52
+
53
+ def get_viewer_enum_from_axes(Naxes: int):
54
+ if Naxes < 0:
55
+ raise ValueError('Naxes could not be below 0')
56
+ if Naxes == 0:
57
+ viewer_enum = ViewersEnum['Viewer0D']
58
+ elif Naxes == 1:
59
+ viewer_enum = ViewersEnum['Viewer1D']
60
+ elif Naxes == 2:
61
+ viewer_enum = ViewersEnum['Viewer2D']
62
+ else:
63
+ viewer_enum = ViewersEnum['ViewerND']
64
+ return viewer_enum
65
+
66
+
67
+ class ViewerFactory(ObjectFactory):
68
+ def get(self, viewer_name, **kwargs):
69
+ if viewer_name not in ViewersEnum.names():
70
+ raise ValueError(f'{viewer_name} is not a valid PyMoDAQ Viewer: {ViewersEnum.names()}')
71
+ return self.create(viewer_name, **kwargs)
72
+
73
+ @property
74
+ def viewers(self):
75
+ return self.keys
76
+
77
+
78
+ @ViewerFactory.register('Viewer0D')
79
+ def create_viewer0D(parent: QtWidgets.QWidget = None, **_ignored):
80
+ return data_viewers.viewer0D.Viewer0D(parent)
81
+
82
+
83
+ @ViewerFactory.register('Viewer1D')
84
+ def create_viewer1D(parent: QtWidgets.QWidget, **_ignored):
85
+ return data_viewers.viewer1D.Viewer1D(parent)
86
+
87
+
88
+ @ViewerFactory.register('Viewer2D')
89
+ def create_viewer2D(parent: QtWidgets.QWidget, **_ignored):
90
+ return data_viewers.viewer2D.Viewer2D(parent)
91
+
92
+
93
+ @ViewerFactory.register('ViewerND')
94
+ def create_viewerND(parent: QtWidgets.QWidget, **_ignored):
95
+ return data_viewers.viewerND.ViewerND(parent)
96
+
97
+
98
+ # @ViewerFactory.register('ViewerSequential')
99
+ # def create_viewer_sequential(widget: QtWidgets.QWidget, **_ignored):
100
+ # return data_viewers.viewer_sequential.ViewerSequential(widget)
101
+
102
+
103
+ viewer_factory = ViewerFactory()
104
+
105
+
106
+ class ViewerDispatcher:
107
+ """MixIn class to add easy control for adding multuiple data viewers in docks depending on data to be plotted"""
108
+
109
+ def __init__(self, dockarea: DockArea, title: str = '', next_to_dock: Dock = None):
110
+ super().__init__()
111
+ self._title = title
112
+ self._next_to_dock = next_to_dock
113
+
114
+ self.dockarea = dockarea
115
+
116
+ self._viewer_docks = []
117
+ self._viewer_widgets = []
118
+ self._viewer_types = []
119
+ self._viewers = []
120
+
121
+ @property
122
+ def viewers(self):
123
+ return self._viewers
124
+
125
+ @property
126
+ def viewer_docks(self):
127
+ return self._viewer_docks
128
+
129
+ @property
130
+ def viewer_widgets(self):
131
+ return self._viewer_widgets
132
+
133
+ @property
134
+ def viewer_types(self):
135
+ return self._viewer_types
136
+
137
+ def remove_viewers(self, Nviewers_to_leave: int = 0):
138
+ """Remove viewers from the list after index Nviewers_to_leave
139
+
140
+ Parameters
141
+ ----------
142
+ Nviewers
143
+
144
+ Returns
145
+ -------
146
+
147
+ """
148
+ while len(self.viewer_docks) > Nviewers_to_leave:
149
+ widget = self.viewer_widgets.pop()
150
+ widget.close()
151
+ dock = self.viewer_docks.pop()
152
+ dock.close()
153
+ self.viewers.pop()
154
+ self.viewer_types.pop()
155
+ QtWidgets.QApplication.processEvents()
156
+
157
+ def add_viewer(self, viewer_type: ViewersEnum, dock_viewer=None, dock_name=None):
158
+ viewer_type = enum_checker(ViewersEnum, viewer_type)
159
+
160
+ if dock_viewer is None:
161
+ if dock_name is None:
162
+ dock_name = f'{self._title}_Viewer_{len(self.viewer_docks) + 1}'
163
+ dock_viewer = Dock(dock_name, size=(350, 350), closable=False)
164
+ self.viewer_docks.append(dock_viewer)
165
+
166
+ self._viewer_widgets.append(QtWidgets.QWidget())
167
+ self.viewers.append(viewer_factory.get(viewer_type.name, parent=self._viewer_widgets[-1]))
168
+
169
+ self.viewer_types.append(viewer_type)
170
+
171
+ self.viewer_docks[-1].addWidget(self._viewer_widgets[-1])
172
+ # if len(self.viewer_docks) == 1:
173
+ # if self._next_to_dock is not None:
174
+ # self.dockarea.addDock(self.viewer_docks[-1], 'right', self._next_to_dock)
175
+ # else:
176
+ # self.dockarea.addDock(self.viewer_docks[-1])
177
+ # else:
178
+ # self.dockarea.addDock(self.viewer_docks[-1], 'right', self.viewer_docks[-2])
179
+ self.dockarea.addDock(self.viewer_docks[-1], 'right')
180
+
181
+ def update_viewers(self, viewers_type: List[ViewersEnum], viewers_name: List[str] = None, force=False):
182
+ """
183
+
184
+ Parameters
185
+ ----------
186
+ viewers_type: List[ViewersEnum]
187
+ viewers_name: List[str] or None
188
+ force: bool
189
+ if True remove all viewers before update else check if new viewers type are compatible with old ones
190
+
191
+ Returns
192
+ -------
193
+
194
+ """
195
+
196
+ Nviewers_to_leave = 0
197
+ if not force:
198
+ # check if viewers are compatible with new data dim
199
+ for ind, viewer_type in enumerate(viewers_type):
200
+ if len(self.viewer_types) > ind:
201
+ if viewer_type == self.viewer_types[ind]:
202
+ Nviewers_to_leave += 1
203
+ else:
204
+ break
205
+ else:
206
+ break
207
+ self.remove_viewers(Nviewers_to_leave)
208
+ ind_loop = 0
209
+ while len(self.viewers) < len(viewers_type):
210
+ self.add_viewer(viewers_type[Nviewers_to_leave + ind_loop],
211
+ dock_name=viewers_name[Nviewers_to_leave + ind_loop] if viewers_name is not None else None)
212
+ ind_loop += 1
213
+ QtWidgets.QApplication.processEvents()
214
+
215
+ def close(self):
216
+ for dock in self.viewer_docks:
217
+ dock.close()
218
+
219
+
220
+ class ViewerBase(QObject):
221
+ """Base Class for data viewers implementing all common functionalities
222
+
223
+ Parameters
224
+ ----------
225
+ parent: QtWidgets.QWidget
226
+ title: str
227
+
228
+ Attributes
229
+ ----------
230
+ view: QObject
231
+ Ui interface of the viewer
232
+
233
+ data_to_export_signal: Signal[DataToExport]
234
+ ROI_changed: Signal
235
+ crosshair_dragged: Signal[float, float]
236
+ crosshair_clicked: Signal[bool]
237
+ sig_double_clicked: Signal[float, float]
238
+ status_signal: Signal[str]
239
+ """
240
+ data_to_export_signal = Signal(DataToExport)
241
+ _data_to_show_signal = Signal(DataWithAxes)
242
+
243
+ ROI_changed = Signal()
244
+ crosshair_dragged = Signal(float, float) # Crosshair position in units of scaled top/right axes
245
+ status_signal = Signal(str)
246
+ crosshair_clicked = Signal(bool)
247
+ sig_double_clicked = Signal(float, float)
248
+
249
+ def __init__(self, parent=None, title=''):
250
+ super().__init__()
251
+ self.title = title if title != '' else self.__class__.__name__
252
+
253
+ self._raw_data = None
254
+ self.data_to_export: DataToExport = DataToExport(name=self.title)
255
+ self.view: Union[Viewer0D, Viewer1D, Viewer2D, ViewerND] = None
256
+
257
+ if parent is None:
258
+ parent = QtWidgets.QWidget()
259
+ parent.show()
260
+ self.parent = parent
261
+
262
+ self._display_temporary = False
263
+
264
+ @property
265
+ def has_action(self):
266
+ """Convenience method"""
267
+ if hasattr(self.view, 'has_action'):
268
+ return self.view.has_action
269
+
270
+ @property
271
+ def is_action_checked(self):
272
+ """Convenience method"""
273
+ if hasattr(self.view, 'is_action_checked'):
274
+ return self.view.is_action_checked
275
+
276
+ @property
277
+ def is_action_visible(self):
278
+ """Convenience method"""
279
+ if hasattr(self.view, 'is_action_visible'):
280
+ return self.view.is_action_visible
281
+
282
+ @property
283
+ def set_action_checked(self):
284
+ """Convenience method"""
285
+ if hasattr(self.view, 'set_action_checked'):
286
+ return self.view.set_action_checked
287
+
288
+ @property
289
+ def set_action_visible(self):
290
+ """Convenience method"""
291
+ if hasattr(self.view, 'set_action_visible'):
292
+ return self.view.set_action_visible
293
+
294
+ @property
295
+ def get_action(self):
296
+ """Convenience method"""
297
+ if hasattr(self.view, 'get_action'):
298
+ return self.view.get_action
299
+
300
+ @property
301
+ def toolbar(self):
302
+ """Convenience property"""
303
+ if hasattr(self.view, 'toolbar'):
304
+ return self.view.toolbar
305
+
306
+ @property
307
+ def viewer_type(self):
308
+ """str: the viewer data type see DATA_TYPES"""
309
+ return ViewersEnum[self.__class__.__name__].value
310
+
311
+ def show_data(self, data: DataWithAxes, **kwargs):
312
+ """Entrypoint to display data into the viewer
313
+
314
+ Parameters
315
+ ----------
316
+ data: data_mod.DataFromPlugins
317
+ """
318
+ if len(data.shape) > 4:
319
+ raise ViewerError(f'Ndarray of dim: {len(data.shape)} cannot be plotted using a {self.viewer_type}')
320
+
321
+ self.data_to_export = DataToExport(name=self.title)
322
+ self._raw_data = data
323
+
324
+ self._display_temporary = False
325
+
326
+ self._show_data(data, **kwargs)
327
+
328
+ def show_data_temp(self, data: DataRaw, **kwargs):
329
+ """Entrypoint to display temporary data into the viewer
330
+
331
+ No processed data signal is emitted from the viewer
332
+
333
+ Parameters
334
+ ----------
335
+ data: data_mod.DataFromPlugins
336
+ """
337
+ self._display_temporary = True
338
+ self.show_data(data, **kwargs)
339
+
340
+ def _show_data(self, data: DataRaw):
341
+ """Specific viewers should implement it"""
342
+ raise NotImplementedError
343
+
344
+ def add_attributes_from_view(self):
345
+ """Convenience function to add attributes from the view to self"""
346
+ for attribute in self.convenience_attributes:
347
+ if hasattr(self.view, attribute):
348
+ setattr(self, attribute, getattr(self.view, attribute))
349
+
350
+ def trigger_action(self, action_name: str):
351
+ """Convenience function to trigger programmatically one of the action of the related view"""
352
+ if self.has_action(action_name):
353
+ self.get_action(action_name).trigger()
354
+
355
+ def activate_roi(self, activate=True):
356
+ """Activate the Roi manager using the corresponding action"""
357
+ raise NotImplementedError
358
+
359
+ def setVisible(self, show=True):
360
+ """convenience method to show or hide the paretn widget"""
361
+ self.parent.setVisible(show)
362
+
363
+ @property
364
+ def roi_target(self) -> Union[InfiniteLine.InfiniteLine, ROI.ROI]:
365
+ """To be implemented if necessary (Viewer1D and above)"""
366
+ return None
367
+
368
+ def move_roi_target(self, pos: Iterable[float] = None, **kwargs):
369
+ """move a specific read only ROI at the given position on the viewer"""
370
+ ...
371
+
372
+ def show_roi_target(self, show=True):
373
+ """Show/Hide a specific read only ROI"""
374
+ if self.roi_target is not None:
375
+ self.roi_target.setVisible(show)
376
+
377
+
378
+ if __name__ == '__main__':
379
+ import sys
380
+ import random
381
+ app = QtWidgets.QApplication(sys.argv)
382
+
383
+ dockarea = DockArea()
384
+ prog = ViewerDispatcher(dockarea=dockarea, title='Dispatcher')
385
+ dockarea.show()
386
+ N = 2
387
+ viewers = ['Viewer0D', 'Viewer1D', 'Viewer2D']
388
+ viewers = [ViewersEnum[random.choice(viewers)] for ind in range(N)]
389
+ viewers = ['Viewer2D', 'Viewer2D', ]
390
+ print(viewers)
391
+ prog.update_viewers(viewers)
392
+
393
+ sys.exit(app.exec_())
@@ -0,0 +1,251 @@
1
+ from typing import List, Union
2
+ from numbers import Real
3
+
4
+ from qtpy import QtWidgets, QtGui
5
+ from qtpy.QtCore import QObject, Slot, Signal, Qt
6
+ import sys
7
+ import pyqtgraph
8
+
9
+ import pymodaq.utils.daq_utils as utils
10
+ from pymodaq.utils import data as data_mod
11
+ from pymodaq.utils.logger import set_logger, get_module_name
12
+ from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase
13
+ from pymodaq.utils.managers.action_manager import ActionManager
14
+ from pymodaq.utils.plotting.widgets import PlotWidget
15
+ from pymodaq.utils.plotting.utils.plot_utils import Data0DWithHistory
16
+
17
+ import numpy as np
18
+ from collections import OrderedDict
19
+ import datetime
20
+
21
+ logger = set_logger(get_module_name(__file__))
22
+ PLOT_COLORS = utils.plot_colors
23
+
24
+
25
+ class DataDisplayer(QObject):
26
+ """
27
+ This Object deals with the display of 0D data on a plotitem
28
+ """
29
+
30
+ updated_item = Signal(list)
31
+ labels_changed = Signal(list)
32
+
33
+ def __init__(self, plotitem: pyqtgraph.PlotItem):
34
+ super().__init__()
35
+ self._plotitem = plotitem
36
+ self._plotitem.addLegend()
37
+ self._plot_items: List[pyqtgraph.PlotDataItem] = []
38
+ self._min_lines: List[pyqtgraph.InfiniteLine] = []
39
+ self._max_lines: List[pyqtgraph.InfiniteLine] = []
40
+ self._data = Data0DWithHistory()
41
+
42
+ self._mins: List = []
43
+ self._maxs: List = []
44
+
45
+ self._show_lines: bool = False
46
+
47
+ axis = self._plotitem.getAxis('bottom')
48
+ axis.setLabel(text='Samples', units='S')
49
+
50
+ @property
51
+ def legend(self):
52
+ return self._plotitem.legend
53
+
54
+ @property
55
+ def axis(self):
56
+ return self._data.xaxis
57
+
58
+ def clear_data(self):
59
+ self._data.clear_data()
60
+ self._mins = []
61
+ self._maxs = []
62
+
63
+ def update_axis(self, history_length: int):
64
+ self._data.length = history_length
65
+
66
+ def update_data(self, data: data_mod.DataRaw):
67
+ if len(data) != len(self._plot_items):
68
+ self.update_display_items(data)
69
+
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 = []
76
+
77
+ for ind, label in enumerate(self._data.datas):
78
+ 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
+ while len(self._plot_items) > 0:
89
+ plot_item = self._plotitem.removeItem(self._plot_items.pop(0))
90
+ self.legend.removeItem(plot_item)
91
+ self._plotitem.removeItem(self._max_lines.pop(0))
92
+ 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)
109
+
110
+ def show_min_max(self, show=True):
111
+ self._show_lines = show
112
+ for line in self._max_lines:
113
+ line.setVisible(show)
114
+ for line in self._min_lines:
115
+ line.setVisible(show)
116
+
117
+
118
+ class View0D(ActionManager, QObject):
119
+ def __init__(self, parent_widget: QtWidgets.QWidget = None):
120
+ QObject.__init__(self)
121
+ ActionManager.__init__(self, toolbar=QtWidgets.QToolBar())
122
+
123
+ self.data_displayer: DataDisplayer = None
124
+ self.plot_widget: PlotWidget = PlotWidget()
125
+ self.values_list = QtWidgets.QListWidget()
126
+
127
+ self.setup_actions()
128
+
129
+ self.parent_widget = parent_widget
130
+ if self.parent_widget is None:
131
+ self.parent_widget = QtWidgets.QWidget()
132
+ self.parent_widget.show()
133
+
134
+ self.data_displayer = DataDisplayer(self.plotitem)
135
+
136
+ self._setup_widgets()
137
+ self._connect_things()
138
+ self._prepare_ui()
139
+
140
+ self.get_action('Nhistory').setValue(200) #default history length
141
+
142
+ def setup_actions(self):
143
+ self.add_action('clear', 'Clear plot', 'clear2', 'Clear the current plots')
144
+ self.add_widget('Nhistory', pyqtgraph.SpinBox, tip='Set the history length of the plot',
145
+ setters=dict(setMaximumWidth=100))
146
+ self.add_action('show_data_as_list', 'Show numbers', 'ChnNum', 'If triggered, will display last data as numbers'
147
+ 'in a side panel', checkable=True)
148
+ self.add_action('show_min_max', 'Show Min/Max lines', 'Statistics',
149
+ 'If triggered, will display horizontal dashed lines for min/max of data', checkable=True)
150
+
151
+ def _setup_widgets(self):
152
+ self.parent_widget.setLayout(QtWidgets.QVBoxLayout())
153
+ self.parent_widget.layout().addWidget(self.toolbar)
154
+
155
+ splitter_hor = QtWidgets.QSplitter(Qt.Horizontal)
156
+ self.parent_widget.layout().addWidget(splitter_hor)
157
+
158
+ splitter_hor.addWidget(self.plot_widget)
159
+ splitter_hor.addWidget(self.values_list)
160
+
161
+ font = QtGui.QFont()
162
+ font.setPointSize(20)
163
+ self.values_list.setFont(font)
164
+
165
+ def _connect_things(self):
166
+ self.connect_action('clear', self.data_displayer.clear_data)
167
+ self.connect_action('show_data_as_list', self.show_data_list)
168
+ self.connect_action('Nhistory', self.data_displayer.update_axis, signal_name='valueChanged')
169
+ self.connect_action('show_min_max', self.data_displayer.show_min_max)
170
+
171
+ def _prepare_ui(self):
172
+ """add here everything needed at startup"""
173
+ self.values_list.setVisible(False)
174
+
175
+ def get_double_clicked(self):
176
+ return self.plot_widget.view.sig_double_clicked
177
+
178
+ @property
179
+ def plotitem(self):
180
+ return self.plot_widget.plotItem
181
+
182
+ def display_data(self, data: data_mod.DataRaw):
183
+ self.data_displayer.update_data(data)
184
+ if self.is_action_checked('show_data_as_list'):
185
+ self.values_list.clear()
186
+ self.values_list.addItems(['{:.03e}'.format(dat[0]) for dat in data])
187
+ QtWidgets.QApplication.processEvents()
188
+
189
+ def show_data_list(self, state=None):
190
+ if state is None:
191
+ state = self.is_action_checked('show_data_as_list')
192
+ self.values_list.setVisible(state)
193
+
194
+
195
+ class Viewer0D(ViewerBase):
196
+ """this plots 0D data on a plotwidget with history. Display as numbers in a table is possible.
197
+
198
+ Datas and measurements are then exported with the signal data_to_export_signal
199
+ """
200
+
201
+ def __init__(self, parent=None, title=''):
202
+ super().__init__(parent, title)
203
+ self.view = View0D(self.parent)
204
+ self._labels = []
205
+
206
+ @property
207
+ def labels(self):
208
+ return self._labels
209
+
210
+ @labels.setter
211
+ def labels(self, labels):
212
+ if labels != self._labels:
213
+ self._labels = labels
214
+
215
+ @Slot(list)
216
+ def _show_data(self, data: data_mod.DataRaw):
217
+ self.labels = data.labels
218
+ self.view.display_data(data)
219
+ self.data_to_export_signal.emit(self.data_to_export)
220
+
221
+
222
+ def main_view():
223
+ app = QtWidgets.QApplication(sys.argv)
224
+ widget = QtWidgets.QWidget()
225
+ prog = View0D(widget)
226
+ widget.show()
227
+ sys.exit(app.exec_())
228
+
229
+
230
+ def main():
231
+ app = QtWidgets.QApplication(sys.argv)
232
+ widget = QtWidgets.QWidget()
233
+ prog = Viewer0D(widget)
234
+ from pymodaq.utils.daq_utils import gauss1D
235
+
236
+ x = np.linspace(0, 200, 201)
237
+ y1 = gauss1D(x, 75, 25) + 0.1*np.random.rand(len(x))
238
+ y2 = 0.7 * gauss1D(x, 120, 50, 2) + 0.2*np.random.rand(len(x))
239
+ widget.show()
240
+ prog.get_action('show_data_as_list').trigger()
241
+ for ind, data in enumerate(y1):
242
+ prog.show_data(data_mod.DataRaw('mydata', data=[np.array([data]), np.array([y2[ind]])],
243
+ labels=['lab1', 'lab2']))
244
+ QtWidgets.QApplication.processEvents()
245
+
246
+ sys.exit(app.exec_())
247
+
248
+
249
+ if __name__ == '__main__': # pragma: no cover
250
+ #main_view()
251
+ main()