pymodaq 5.1.6__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.
Files changed (154) hide show
  1. pymodaq/__init__.py +98 -0
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +1238 -0
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +1517 -0
  13. pymodaq/control_modules/daq_viewer_ui.py +407 -0
  14. pymodaq/control_modules/mocks.py +57 -0
  15. pymodaq/control_modules/move_utility_classes.py +1141 -0
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +591 -0
  19. pymodaq/control_modules/viewer_utility_classes.py +670 -0
  20. pymodaq/daq_utils/__init__.py +0 -0
  21. pymodaq/daq_utils/daq_utils.py +6 -0
  22. pymodaq/dashboard.py +2396 -0
  23. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
  24. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
  25. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
  26. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
  27. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
  28. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
  29. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
  30. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
  31. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
  32. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
  33. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
  34. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
  35. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
  36. pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
  37. pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
  38. pymodaq/examples/__init__.py +0 -0
  39. pymodaq/examples/function_plotter.py +160 -0
  40. pymodaq/examples/nonlinearscanner.py +126 -0
  41. pymodaq/examples/qt_less_standalone_module.py +165 -0
  42. pymodaq/examples/tcp_client.py +97 -0
  43. pymodaq/extensions/__init__.py +25 -0
  44. pymodaq/extensions/adaptive/__init__.py +2 -0
  45. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  46. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  47. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  48. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  49. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  50. pymodaq/extensions/adaptive/utils.py +123 -0
  51. pymodaq/extensions/bayesian/__init__.py +2 -0
  52. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  53. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  54. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  55. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  56. pymodaq/extensions/bayesian/utils.py +180 -0
  57. pymodaq/extensions/console.py +73 -0
  58. pymodaq/extensions/daq_logger/__init__.py +1 -0
  59. pymodaq/extensions/daq_logger/abstract.py +52 -0
  60. pymodaq/extensions/daq_logger/daq_logger.py +519 -0
  61. pymodaq/extensions/daq_logger/db/__init__.py +0 -0
  62. pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
  63. pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
  64. pymodaq/extensions/daq_logger/h5logging.py +84 -0
  65. pymodaq/extensions/daq_scan.py +1218 -0
  66. pymodaq/extensions/daq_scan_ui.py +241 -0
  67. pymodaq/extensions/data_mixer/__init__.py +0 -0
  68. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  69. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  70. pymodaq/extensions/data_mixer/model.py +108 -0
  71. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  72. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  73. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  74. pymodaq/extensions/data_mixer/parser.py +53 -0
  75. pymodaq/extensions/data_mixer/utils.py +23 -0
  76. pymodaq/extensions/h5browser.py +9 -0
  77. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  78. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  79. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  80. pymodaq/extensions/optimizers_base/utils.py +427 -0
  81. pymodaq/extensions/pid/__init__.py +16 -0
  82. pymodaq/extensions/pid/actuator_controller.py +14 -0
  83. pymodaq/extensions/pid/daq_move_PID.py +154 -0
  84. pymodaq/extensions/pid/pid_controller.py +1016 -0
  85. pymodaq/extensions/pid/utils.py +189 -0
  86. pymodaq/extensions/utils.py +111 -0
  87. pymodaq/icon.ico +0 -0
  88. pymodaq/post_treatment/__init__.py +6 -0
  89. pymodaq/post_treatment/load_and_plot.py +352 -0
  90. pymodaq/resources/__init__.py +0 -0
  91. pymodaq/resources/config_template.toml +57 -0
  92. pymodaq/resources/preset_default.xml +1 -0
  93. pymodaq/resources/setup_plugin.py +73 -0
  94. pymodaq/splash.png +0 -0
  95. pymodaq/utils/__init__.py +0 -0
  96. pymodaq/utils/array_manipulation.py +6 -0
  97. pymodaq/utils/calibration_camera.py +180 -0
  98. pymodaq/utils/chrono_timer.py +203 -0
  99. pymodaq/utils/config.py +53 -0
  100. pymodaq/utils/conftests.py +5 -0
  101. pymodaq/utils/daq_utils.py +158 -0
  102. pymodaq/utils/data.py +128 -0
  103. pymodaq/utils/enums.py +6 -0
  104. pymodaq/utils/exceptions.py +38 -0
  105. pymodaq/utils/gui_utils/__init__.py +10 -0
  106. pymodaq/utils/gui_utils/loader_utils.py +75 -0
  107. pymodaq/utils/gui_utils/utils.py +18 -0
  108. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  109. pymodaq/utils/h5modules/__init__.py +2 -0
  110. pymodaq/utils/h5modules/module_saving.py +526 -0
  111. pymodaq/utils/leco/__init__.py +25 -0
  112. pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
  113. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
  114. pymodaq/utils/leco/director_utils.py +74 -0
  115. pymodaq/utils/leco/leco_director.py +166 -0
  116. pymodaq/utils/leco/pymodaq_listener.py +364 -0
  117. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  118. pymodaq/utils/leco/utils.py +74 -0
  119. pymodaq/utils/logger.py +6 -0
  120. pymodaq/utils/managers/__init__.py +0 -0
  121. pymodaq/utils/managers/batchscan_manager.py +346 -0
  122. pymodaq/utils/managers/modules_manager.py +589 -0
  123. pymodaq/utils/managers/overshoot_manager.py +242 -0
  124. pymodaq/utils/managers/preset_manager.py +229 -0
  125. pymodaq/utils/managers/preset_manager_utils.py +262 -0
  126. pymodaq/utils/managers/remote_manager.py +484 -0
  127. pymodaq/utils/math_utils.py +6 -0
  128. pymodaq/utils/messenger.py +6 -0
  129. pymodaq/utils/parameter/__init__.py +10 -0
  130. pymodaq/utils/parameter/utils.py +6 -0
  131. pymodaq/utils/scanner/__init__.py +5 -0
  132. pymodaq/utils/scanner/scan_config.py +16 -0
  133. pymodaq/utils/scanner/scan_factory.py +259 -0
  134. pymodaq/utils/scanner/scan_selector.py +477 -0
  135. pymodaq/utils/scanner/scanner.py +324 -0
  136. pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
  137. pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
  138. pymodaq/utils/scanner/scanners/__init__.py +1 -0
  139. pymodaq/utils/scanner/scanners/sequential.py +224 -0
  140. pymodaq/utils/scanner/scanners/tabular.py +319 -0
  141. pymodaq/utils/scanner/utils.py +110 -0
  142. pymodaq/utils/svg/__init__.py +6 -0
  143. pymodaq/utils/svg/svg_renderer.py +20 -0
  144. pymodaq/utils/svg/svg_view.py +35 -0
  145. pymodaq/utils/svg/svg_viewer2D.py +50 -0
  146. pymodaq/utils/tcp_ip/__init__.py +6 -0
  147. pymodaq/utils/tcp_ip/mysocket.py +12 -0
  148. pymodaq/utils/tcp_ip/serializer.py +13 -0
  149. pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
  150. pymodaq-5.1.6.dist-info/METADATA +238 -0
  151. pymodaq-5.1.6.dist-info/RECORD +154 -0
  152. pymodaq-5.1.6.dist-info/WHEEL +4 -0
  153. pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
  154. pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,189 @@
