pymodaq 5.0.17__py3-none-any.whl → 5.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pymodaq might be problematic. Click here for more details.
- pymodaq/__init__.py +23 -11
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +458 -246
- 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.py → daq_move_ui/ui_base.py} +168 -210
- 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 +113 -101
- pymodaq/control_modules/daq_viewer_ui.py +41 -31
- pymodaq/control_modules/mocks.py +2 -2
- pymodaq/control_modules/move_utility_classes.py +113 -41
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +107 -63
- pymodaq/control_modules/viewer_utility_classes.py +13 -17
- pymodaq/dashboard.py +1294 -625
- pymodaq/examples/qt_less_standalone_module.py +48 -11
- pymodaq/extensions/__init__.py +8 -3
- 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 +1 -1
- 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 +71 -297
- pymodaq/extensions/daq_logger/daq_logger.py +7 -12
- pymodaq/extensions/daq_logger/h5logging.py +1 -1
- pymodaq/extensions/daq_scan.py +30 -55
- 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 +3 -34
- 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/actuator_controller.py +3 -2
- pymodaq/extensions/pid/daq_move_PID.py +107 -30
- pymodaq/extensions/pid/pid_controller.py +613 -287
- pymodaq/extensions/pid/utils.py +8 -5
- pymodaq/extensions/utils.py +17 -2
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -1
- pymodaq/utils/config.py +13 -4
- pymodaq/utils/daq_utils.py +14 -0
- pymodaq/utils/data.py +1 -0
- pymodaq/utils/gui_utils/loader_utils.py +25 -15
- pymodaq/utils/h5modules/module_saving.py +134 -22
- pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
- pymodaq/utils/leco/director_utils.py +32 -16
- pymodaq/utils/leco/leco_director.py +104 -27
- pymodaq/utils/leco/pymodaq_listener.py +186 -97
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +25 -25
- pymodaq/utils/managers/batchscan_manager.py +12 -11
- pymodaq/utils/managers/modules_manager.py +74 -33
- pymodaq/utils/managers/overshoot_manager.py +11 -10
- pymodaq/utils/managers/preset_manager.py +100 -64
- pymodaq/utils/managers/preset_manager_utils.py +163 -107
- pymodaq/utils/managers/remote_manager.py +21 -16
- pymodaq/utils/scanner/scan_factory.py +18 -4
- pymodaq/utils/scanner/scan_selector.py +1 -3
- pymodaq/utils/scanner/scanner.py +35 -6
- pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
- pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
- pymodaq/utils/scanner/scanners/sequential.py +50 -31
- pymodaq/utils/scanner/scanners/tabular.py +45 -28
- {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
- pymodaq-5.1.0.dist-info/RECORD +154 -0
- {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
- pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -685
- pymodaq/utils/leco/desktop.ini +0 -2
- pymodaq-5.0.17.dist-info/RECORD +0 -121
- {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
- {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
pymodaq/extensions/daq_scan.py
CHANGED
|
@@ -16,6 +16,7 @@ from typing import List, Tuple, TYPE_CHECKING
|
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
from qtpy import QtWidgets, QtCore, QtGui
|
|
19
|
+
from qtpy.QtWidgets import QDialogButtonBox
|
|
19
20
|
from qtpy.QtCore import QObject, Slot, QThread, Signal, QDateTime, QDate, QTime
|
|
20
21
|
|
|
21
22
|
from pymodaq_utils.logger import set_logger, get_module_name
|
|
@@ -41,12 +42,15 @@ from pymodaq.extensions.daq_scan_ui import DAQScanUI
|
|
|
41
42
|
from pymodaq.utils.h5modules import module_saving
|
|
42
43
|
from pymodaq.utils.scanner.scan_selector import ScanSelector, SelectorItem
|
|
43
44
|
from pymodaq.utils.data import DataActuator
|
|
45
|
+
from pymodaq.utils.config import Config as ControlModulesConfig
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
if TYPE_CHECKING:
|
|
47
49
|
from pymodaq.dashboard import DashBoard
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
config_utils = Config()
|
|
52
|
+
config = ControlModulesConfig()
|
|
53
|
+
|
|
50
54
|
|
|
51
55
|
logger = set_logger(get_module_name(__file__))
|
|
52
56
|
|
|
@@ -146,7 +150,7 @@ class DAQScan(QObject, ParameterManager):
|
|
|
146
150
|
self.curvilinear_values = []
|
|
147
151
|
self.plot_colors = utils.plot_colors
|
|
148
152
|
|
|
149
|
-
self.
|
|
153
|
+
self.runner_thread: QThread = None
|
|
150
154
|
self._h5saver: H5Saver = None
|
|
151
155
|
self._module_and_data_saver: module_saving.ScanSaver = None
|
|
152
156
|
|
|
@@ -297,14 +301,14 @@ class DAQScan(QObject, ParameterManager):
|
|
|
297
301
|
# params about dataset attributes and scan attibutes
|
|
298
302
|
date = QDateTime(QDate.currentDate(), QTime.currentTime())
|
|
299
303
|
params_dataset = [{'title': 'Dataset information', 'name': 'dataset_info', 'type': 'group', 'children': [
|
|
300
|
-
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value':
|
|
304
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config_utils['user']['name']},
|
|
301
305
|
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
302
306
|
{'title': 'Sample:', 'name': 'sample', 'type': 'str', 'value': ''},
|
|
303
307
|
{'title': 'Experiment type:', 'name': 'experiment_type', 'type': 'str', 'value': ''},
|
|
304
308
|
{'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''}]}]
|
|
305
309
|
|
|
306
310
|
params_scan = [{'title': 'Scan information', 'name': 'scan_info', 'type': 'group', 'children': [
|
|
307
|
-
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value':
|
|
311
|
+
{'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config_utils['user']['name']},
|
|
308
312
|
{'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
|
|
309
313
|
{'title': 'Scan type:', 'name': 'scan_type', 'type': 'str', 'value': ''},
|
|
310
314
|
{'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'str', 'value': ''},
|
|
@@ -380,9 +384,9 @@ class DAQScan(QObject, ParameterManager):
|
|
|
380
384
|
|
|
381
385
|
vlayout.addWidget(tree)
|
|
382
386
|
dialog.setLayout(vlayout)
|
|
383
|
-
buttonBox =
|
|
384
|
-
buttonBox.addButton(
|
|
385
|
-
buttonBox.addButton(
|
|
387
|
+
buttonBox = QDialogButtonBox(parent=dialog)
|
|
388
|
+
buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
|
|
389
|
+
buttonBox.addButton("Apply", QDialogButtonBox.ButtonRole.AcceptRole)
|
|
386
390
|
buttonBox.rejected.connect(dialog.reject)
|
|
387
391
|
buttonBox.accepted.connect(dialog.accept)
|
|
388
392
|
|
|
@@ -521,7 +525,7 @@ class DAQScan(QObject, ParameterManager):
|
|
|
521
525
|
@property
|
|
522
526
|
def h5saver(self):
|
|
523
527
|
if self._h5saver is None:
|
|
524
|
-
self._h5saver = H5Saver(backend=
|
|
528
|
+
self._h5saver = H5Saver(backend=config_utils('general', 'hdf5_backend'))
|
|
525
529
|
if self._h5saver.h5_file is None:
|
|
526
530
|
self._h5saver.init_file(update_h5=True)
|
|
527
531
|
if not self._h5saver.isopen():
|
|
@@ -600,7 +604,7 @@ class DAQScan(QObject, ParameterManager):
|
|
|
600
604
|
actuators = self.modules_manager.actuators
|
|
601
605
|
dte = data_mod.DataToExport(name="move_at")
|
|
602
606
|
for ind, pos in enumerate(positions):
|
|
603
|
-
dte.append(DataActuator(actuators[ind].title, data=float(pos)))
|
|
607
|
+
dte.append(DataActuator(actuators[ind].title, data=float(pos), units=actuators[ind].units))
|
|
604
608
|
|
|
605
609
|
self.modules_manager.move_actuators(dte, polling=False)
|
|
606
610
|
|
|
@@ -880,6 +884,13 @@ class DAQScan(QObject, ParameterManager):
|
|
|
880
884
|
self._metada_dataset_set = True
|
|
881
885
|
return res
|
|
882
886
|
|
|
887
|
+
def exit_runner_thread(self, duration : int = 5000):
|
|
888
|
+
self.runner_thread.quit()
|
|
889
|
+
terminated = self.runner_thread.wait(duration)
|
|
890
|
+
if not terminated:
|
|
891
|
+
self.runner_thread.terminate()
|
|
892
|
+
self.runner_thread.wait()
|
|
893
|
+
|
|
883
894
|
def start_scan(self):
|
|
884
895
|
"""
|
|
885
896
|
Start an acquisition calling the set_scan function.
|
|
@@ -919,27 +930,24 @@ class DAQScan(QObject, ParameterManager):
|
|
|
919
930
|
self.module_and_data_saver.h5saver = self.h5saver # force the update as the h5saver ill also be set on each detectors
|
|
920
931
|
|
|
921
932
|
# mandatory to deal with multithreads
|
|
922
|
-
if self.
|
|
933
|
+
if self.runner_thread is not None:
|
|
923
934
|
self.command_daq_signal.disconnect()
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
while not self.scan_thread.isFinished():
|
|
927
|
-
QThread.msleep(100)
|
|
928
|
-
self.scan_thread = None
|
|
935
|
+
self.exit_runner_thread()
|
|
936
|
+
self.runner_thread = None
|
|
929
937
|
|
|
930
|
-
self.
|
|
938
|
+
self.runner_thread = QThread()
|
|
931
939
|
|
|
932
940
|
scan_acquisition = DAQScanAcquisition(self.settings, self.scanner, self.modules_manager,
|
|
933
941
|
)
|
|
934
942
|
|
|
935
943
|
if config['scan']['scan_in_thread']:
|
|
936
|
-
scan_acquisition.moveToThread(self.
|
|
944
|
+
scan_acquisition.moveToThread(self.runner_thread)
|
|
937
945
|
self.command_daq_signal[utils.ThreadCommand].connect(scan_acquisition.queue_command)
|
|
938
946
|
scan_acquisition.scan_data_tmp[ScanDataTemp].connect(self.save_temp_live_data)
|
|
939
947
|
scan_acquisition.status_sig[utils.ThreadCommand].connect(self.thread_status)
|
|
940
948
|
|
|
941
|
-
self.
|
|
942
|
-
self.
|
|
949
|
+
self.runner_thread.scan_acquisition = scan_acquisition
|
|
950
|
+
self.runner_thread.start()
|
|
943
951
|
|
|
944
952
|
self.ui.set_action_enabled('ini_positions', False)
|
|
945
953
|
self.ui.set_action_enabled('start', False)
|
|
@@ -992,7 +1000,8 @@ class DAQScan(QObject, ParameterManager):
|
|
|
992
1000
|
self.ui.set_permanent_status('Stoping acquisition')
|
|
993
1001
|
self.command_daq_signal.emit(utils.ThreadCommand("stop_acquisition"))
|
|
994
1002
|
scan_node = self.module_and_data_saver.get_last_node()
|
|
995
|
-
scan_node
|
|
1003
|
+
if scan_node is not None:
|
|
1004
|
+
scan_node.attrs['scan_done'] = True
|
|
996
1005
|
|
|
997
1006
|
if not self.dashboard.overshoot:
|
|
998
1007
|
self.set_ini_positions() # do not set ini position again in case overshoot fired
|
|
@@ -1095,7 +1104,6 @@ class DAQScanAcquisition(QObject):
|
|
|
1095
1104
|
|
|
1096
1105
|
def start_acquisition(self):
|
|
1097
1106
|
try:
|
|
1098
|
-
#todo hoaw to apply newlayout to adaptive mode? => cannot has to be a new extension
|
|
1099
1107
|
|
|
1100
1108
|
self.modules_manager.connect_actuators()
|
|
1101
1109
|
self.modules_manager.connect_detectors()
|
|
@@ -1120,20 +1128,6 @@ class DAQScanAcquisition(QObject):
|
|
|
1120
1128
|
positions = self.scanner.positions_at(self.ind_scan) # get positions
|
|
1121
1129
|
else:
|
|
1122
1130
|
pass
|
|
1123
|
-
#todo update for v4
|
|
1124
|
-
# positions = learner.ask(1)[0][-1] # next point to probe
|
|
1125
|
-
# if self.scanner.scan_type == 'Tabular': # translate normalized curvilinear position to real coordinates
|
|
1126
|
-
# self.curvilinear = positions
|
|
1127
|
-
# length = 0.
|
|
1128
|
-
# for v in self.scanner.vectors:
|
|
1129
|
-
# length += v.norm()
|
|
1130
|
-
# if length >= self.curvilinear:
|
|
1131
|
-
# vec = v
|
|
1132
|
-
# frac_curvilinear = (self.curvilinear - (length - v.norm())) / v.norm()
|
|
1133
|
-
# break
|
|
1134
|
-
#
|
|
1135
|
-
# position = (vec.vectorize() * frac_curvilinear).translate_to(vec.p1()).p2()
|
|
1136
|
-
# positions = [position.x(), position.y()]
|
|
1137
1131
|
|
|
1138
1132
|
self.status_sig.emit(
|
|
1139
1133
|
utils.ThreadCommand("Update_scan_index",
|
|
@@ -1148,21 +1142,7 @@ class DAQScanAcquisition(QObject):
|
|
|
1148
1142
|
QThread.msleep(self.scan_settings['time_flow', 'wait_time_between'])
|
|
1149
1143
|
|
|
1150
1144
|
#grab datas and wait for grab completion
|
|
1151
|
-
self.det_done(self.modules_manager.
|
|
1152
|
-
|
|
1153
|
-
if self.isadaptive:
|
|
1154
|
-
#todo update for v4
|
|
1155
|
-
# det_channel = self.modules_manager.get_selected_probed_data()
|
|
1156
|
-
# det, channel = det_channel[0].split('/')
|
|
1157
|
-
# if self.scanner.scan_type == 'Tabular':
|
|
1158
|
-
# self.curvilinear_array.append(np.array([self.curvilinear]))
|
|
1159
|
-
# new_positions = self.curvilinear
|
|
1160
|
-
# elif self.scanner.scan_type == 'Scan1D':
|
|
1161
|
-
# new_positions = positions[0]
|
|
1162
|
-
# else:
|
|
1163
|
-
# new_positions = positions[:]
|
|
1164
|
-
# learner.tell(new_positions, self.modules_manager.det_done_datas[det]['data0D'][channel]['data'])
|
|
1165
|
-
pass
|
|
1145
|
+
self.det_done(self.modules_manager.grab_data(positions=positions), positions)
|
|
1166
1146
|
|
|
1167
1147
|
# daq_scan wait time
|
|
1168
1148
|
QThread.msleep(self.scan_settings.child('time_flow', 'wait_time').value())
|
|
@@ -1201,11 +1181,6 @@ class DAQScanAcquisition(QObject):
|
|
|
1201
1181
|
utils.ThreadCommand("add_data",
|
|
1202
1182
|
dict(indexes=indexes, distribution=self.scanner.distribution)))
|
|
1203
1183
|
|
|
1204
|
-
#todo related to adaptive (solution lies along the Enlargeable data saver)
|
|
1205
|
-
if self.isadaptive:
|
|
1206
|
-
for ind_ax, nav_axis in enumerate(self.navigation_axes):
|
|
1207
|
-
nav_axis.append(np.array(positions[ind_ax]))
|
|
1208
|
-
|
|
1209
1184
|
self.det_done_flag = True
|
|
1210
1185
|
|
|
1211
1186
|
full_names: list = self.scan_settings['plot_options', 'plot_0d']['selected'][:]
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from pymodaq_utils.utils import ThreadCommand
|
|
5
|
+
from pymodaq_data.data import DataToExport
|
|
6
|
+
from pymodaq_gui.parameter import Parameter
|
|
7
|
+
|
|
8
|
+
from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main
|
|
9
|
+
from pymodaq.utils.data import DataFromPlugins
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pymodaq.extensions.data_mixer import DataMixer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DAQ_0DViewer_DataMixer(DAQ_Viewer_base):
|
|
16
|
+
""" Instrument plugin class for a OD viewer.
|
|
17
|
+
|
|
18
|
+
This object inherits all functionalities to communicate with PyMoDAQ’s DAQ_Viewer module through inheritance via
|
|
19
|
+
DAQ_Viewer_base. It makes a bridge between the DAQ_Viewer module and the Python wrapper of a particular instrument.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
-----------
|
|
23
|
+
controller: object
|
|
24
|
+
The particular object that allow the communication with the hardware, in general a python wrapper around the
|
|
25
|
+
hardware library.
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
params = (comon_parameters+
|
|
29
|
+
[
|
|
30
|
+
{'title': 'Related Detectors', 'name': 'overridden_detectors', 'type': 'list',
|
|
31
|
+
'readonly': True} # mandatory to know what detectors are related to the DataMixer
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
def ini_attributes(self):
|
|
35
|
+
self.controller: DataMixer = None
|
|
36
|
+
|
|
37
|
+
def commit_settings(self, param: Parameter):
|
|
38
|
+
"""Apply the consequences of a change of value in the detector settings
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
param: Parameter
|
|
43
|
+
A given parameter (within detector_settings) whose value has been changed by the user
|
|
44
|
+
"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
def ini_detector(self, controller=None):
|
|
48
|
+
"""Detector communication initialization
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
controller: (object)
|
|
53
|
+
custom object of a PyMoDAQ plugin (Slave case). None if only one actuator/detector by controller
|
|
54
|
+
(Master case)
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
info: str
|
|
59
|
+
initialized: bool
|
|
60
|
+
False if initialization failed otherwise True
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
self.controller: DataMixer = controller
|
|
64
|
+
if self.controller is not None:
|
|
65
|
+
self.controller.dte_computed_signal.connect(self.grab_done)
|
|
66
|
+
|
|
67
|
+
info = "DataMixer Detector Initialized"
|
|
68
|
+
initialized = True
|
|
69
|
+
return info, initialized
|
|
70
|
+
|
|
71
|
+
def close(self):
|
|
72
|
+
"""Terminate the communication protocol"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def grab_done(self, dte: DataToExport):
|
|
76
|
+
self.dte_signal.emit(dte)
|
|
77
|
+
|
|
78
|
+
def grab_data(self, Naverage=1, **kwargs):
|
|
79
|
+
"""Start a grab from the detector
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
Naverage: int
|
|
84
|
+
Number of hardware averaging (if hardware averaging is possible, self.hardware_averaging should be set to
|
|
85
|
+
True in class preamble and you should code this implementation)
|
|
86
|
+
kwargs: dict
|
|
87
|
+
others optionals arguments
|
|
88
|
+
"""
|
|
89
|
+
self.controller.snap()
|
|
90
|
+
|
|
91
|
+
def stop(self):
|
|
92
|
+
"""Stop the current grab hardware wise if necessary"""
|
|
93
|
+
return ''
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == '__main__':
|
|
97
|
+
main(__file__)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from qtpy import QtWidgets, QtCore
|
|
2
|
+
import numpy as np
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from pymodaq_gui import utils as gutils
|
|
8
|
+
from pymodaq_utils.config import Config, ConfigError
|
|
9
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
10
|
+
from pymodaq_utils.utils import find_dict_in_list_from_key_val
|
|
11
|
+
from pymodaq_data.data import DataToExport, DataWithAxes
|
|
12
|
+
|
|
13
|
+
from pymodaq.utils.config import Config as PyMoConfig
|
|
14
|
+
from pymodaq.extensions.utils import CustomExt
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from pymodaq_gui.plotting.data_viewers.viewer import ViewerDispatcher
|
|
18
|
+
from pymodaq_gui.utils.widgets.qled import QLED
|
|
19
|
+
from pymodaq_gui.parameter import utils as putils
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from pymodaq.extensions.data_mixer.model import get_models, DataMixerModel
|
|
23
|
+
from pymodaq.extensions.data_mixer.utils import DataMixerConfig, find_key_in_nested_dict
|
|
24
|
+
|
|
25
|
+
logger = set_logger(get_module_name(__file__))
|
|
26
|
+
|
|
27
|
+
config_utils = Config()
|
|
28
|
+
config_pymodaq = PyMoConfig()
|
|
29
|
+
|
|
30
|
+
EXTENSION_NAME = 'Data Mixer' # the name that will be displayed in the extension list in the
|
|
31
|
+
# dashboard
|
|
32
|
+
CLASS_NAME = 'DataMixer' # this should be the name of your class defined below
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DataMixer(CustomExt):
|
|
36
|
+
settings_name = 'DataMixerSettings'
|
|
37
|
+
models = get_models()
|
|
38
|
+
params = [
|
|
39
|
+
{'title': 'Models', 'name': 'models', 'type': 'group', 'expanded': True, 'visible': True,
|
|
40
|
+
'children': [
|
|
41
|
+
{'title': 'Models class:', 'name': 'model_class', 'type': 'list',
|
|
42
|
+
'limits': [d['name'] for d in models]},
|
|
43
|
+
{'title': 'Ini Model', 'name': 'ini_model', 'type': 'action', },
|
|
44
|
+
{'title': 'Model params:', 'name': 'model_params', 'type': 'group', 'children': []},
|
|
45
|
+
|
|
46
|
+
]}]
|
|
47
|
+
|
|
48
|
+
dte_computed_signal = QtCore.Signal(DataToExport)
|
|
49
|
+
|
|
50
|
+
def __init__(self, parent: gutils.DockArea, dashboard):
|
|
51
|
+
super().__init__(parent, dashboard)
|
|
52
|
+
|
|
53
|
+
self.model_class: Optional[DataMixerModel] = None
|
|
54
|
+
self.datamixer_config = DataMixerConfig()
|
|
55
|
+
self.setup_ui()
|
|
56
|
+
|
|
57
|
+
self.settings.child('models', 'ini_model').sigActivated.connect(
|
|
58
|
+
self.get_action('ini_model').trigger)
|
|
59
|
+
|
|
60
|
+
def get_set_model_params(self, model_name):
|
|
61
|
+
self.settings.child('models', 'model_params').clearChildren()
|
|
62
|
+
if len(self.models) > 0:
|
|
63
|
+
model_class = find_dict_in_list_from_key_val(self.models, 'name', model_name)['class']
|
|
64
|
+
params = getattr(model_class, 'params')
|
|
65
|
+
self.settings.child('models', 'model_params').addChildren(params)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def setup_docks(self):
|
|
69
|
+
"""Mandatory method to be subclassed to setup the docks layout
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
self.docks['settings'] = gutils.Dock('Settings')
|
|
73
|
+
self.dockarea.addDock(self.docks['settings'])
|
|
74
|
+
splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)
|
|
75
|
+
self.docks['settings'].addWidget(splitter)
|
|
76
|
+
splitter.addWidget(self.modules_manager.settings_tree)
|
|
77
|
+
self.modules_manager.tree.header().setVisible(False)
|
|
78
|
+
self.modules_manager.settings.child('modules', 'actuators').hide()
|
|
79
|
+
self.modules_manager.settings.child('move_done').hide()
|
|
80
|
+
self.modules_manager.settings.child('det_done').hide()
|
|
81
|
+
self.modules_manager.settings.child('data_dimensions',
|
|
82
|
+
'det_data_list0D').setOpts(height=150)
|
|
83
|
+
self.modules_manager.settings.child('data_dimensions').hide()
|
|
84
|
+
self.modules_manager.settings.child('actuators_positions').hide()
|
|
85
|
+
|
|
86
|
+
splitter.addWidget(self.settings_tree)
|
|
87
|
+
|
|
88
|
+
self.docks['computed'] = gutils.Dock('Computed data')
|
|
89
|
+
self.dockarea.addDock(self.docks['computed'], 'right')
|
|
90
|
+
|
|
91
|
+
self.area_computed = gutils.DockArea()
|
|
92
|
+
self.docks['computed'].addWidget(self.area_computed)
|
|
93
|
+
|
|
94
|
+
self.dte_computed_viewer = ViewerDispatcher(self.area_computed)
|
|
95
|
+
|
|
96
|
+
if len(self.models) != 0:
|
|
97
|
+
self.get_set_model_params(self.models[0]['name'])
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def config_path(self) -> Path:
|
|
101
|
+
return self.datamixer_config.config_path
|
|
102
|
+
|
|
103
|
+
def validate_config(self) -> bool:
|
|
104
|
+
""" Read eventually saved settings from self.datamixer_config
|
|
105
|
+
|
|
106
|
+
Example
|
|
107
|
+
-------
|
|
108
|
+
utility = find_key_in_nested_dict(self.datamixer_config.to_dict(), 'prediction')
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
def setup_actions(self):
|
|
114
|
+
"""Method where to create actions to be subclassed. Mandatory
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
self.add_action('quit', 'Quit', 'close2', "Quit program")
|
|
118
|
+
combo_model = QtWidgets.QComboBox()
|
|
119
|
+
combo_model.addItems([model['name'] for model in self.models])
|
|
120
|
+
self.add_widget('models', combo_model, tip='List of available models')
|
|
121
|
+
self.add_action('ini_model', 'Init Model', 'ini')
|
|
122
|
+
self.add_widget('model_led', QLED, toolbar=self.toolbar)
|
|
123
|
+
self.add_action('snap', 'Snap Detectors', 'snap',
|
|
124
|
+
'Snap all selected detectors')
|
|
125
|
+
self.add_action('create_computed_detectors', 'Create Computed Detectors', 'Add_Step',
|
|
126
|
+
tip='Create a DAQ_Viewer Control Module')
|
|
127
|
+
|
|
128
|
+
def connect_things(self):
|
|
129
|
+
"""Connect actions and/or other widgets signal to methods"""
|
|
130
|
+
self.connect_action('quit', self.quit_fun)
|
|
131
|
+
self.connect_action('models', self.update_model_settings_from_action, signal_name='currentTextChanged')
|
|
132
|
+
self.connect_action('ini_model', self.ini_model)
|
|
133
|
+
self.modules_manager.det_done_signal.connect(self.process_data)
|
|
134
|
+
self.dte_computed_signal.connect(self.plot_computed_results)
|
|
135
|
+
self.connect_action('snap', self.snap)
|
|
136
|
+
self.modules_manager.detectors_changed.connect(self.update_connect_detectors)
|
|
137
|
+
self.connect_action('create_computed_detectors', self.create_computed_detectors)
|
|
138
|
+
|
|
139
|
+
def update_model_settings_from_action(self, model: str):
|
|
140
|
+
self.settings.child('models', 'model_class').setValue(model)
|
|
141
|
+
|
|
142
|
+
def process_data(self, dte: DataToExport):
|
|
143
|
+
if self.model_class is not None:
|
|
144
|
+
dte_computed = self.model_class.process_dte(dte)
|
|
145
|
+
self.dte_computed_signal.emit(dte_computed)
|
|
146
|
+
|
|
147
|
+
def snap(self):
|
|
148
|
+
self.modules_manager.grab_data(check_do_override=False)
|
|
149
|
+
|
|
150
|
+
def create_computed_detectors(self):
|
|
151
|
+
try:
|
|
152
|
+
self.dashboard.add_det_from_extension('DataMixer', 'DAQ0D', 'DataMixer', self)
|
|
153
|
+
self.dashboard.modules_manager.get_mod_from_name(
|
|
154
|
+
'DataMixer', 'det').settings.child('detector_settings', 'overridden_detectors').setOpts(
|
|
155
|
+
limits=self.modules_manager.selected_detectors_name)
|
|
156
|
+
self.set_action_enabled('create_computed_detectors', False)
|
|
157
|
+
#self.dashboard.override_det_from_extension(self.modules_manager.selected_detectors_name)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.exception(str(e))
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
def update_connect_detectors(self):
|
|
163
|
+
try:
|
|
164
|
+
self.connect_detectors(False)
|
|
165
|
+
except :
|
|
166
|
+
pass
|
|
167
|
+
self.connect_detectors()
|
|
168
|
+
|
|
169
|
+
def connect_detectors(self, connect=True):
|
|
170
|
+
"""Connect detectors to DAQ_Logging do_save_continuous method
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
connect: bool
|
|
175
|
+
If True make the connection else disconnect
|
|
176
|
+
"""
|
|
177
|
+
self.modules_manager.connect_detectors(connect=connect)
|
|
178
|
+
|
|
179
|
+
def plot_computed_results(self, dte):
|
|
180
|
+
self.dte_computed_viewer.show_data(dte)
|
|
181
|
+
|
|
182
|
+
def ini_model(self):
|
|
183
|
+
if self.model_class is None:
|
|
184
|
+
self.set_model()
|
|
185
|
+
|
|
186
|
+
self.get_action('model_led').set_as_true()
|
|
187
|
+
self.set_action_enabled('ini_model', False)
|
|
188
|
+
self.settings.child('models', 'ini_model').setValue(True)
|
|
189
|
+
self.set_action_enabled('models', False)
|
|
190
|
+
self.settings.child('models', 'model_class').setOpts(enabled=False)
|
|
191
|
+
self.modules_manager.settings_tree.setEnabled(False)
|
|
192
|
+
|
|
193
|
+
self.update_connect_detectors()
|
|
194
|
+
|
|
195
|
+
def set_model(self):
|
|
196
|
+
model_name = self.settings['models', 'model_class']
|
|
197
|
+
self.model_class = find_dict_in_list_from_key_val(
|
|
198
|
+
self.models, 'name', model_name)['class'](self)
|
|
199
|
+
self.model_class.ini_model_base()
|
|
200
|
+
|
|
201
|
+
def setup_menu(self, menubar: QtWidgets.QMenuBar = None):
|
|
202
|
+
"""Non mandatory method to be subclassed in order to create a menubar
|
|
203
|
+
|
|
204
|
+
create menu for actions contained into the self._actions, for instance:
|
|
205
|
+
|
|
206
|
+
Examples
|
|
207
|
+
--------
|
|
208
|
+
>>>file_menu = self.mainwindow.menuBar().addMenu('File')
|
|
209
|
+
>>>self.affect_to('load', file_menu)
|
|
210
|
+
>>>self.affect_to('save', file_menu)
|
|
211
|
+
|
|
212
|
+
>>>file_menu.addSeparator()
|
|
213
|
+
>>>self.affect_to('quit', file_menu)
|
|
214
|
+
|
|
215
|
+
See Also
|
|
216
|
+
--------
|
|
217
|
+
pymodaq.utils.managers.action_manager.ActionManager
|
|
218
|
+
"""
|
|
219
|
+
# todo create and populate menu using actions defined above in self.setup_actions
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
def value_changed(self, param):
|
|
223
|
+
""" Actions to perform when one of the param's value in self.settings is changed from the
|
|
224
|
+
user interface
|
|
225
|
+
|
|
226
|
+
For instance:
|
|
227
|
+
if param.name() == 'do_something':
|
|
228
|
+
if param.value():
|
|
229
|
+
print('Do something')
|
|
230
|
+
self.settings.child('main_settings', 'something_done').setValue(False)
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
param: (Parameter) the parameter whose value just changed
|
|
235
|
+
"""
|
|
236
|
+
if param.name() == 'model_class':
|
|
237
|
+
self.get_set_model_params(param.value())
|
|
238
|
+
self.get_action('models').setCurrentText(param.value())
|
|
239
|
+
elif param.name() in putils.iter_children(self.settings.child('models', 'model_params'), []):
|
|
240
|
+
if self.model_class is not None:
|
|
241
|
+
self.model_class.update_settings(param)
|
|
242
|
+
|
|
243
|
+
def quit_fun(self):
|
|
244
|
+
self.mainwindow.close()
|
|
245
|
+
self.dashboard.remove_modules(['DataMixer'])
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def main():
|
|
249
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
250
|
+
from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
|
|
251
|
+
|
|
252
|
+
app = mkQApp('DataMixer')
|
|
253
|
+
|
|
254
|
+
preset_file_name = config_pymodaq('presets', 'default_preset_for_datamixer')
|
|
255
|
+
dashboard, extension, win = load_dashboard_with_preset(preset_file_name, EXTENSION_NAME)
|
|
256
|
+
app.exec()
|
|
257
|
+
|
|
258
|
+
return dashboard, extension, win
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == '__main__':
|
|
262
|
+
main()
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import List, TYPE_CHECKING
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import pkgutil
|
|
6
|
+
import warnings
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Union, List
|
|
9
|
+
|
|
10
|
+
import numpy as np # to be imported within models
|
|
11
|
+
|
|
12
|
+
from pymodaq_utils.utils import find_dict_in_list_from_key_val, get_entrypoints
|
|
13
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
14
|
+
|
|
15
|
+
from pymodaq_data.data import DataToExport
|
|
16
|
+
|
|
17
|
+
from pymodaq_gui.managers.parameter_manager import ParameterManager, Parameter
|
|
18
|
+
from pymodaq_gui.config_saver_loader import ConfigSaverLoader
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
logger = set_logger(get_module_name(__file__))
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pymodaq.extensions.data_mixer import DataMixer
|
|
25
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DataMixerModel:
|
|
29
|
+
|
|
30
|
+
detectors_name: List[str] = []
|
|
31
|
+
params = []
|
|
32
|
+
|
|
33
|
+
def __init__(self, data_mixer: 'DataMixer'):
|
|
34
|
+
self.data_mixer = data_mixer
|
|
35
|
+
self.modules_manager: ModulesManager = data_mixer.modules_manager
|
|
36
|
+
self.settings: Parameter = self.data_mixer.settings.child('models', 'model_params')
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def ini_model_base(self):
|
|
40
|
+
""" Method to add things that should be executed before instantiating the model"""
|
|
41
|
+
self.ini_model()
|
|
42
|
+
|
|
43
|
+
def ini_model(self):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
def update_settings(self, param: Parameter):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
def process_dte(self, measurements: DataToExport) -> DataToExport:
|
|
50
|
+
"""
|
|
51
|
+
Convert the measurements in the units to be fed to the PID (same dimensionality as the setpoint)
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
measurements: DataToExport
|
|
55
|
+
DataToExport object from which the model extract a value of the same units as the setpoint
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
DataToExport: the converted input as 0D DataCalculated stored in a DataToExport
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclasses.dataclass
|
|
65
|
+
class PkgMock:
|
|
66
|
+
value: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_models(model_name=None) -> list[dict[(str, str), (str, type)]]:
|
|
70
|
+
"""
|
|
71
|
+
Get DataMixer Models
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
list: list of dict containing the name and python module of the found models
|
|
76
|
+
|
|
77
|
+
Example
|
|
78
|
+
-------
|
|
79
|
+
model = [{'name': 'MyModel', 'class': DataModel}]
|
|
80
|
+
"""
|
|
81
|
+
models_import = []
|
|
82
|
+
discovered_models = list(get_entrypoints(group='pymodaq.models'))
|
|
83
|
+
discovered_models.append(PkgMock('pymodaq.extensions.data_mixer'))
|
|
84
|
+
if len(discovered_models) > 0:
|
|
85
|
+
for pkg in discovered_models:
|
|
86
|
+
try:
|
|
87
|
+
module = importlib.import_module(pkg.value)
|
|
88
|
+
module_name = pkg.value
|
|
89
|
+
|
|
90
|
+
for mod in pkgutil.iter_modules([str(Path(module.__file__).parent.joinpath('models'))]):
|
|
91
|
+
try:
|
|
92
|
+
model_module = importlib.import_module(f'{module_name}.models.{mod.name}', module)
|
|
93
|
+
classes = inspect.getmembers(model_module, inspect.isclass)
|
|
94
|
+
for name, klass in classes:
|
|
95
|
+
if klass.__base__ is DataMixerModel:
|
|
96
|
+
models_import.append({'name': mod.name, 'module': model_module, 'class': klass})
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
except Exception as e: # pragma: no cover
|
|
100
|
+
logger.warning(str(e))
|
|
101
|
+
|
|
102
|
+
except Exception as e: # pragma: no cover
|
|
103
|
+
logger.warning(f'Impossible to import the {pkg.value} extension: {str(e)}')
|
|
104
|
+
|
|
105
|
+
if model_name is None:
|
|
106
|
+
return models_import
|
|
107
|
+
else:
|
|
108
|
+
return find_dict_in_list_from_key_val(models_import, 'name', model_name)
|
|
File without changes
|