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.
- pymodaq/__init__.py +98 -0
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +1238 -0
- pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/factory.py +48 -0
- pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
- pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
- pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
- pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
- pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
- pymodaq/control_modules/daq_viewer.py +1517 -0
- pymodaq/control_modules/daq_viewer_ui.py +407 -0
- pymodaq/control_modules/mocks.py +57 -0
- pymodaq/control_modules/move_utility_classes.py +1141 -0
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +591 -0
- pymodaq/control_modules/viewer_utility_classes.py +670 -0
- pymodaq/daq_utils/__init__.py +0 -0
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +2396 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
- pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
- pymodaq/examples/__init__.py +0 -0
- pymodaq/examples/function_plotter.py +160 -0
- pymodaq/examples/nonlinearscanner.py +126 -0
- pymodaq/examples/qt_less_standalone_module.py +165 -0
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +25 -0
- pymodaq/extensions/adaptive/__init__.py +2 -0
- pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
- pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
- pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
- pymodaq/extensions/adaptive/utils.py +123 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
- pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
- pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
- pymodaq/extensions/bayesian/utils.py +180 -0
- pymodaq/extensions/console.py +73 -0
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/abstract.py +52 -0
- pymodaq/extensions/daq_logger/daq_logger.py +519 -0
- pymodaq/extensions/daq_logger/db/__init__.py +0 -0
- pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
- pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
- pymodaq/extensions/daq_logger/h5logging.py +84 -0
- pymodaq/extensions/daq_scan.py +1218 -0
- pymodaq/extensions/daq_scan_ui.py +241 -0
- pymodaq/extensions/data_mixer/__init__.py +0 -0
- pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
- pymodaq/extensions/data_mixer/data_mixer.py +262 -0
- pymodaq/extensions/data_mixer/model.py +108 -0
- pymodaq/extensions/data_mixer/models/__init__.py +0 -0
- pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
- pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
- pymodaq/extensions/data_mixer/parser.py +53 -0
- pymodaq/extensions/data_mixer/utils.py +23 -0
- pymodaq/extensions/h5browser.py +9 -0
- pymodaq/extensions/optimizers_base/__init__.py +0 -0
- pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
- pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
- pymodaq/extensions/optimizers_base/utils.py +427 -0
- pymodaq/extensions/pid/__init__.py +16 -0
- pymodaq/extensions/pid/actuator_controller.py +14 -0
- pymodaq/extensions/pid/daq_move_PID.py +154 -0
- pymodaq/extensions/pid/pid_controller.py +1016 -0
- pymodaq/extensions/pid/utils.py +189 -0
- pymodaq/extensions/utils.py +111 -0
- pymodaq/icon.ico +0 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/post_treatment/load_and_plot.py +352 -0
- pymodaq/resources/__init__.py +0 -0
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -0
- pymodaq/resources/setup_plugin.py +73 -0
- pymodaq/splash.png +0 -0
- pymodaq/utils/__init__.py +0 -0
- pymodaq/utils/array_manipulation.py +6 -0
- pymodaq/utils/calibration_camera.py +180 -0
- pymodaq/utils/chrono_timer.py +203 -0
- pymodaq/utils/config.py +53 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +158 -0
- pymodaq/utils/data.py +128 -0
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/exceptions.py +38 -0
- pymodaq/utils/gui_utils/__init__.py +10 -0
- pymodaq/utils/gui_utils/loader_utils.py +75 -0
- pymodaq/utils/gui_utils/utils.py +18 -0
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/__init__.py +2 -0
- pymodaq/utils/h5modules/module_saving.py +526 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
- pymodaq/utils/leco/director_utils.py +74 -0
- pymodaq/utils/leco/leco_director.py +166 -0
- pymodaq/utils/leco/pymodaq_listener.py +364 -0
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +74 -0
- pymodaq/utils/logger.py +6 -0
- pymodaq/utils/managers/__init__.py +0 -0
- pymodaq/utils/managers/batchscan_manager.py +346 -0
- pymodaq/utils/managers/modules_manager.py +589 -0
- pymodaq/utils/managers/overshoot_manager.py +242 -0
- pymodaq/utils/managers/preset_manager.py +229 -0
- pymodaq/utils/managers/preset_manager_utils.py +262 -0
- pymodaq/utils/managers/remote_manager.py +484 -0
- pymodaq/utils/math_utils.py +6 -0
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +10 -0
- pymodaq/utils/parameter/utils.py +6 -0
- pymodaq/utils/scanner/__init__.py +5 -0
- pymodaq/utils/scanner/scan_config.py +16 -0
- pymodaq/utils/scanner/scan_factory.py +259 -0
- pymodaq/utils/scanner/scan_selector.py +477 -0
- pymodaq/utils/scanner/scanner.py +324 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
- pymodaq/utils/scanner/scanners/__init__.py +1 -0
- pymodaq/utils/scanner/scanners/sequential.py +224 -0
- pymodaq/utils/scanner/scanners/tabular.py +319 -0
- pymodaq/utils/scanner/utils.py +110 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +50 -0
- pymodaq/utils/tcp_ip/__init__.py +6 -0
- pymodaq/utils/tcp_ip/mysocket.py +12 -0
- pymodaq/utils/tcp_ip/serializer.py +13 -0
- pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
- pymodaq-5.1.6.dist-info/METADATA +238 -0
- pymodaq-5.1.6.dist-info/RECORD +154 -0
- pymodaq-5.1.6.dist-info/WHEEL +4 -0
- pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
- 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,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
|