1
+ import importlib
2
+ import inspect
3
+ import pkgutil
4
+ import warnings
5
+ from pathlib import Path
6
+ from typing import Union, List
7
+
8
+ from pymodaq_utils.logger import get_module_name, set_logger
9
+ from pymodaq_utils.warnings import deprecation_msg
10
+ from pymodaq_utils.utils import find_dict_in_list_from_key_val, get_entrypoints
11
+
12
+ from pymodaq_gui.utils.dock import DockArea
13
+
14
+ from pymodaq_data.data import DataToExport
15
+
16
+ from pymodaq.utils.daq_utils import get_plugins
17
+ from pymodaq.utils.data import DataActuator, DataToActuators
18
+
19
+
20
+ logger = set_logger(get_module_name(__file__))
21
+
22
+
23
+ DAQ_Move_Stage_type = get_plugins('daq_move')
24
+ DAQ_0DViewer_Det_types = get_plugins('daq_0Dviewer')
25
+ DAQ_1DViewer_Det_types = get_plugins('daq_1Dviewer')
26
+ DAQ_2DViewer_Det_types = get_plugins('daq_2Dviewer')
27
+ DAQ_NDViewer_Det_types = get_plugins('daq_NDviewer')
28
+
29
+
30
+
31
+ class DataToActuatorPID(DataToActuators):
32
+
33
+ def __init__(self, *args, **kwargs):
34
+ super().__init__(*args, **kwargs)
35
+ deprecation_msg('DataToActuatorPID object is deprecated use: pymodaq.utils.data:DataToActuators')
36
+
37
+ class PIDModelGeneric:
38
+ limits = dict(max=dict(state=False, value=1),
39
+ min=dict(state=False, value=0),)
40
+ konstants = dict(kp=1, ki=0.1, kd=0.001)
41
+ params = []
42
+
43
+ Nsetpoints = 1
44
+ setpoint_ini = [0. for ind in range(Nsetpoints)]
45
+ setpoints_names = ['' for ind in range(Nsetpoints)]
46
+
47
+ actuators_name = []
48
+ detectors_name = []
49
+
50
+ epsilon = 1
51
+
52
+ def __init__(self, pid_controller):
53
+ self.pid_controller = pid_controller # instance of the pid_controller using this model
54
+ self.modules_manager = pid_controller.modules_manager
55
+
56
+ self.settings = self.pid_controller.settings.child('models', 'model_params') # set of parameters
57
+ self.data_names = None
58
+ self.curr_output = [0. for ind in range(self.Nsetpoints)]
59
+ self.curr_input = None
60
+
61
+ self.check_modules(self.modules_manager)
62
+
63
+ def setpoint(self, values):
64
+ self.pid_controller.setpoints = values
65
+
66
+ def apply_constants(self):
67
+ for kxx in self.konstants:
68
+ self.pid_controller.settings.child('main_settings', 'pid_settings', 'pid_constants',
69
+ kxx).setValue(self.konstants[kxx])
70
+
71
+ def apply_limits(self):
72
+ for limit in self.limits:
73
+ self.pid_controller.settings.child('main_settings', 'pid_settings', 'output_limits',
74
+ f'output_limit_{limit}').setValue(self.limits[limit]['value'])
75
+ self.pid_controller.settings.child('main_settings', 'pid_settings', 'output_limits',
76
+ f'output_limit_{limit}_enabled').setValue(self.limits[limit]['state'])
77
+
78
+ def check_modules(self, modules_manager):
79
+ for act in self.actuators_name:
80
+ if act not in modules_manager.actuators_name:
81
+ logger.warning(f'The actuator {act} defined in the PID model is not present in the Dashboard')
82
+ return False
83
+ for det in self.detectors_name:
84
+ if det not in modules_manager.detectors_name:
85
+ logger.warning(f'The detector {det} defined in the PID model is not present in the Dashboard')
86
+
87
+ def update_detector_names(self):
88
+ names = self.pid_controller.settings.child('main_settings', 'detector_modules').value()['selected']
89
+ self.data_names = []
90
+ for name in names:
91
+ name = name.split('//')
92
+ self.data_names.append(name)
93
+
94
+ def update_settings(self, param):
95
+ """
96
+ Get a parameter instance whose value has been modified by a user on the UI
97
+ To be overwritten in child class
98
+ """
99
+ if param.name() == '':
100
+ pass
101
+
102
+ def ini_model(self):
103
+ self.apply_limits()
104
+ self.setpoint(self.setpoint_ini)
105
+ self.apply_constants()
106
+
107
+ def convert_input(self, measurements: DataToExport) -> DataToExport:
108
+ """
109
+ Convert the measurements in the units to be fed to the PID (same dimensionality as the setpoint)
110
+ Parameters
111
+ ----------
112
+ measurements: DataToExport
113
+ DataToExport object from which the model extract a value of the same units as the setpoint
114
+
115
+ Returns
116
+ -------
117
+ DataToExport: the converted input as 0D DataCalculated stored in a DataToExport
118
+ """
119
+ raise NotImplementedError
120
+
121
+ def convert_output(self, outputs: List[float], **kwargs) -> DataToActuators:
122
+ """
123
+ Convert the output of the PID in units to be fed into the actuator
124
+ Parameters
125
+ ----------
126
+ outputs: (list of float) output value from the PID from which the model extract a value of the same units as the actuator
127
+
128
+ Returns
129
+ -------
130
+ DataToActuatorPID: the converted output as a DataToActuatorPID object (derived from DataToExport)
131
+
132
+ """
133
+ if kwargs.get('dt', None) is not None or kwargs.get('stab', None) is not None:
134
+ logger.warning("dt and stab are deprecated, it will be removed in a future release.")
135
+
136
+ self.curr_output = outputs
137
+ return DataToActuators('pid', mode='rel',
138
+ data=[DataActuator(self.actuators_name[ind], data=outputs[ind])
139
+ for ind in range(len(outputs))])
140
+
141
+
142
+ def main(xmlfile):
143
+ from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
144
+ from pathlib import Path
145
+ from qtpy import QtWidgets
146
+ from pymodaq_gui.utils.utils import mkQApp
147
+
148
+ import sys
149
+ app = mkQApp('BeamSteering')
150
+ dashboard, extension, win = load_dashboard_with_preset(xmlfile, 'DAQ_PID')
151
+ sys.exit(app.exec_())
152
+
153
+
154
+ def get_models(model_name=None):
155
+ """
156
+ Get PID Models as a list to instantiate Control Actuators per degree of liberty in the model
157
+
158
+ Returns
159
+ -------
160
+ list: list of disct containting the name and python module of the found models
161
+ """
162
+ models_import = []
163
+ discovered_models = list(get_entrypoints(group='pymodaq.pid_models'))
164
+ discovered_models.extend(list(get_entrypoints(group='pymodaq.models')))
165
+ if len(discovered_models) > 0:
166
+ for pkg in discovered_models:
167
+ try:
168
+ module = importlib.import_module(pkg.value)
169
+ module_name = pkg.value
170
+
171
+ for mod in pkgutil.iter_modules([str(Path(module.__file__).parent.joinpath('models'))]):
172
+ try:
173
+ model_module = importlib.import_module(f'{module_name}.models.{mod.name}', module)
174
+ classes = inspect.getmembers(model_module, inspect.isclass)
175
+ for name, klass in classes:
176
+ if klass.__base__ is PIDModelGeneric:
177
+ models_import.append({'name': mod.name, 'module': model_module, 'class': klass})
178
+ break
179
+
180
+ except Exception as e: # pragma: no cover
181
+ logger.warning(str(e))
182
+
183
+ except Exception as e: # pragma: no cover
184
+ logger.warning(f'Impossible to import the {pkg.value} extension: {str(e)}')
185
+
186
+ if model_name is None:
187
+ return models_import
188
+ else:
189
+ return find_dict_in_list_from_key_val(models_import, 'name', model_name)
@@ -0,0 +1,111 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 27/10/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ import importlib
8
+ from pathlib import Path
9
+ import pkgutil
10
+ import warnings
11
+ from typing import Union, TYPE_CHECKING
12
+
13
+ from qtpy import QtCore, QtWidgets
14
+
15
+ from pymodaq_gui.utils.dock import DockArea
16
+ from pymodaq_utils.utils import get_entrypoints
17
+ from pymodaq_utils import logger as logger_module
18
+ from pymodaq_gui.utils.custom_app import CustomApp
19
+
20
+ from pymodaq.utils.managers.modules_manager import ModulesManager
21
+
22
+ logger = logger_module.set_logger(logger_module.get_module_name(__file__))
23
+
24
+ if TYPE_CHECKING:
25
+ from pymodaq.dashboard import DashBoard
26
+
27
+
28
+ def get_ext_modules(path: Path):
29
+ modules = []
30
+ for mod in pkgutil.iter_modules([path]):
31
+ modules.append(mod.name)
32
+ return modules
33
+
34
+
35
+ def get_extensions():
36
+ """
37
+ Get pymodaq extensions as a list
38
+
39
+ Returns
40
+ -------
41
+ list: list of dict containing the name and module of the found extension
42
+
43
+ Each dict is defined with four keys:
44
+ * pkg: the name of the plugin package
45
+ * module: the module name where your extension class is defined
46
+ * class_name: the name of the class defining the extension
47
+ * name: a nice name for your extension to be displayed in the menu
48
+
49
+ """
50
+ extension_import = []
51
+ discovered_extension = get_entrypoints(group='pymodaq.extensions')
52
+ if len(discovered_extension) > 0:
53
+ for pkg in discovered_extension:
54
+ try:
55
+ module = importlib.import_module(pkg.value)
56
+ modules = get_ext_modules(Path(module.__path__[0]).joinpath('extensions'))
57
+ for mod in modules:
58
+ try:
59
+ mod_in = importlib.import_module(f'{pkg.value}.extensions.{mod}')
60
+ if hasattr(mod_in, 'EXTENSION_NAME'):
61
+ extension_import.append({'pkg': pkg.value, 'module': mod,
62
+ 'name': mod_in.EXTENSION_NAME,
63
+ 'class_name': mod_in.CLASS_NAME})
64
+
65
+ except Exception as e: # pragma: no cover
66
+ logger.warning(f'Impossible to import the {pkg.value}.extensions.{mod} extension: '
67
+ f'{str(e)}')
68
+ except Exception as e:
69
+ logger.warning(f'Impossible to import the {pkg.value} package: '
70
+ f'{str(e)}')
71
+
72
+ return extension_import
73
+
74
+
75
+ class CustomExt(CustomApp):
76
+
77
+ def __init__(self, parent: Union[DockArea, QtWidgets.QWidget, QtWidgets.QMainWindow],
78
+ dashboard: 'DashBoard', **kwargs):
79
+ super().__init__(parent, **kwargs)
80
+
81
+ self.dashboard = dashboard
82
+ self.runner_thread : QtCore.QThread = None
83
+ if dashboard is not None:
84
+ self._modules_manager = ModulesManager(detectors=self.dashboard.detector_modules,
85
+ actuators=self.dashboard.actuators_modules,
86
+ parent_name=self.__class__.__name__)
87
+ else:
88
+ self._modules_manager = None
89
+
90
+ @property
91
+ def splash(self):
92
+ return self.dashboard.splash_sc
93
+
94
+ @property
95
+ def modules_manager(self) -> ModulesManager:
96
+ """useful tool to interact with DAQ_Moves and DAQ_Viewers
97
+
98
+ Will be available if a DashBoard has been set
99
+
100
+ Returns
101
+ -------
102
+ ModulesManager
103
+ """
104
+ return self._modules_manager
105
+
106
+ def exit_runner_thread(self, duration : int = 5000):
107
+ self.runner_thread.quit()
108
+ terminated = self.runner_thread.wait(duration)
109
+ if not terminated:
110
+ self.runner_thread.terminate()
111
+ self.runner_thread.wait()
pymodaq/icon.ico ADDED
Binary file
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 27/10/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
@@ -0,0 +1,352 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 22/01/2023
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ import os
8
+ import sys
9
+ from typing import List, Union, Callable, Iterable, Dict
10
+
11
+ from qtpy import QtWidgets, QtCore
12
+
13
+ from pymodaq_utils.logger import set_logger, get_module_name
14
+
15
+ from pymodaq_data.data import DataToExport, DataDim, enum_checker
16
+ from pymodaq_data.h5modules.data_saving import DataLoader
17
+
18
+ from pymodaq_gui.h5modules.saving import H5Saver
19
+ from pymodaq_gui.plotting.data_viewers.viewer import ViewerBase, ViewerDispatcher
20
+ from pymodaq_gui.plotting.data_viewers import ViewersEnum, Viewer1D, Viewer2D, ViewerND
21
+ from pymodaq_gui.utils import Dock, DockArea
22
+
23
+ from pymodaq.utils.data import DataFromPlugins
24
+
25
+ logger = set_logger(get_module_name(__file__))
26
+
27
+
28
+ class LoaderPlotter:
29
+
30
+ grouped_data0D_fullname = 'Grouped/Data0D'
31
+
32
+ def __init__(self, dockarea):
33
+ self.dockarea = dockarea
34
+ self.dispatcher = ViewerDispatcher(dockarea, title='ViewerDispatcher')
35
+ self._viewers: dict[str, ViewerBase] = None
36
+ self._viewer_docks: dict[str, ViewerBase] = None
37
+ self._viewer_types: List[ViewersEnum] = None
38
+ self._h5saver: H5Saver = None
39
+ self._data: DataToExport = None
40
+ self.dataloader: DataLoader = None
41
+
42
+ @property
43
+ def viewers(self) -> List[ViewerBase]:
44
+ return self.dispatcher.viewers
45
+
46
+ def connect_double_clicked(self, slot: Callable):
47
+ for viewer in self.viewers:
48
+ viewer.sig_double_clicked.connect(slot)
49
+
50
+ def disconnect(self, slot: Callable):
51
+ for viewer in self.viewers:
52
+ viewer.sig_double_clicked.disconnect(slot)
53
+
54
+ def clear_viewers(self):
55
+ self.dispatcher.remove_viewers(0)
56
+
57
+ @property
58
+ def h5saver(self):
59
+ return self._h5saver
60
+
61
+ @h5saver.setter
62
+ def h5saver(self, h5saver: H5Saver):
63
+ self._h5saver = h5saver
64
+ self.dataloader = DataLoader(h5saver)
65
+
66
+ @property
67
+ def data(self) -> DataToExport:
68
+ return self._data
69
+
70
+ def load_data(self, filter_dims: List[Union[DataDim, str]] = None,
71
+ filter_full_names: List[str] = None, remove_navigation: bool = True,
72
+ group_0D=False, average_axis: int=None, average_index: int = 0,
73
+ last_step=False, separate_average=False):
74
+ """Load Data from the h5 node of the dataloader and apply some filtering/manipulation before
75
+ plotting
76
+
77
+ Parameters
78
+ ----------
79
+ filter_dims: List[Union[DataDim, str]]
80
+ load only data with correct dims
81
+ filter_full_names: List[str]
82
+ load only data matching these names
83
+ remove_navigation: bool
84
+ if True, make navigation axes as signal axes (means DataND could be plotted on Viewer1D
85
+ or Viewer2D by concatenation)
86
+ group_0D: bool
87
+ if True, group all (initial) Data0D into one DataFromPlugins
88
+ average_axis: int or None
89
+ which axis in the data shapes should be interpereted as the average (in general it is 0
90
+ or None)
91
+ average_index: int
92
+ which step in the averaging process are we in.
93
+ last_step: bool
94
+ tells if this is the very last step of the (averaged) scan
95
+ separate_average: bool
96
+ Tells if the averaged data are to be plotted on the same data viewer panel or another one
97
+
98
+ Returns
99
+ -------
100
+ DataToExport
101
+ """
102
+
103
+ self._data = DataToExport('All')
104
+ self.dataloader.load_all('/', self._data)
105
+
106
+ if average_axis is not None:
107
+ self.average_axis(average_axis, average_index, last_step=last_step,
108
+ separate_average=separate_average)
109
+
110
+ if filter_dims is not None:
111
+ filter_dims[:] = [enum_checker(DataDim, dim) for dim in filter_dims]
112
+ self._data.data[:] = [data for data in self._data if data.dim in filter_dims]
113
+
114
+ if filter_full_names is not None:
115
+ self._data.data[:] = [data for data in self._data if data.get_full_name() in
116
+ filter_full_names]
117
+
118
+ if group_0D: # 0D initial data
119
+ self.group_0D_data(separate_average=separate_average)
120
+
121
+ if remove_navigation:
122
+ self.remove_navigation_axes()
123
+
124
+ return self._data
125
+
126
+ def average_axis(self, average_axis, average_index, last_step=False,
127
+ separate_average=False) -> None:
128
+ """ Average the data along their average axis
129
+
130
+ Parameters
131
+ ----------
132
+ average_axis: int or None
133
+ which axis in the data shapes should be interpreted as the average
134
+ (in general it is 0 or None)
135
+ average_index: int
136
+ which step in the averaging process are we in.
137
+ last_step: bool
138
+ tells if this is the very last step of the (averaged) scan
139
+ separate_average: bool
140
+ Tells if the averaged data are to be plotted on the same data viewer panel or another one
141
+
142
+ """
143
+ if separate_average and average_index > 0:
144
+ averaged_data = DataToExport('Averaged')
145
+ for ind, data in enumerate(self._data):
146
+ current_data = data.inav[average_index, ...]
147
+ if average_index > 0:
148
+ if last_step:
149
+ data_to_append = data.inav[0:, ...].mean(axis=average_axis)
150
+ else:
151
+ if average_index == 1:
152
+ data_to_append = data.inav[0, ...]
153
+ else:
154
+ data_to_append = data.inav[0:average_index, ...].mean(axis=average_axis)
155
+ data_to_append.name = f'{data_to_append.name}_averaged'
156
+ data_to_append.labels = [f'{label}_averaged' for label in data_to_append.labels]
157
+ if not (separate_average and average_index > 0):
158
+ current_data.append(data_to_append)
159
+ else:
160
+ averaged_data.append(data_to_append)
161
+ self._data[ind] = current_data
162
+ if separate_average and average_index > 0:
163
+ self._data.append(averaged_data.data)
164
+
165
+
166
+ def remove_navigation_axes(self):
167
+ """Make the navigation axes as signal axes
168
+
169
+ transforms DataND into Data1D or Data2D or error... depending the exact shape of the data
170
+ and the number of navigation axes
171
+ """
172
+ for data in self._data:
173
+ data.nav_indexes = ()
174
+ data.transpose() # because usual ND data should be plotted here as 2D with the nav axes as the minor
175
+ # (horizontal)
176
+
177
+ def group_0D_data(self, separate_average=False):
178
+ """Group in a single DataFromPlugins all data that are initialy Data0D
179
+
180
+ """
181
+ data = self._data.get_data_from_sig_axes(0)
182
+ if len(data) > 0:
183
+ data0D_arrays = []
184
+ data0D_arrays_averaged = []
185
+ labels = []
186
+ labels_averaged = []
187
+ for dwa in data:
188
+ if 'averaged' in dwa.name and separate_average:
189
+ data0D_arrays_averaged.extend(dwa.data)
190
+ labels_averaged.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
191
+ self._data.remove(dwa)
192
+
193
+
194
+ else:
195
+ data0D_arrays.extend(dwa.data)
196
+ labels.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
197
+ self._data.remove(dwa)
198
+
199
+ data0D = DataFromPlugins(self.grouped_data0D_fullname.split('/')[1],
200
+ data=data0D_arrays, labels=labels,
201
+ dim='DataND',
202
+ origin=self.grouped_data0D_fullname.split('/')[0],
203
+ axes=dwa.axes, nav_indexes=dwa.nav_indexes,
204
+ )
205
+ self._data.append(data0D)
206
+ if 'averaged' in dwa.name and separate_average:
207
+ data0D_averaged = DataFromPlugins(
208
+ f"{self.grouped_data0D_fullname.split('/')[1]}_averaged",
209
+ data=data0D_arrays_averaged, labels=labels_averaged,
210
+ dim='DataND',
211
+ origin=self.grouped_data0D_fullname.split('/')[0],
212
+ axes=dwa.axes, nav_indexes=dwa.nav_indexes,
213
+ )
214
+ self._data.append(data0D_averaged)
215
+
216
+ def load_plot_data(self, **kwargs):
217
+ """Load and plot all data from the current H5Saver
218
+
219
+ See Also
220
+ -----
221
+ load_data
222
+ """
223
+
224
+ target_at = kwargs.pop('target_at') if 'target_at' in kwargs else None
225
+ last_step = kwargs.pop('last_step') if 'last_step' in kwargs else False
226
+ crosshair_at = kwargs.pop('crosshair_at') if 'crosshair_at' in kwargs else None
227
+
228
+ self.load_data(**kwargs)
229
+ self.show_data(target_at=target_at,
230
+ crosshair_at=crosshair_at)
231
+ if (last_step and 'average_index' in kwargs and kwargs['average_index']
232
+ is not None):
233
+ kwargs['last_step'] = last_step
234
+ self.load_data(**kwargs)
235
+ self.show_data(target_at=target_at,
236
+ crosshair_at=crosshair_at)
237
+
238
+ def show_data(self, **kwargs):
239
+ """Send data to their dedicated viewers
240
+ """
241
+ try:
242
+ self.set_data_to_viewers(self._data, **kwargs)
243
+ except Exception as e:
244
+ logger.warning(f'Could not show data: {str(e)}')
245
+
246
+ def _init_show_data(self, data: DataToExport):
247
+ """Processing before showing data
248
+ """
249
+ self._viewer_types = [ViewersEnum(data.dim.name) for data in data]
250
+ self.prepare_viewers(self._viewer_types)
251
+
252
+ def prepare_viewers(self, viewers_enum: List[Union[ViewersEnum, str]],
253
+ viewers_name: List[str] = None) -> List[ViewerBase]:
254
+
255
+ if self._viewers is not None:
256
+ while len(self._viewers) > 0:
257
+ self._viewers.pop(list(self._viewers.keys())[0])
258
+ self._viewer_docks.pop(list(self._viewer_docks.keys())[0])
259
+
260
+ self._viewer_types = [enum_checker(ViewersEnum, viewer_enum) for viewer_enum in viewers_enum]
261
+ if viewers_name is None or len(viewers_enum) != len(viewers_name):
262
+ viewers_name = [f'DataPlot{ind:02d}' for ind in range(len(self._viewer_types))]
263
+
264
+ if self.dispatcher.viewer_types != self._viewer_types:
265
+ self.dispatcher.update_viewers(self._viewer_types,
266
+ viewers_name=viewers_name)
267
+
268
+ self._viewers = dict(zip(viewers_name, self.dispatcher.viewers))
269
+ self._viewer_docks = dict(zip(viewers_name, self.dispatcher.viewer_docks))
270
+ return self.dispatcher.viewers
271
+
272
+ def set_data_to_viewers(self, data: DataToExport, temp=False,
273
+ target_at: Iterable[float] = None,
274
+ crosshair_at: Iterable[float] = None):
275
+ """Process data dimensionality and send appropriate data to their data viewers
276
+
277
+ Parameters
278
+ ----------
279
+ data: list of DataFromPlugins
280
+ temp: bool
281
+ if True notify the data viewers to display data as temporary (meaning not exporting processed data from roi)
282
+ target_at: Iterable[float]
283
+ if specified show and plot the roi_target of each viewer at the given position
284
+ crosshair_at: Iterable[float]
285
+ if specified show and plot the viewer crosshair of each viewer at the given position
286
+ See Also
287
+ --------
288
+ ViewerBase, Viewer0D, Viewer1D, Viewer2D
289
+ """
290
+ for ind, _data in enumerate(data.data):
291
+ viewer = self._viewers[_data.get_full_name()]
292
+ self._viewer_docks[_data.get_full_name()].setTitle(_data.name)
293
+
294
+ # viewer = self.viewers[ind]
295
+ # self.dispatcher.viewer_docks[ind].setTitle(_data.name)
296
+
297
+ viewer.title = _data.name
298
+ if temp:
299
+ viewer.show_data_temp(_data)
300
+ else:
301
+ viewer.show_data(_data)
302
+ if crosshair_at is not None:
303
+ if not viewer.is_action_checked('crosshair'):
304
+ viewer.get_action('crosshair').trigger()
305
+ viewer.double_clicked(*crosshair_at)
306
+ if target_at is not None:
307
+ viewer.show_roi_target(True)
308
+ if isinstance(viewer, Viewer1D):
309
+ viewer.move_roi_target(target_at)
310
+ elif isinstance(viewer, Viewer2D):
311
+ _target_at = target_at.copy()
312
+ if _data.distribution == 'uniform':
313
+ size = [_data.get_axis_from_index(1)[0].scaling]
314
+ if len(_target_at) == 1: # means concatenation of 1D data
315
+ axis = _data.get_axis_from_index(0)[0]
316
+ size.append(axis.scaling * axis.size)
317
+ _target_at = list(_target_at) + [axis.offset]
318
+ else:
319
+ size.append(_data.get_axis_from_index(0)[0].scaling)
320
+ viewer.move_roi_target(_target_at, size)
321
+ else:
322
+ viewer.move_roi_target(_target_at)
323
+
324
+ def main(init_qt=True):
325
+ if init_qt: # used for the test suite
326
+ app = QtWidgets.QApplication(sys.argv)
327
+
328
+ path = r'C:\Users\weber\Downloads\temp_data.h5'
329
+
330
+ h5saver = H5Saver()
331
+ h5saver.open_file(path)
332
+
333
+ win = QtWidgets.QMainWindow()
334
+ area = DockArea()
335
+ win.setCentralWidget(area)
336
+ win.resize(1000, 500)
337
+ win.setWindowTitle('PyMoDAQ Viewer')
338
+ win.show()
339
+
340
+ loader = LoaderPlotter(area)
341
+ loader.h5saver = h5saver
342
+ data = loader.load_data(filter_dims=['Data2D', 'Data1D'], group_1D=True)
343
+ loader._init_show_data(data)
344
+ loader.show_data()
345
+
346
+ if init_qt:
347
+ sys.exit(app.exec_())
348
+ return loader, win
349
+
350
+
351
+ if __name__ == '__main__':
352
+ main()
File without changes