pymodaq 5.0.0__py3-none-any.whl → 5.0.2__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 +55 -89
- pymodaq/control_modules/daq_move.py +129 -55
- pymodaq/control_modules/daq_move_ui.py +42 -11
- pymodaq/control_modules/daq_viewer.py +32 -13
- pymodaq/control_modules/move_utility_classes.py +346 -79
- pymodaq/control_modules/utils.py +26 -9
- pymodaq/control_modules/viewer_utility_classes.py +51 -14
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +532 -263
- pymodaq/examples/qt_less_standalone_module.py +128 -0
- pymodaq/extensions/bayesian/bayesian_optimisation.py +30 -21
- pymodaq/extensions/bayesian/utils.py +6 -3
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/daq_logger.py +4 -5
- pymodaq/extensions/daq_scan.py +28 -8
- pymodaq/extensions/daq_scan_ui.py +7 -9
- pymodaq/extensions/pid/__init__.py +0 -1
- pymodaq/extensions/pid/actuator_controller.py +13 -0
- pymodaq/extensions/pid/daq_move_PID.py +25 -46
- pymodaq/extensions/pid/pid_controller.py +49 -41
- pymodaq/extensions/pid/utils.py +7 -31
- pymodaq/extensions/utils.py +41 -7
- pymodaq/post_treatment/load_and_plot.py +43 -10
- pymodaq/resources/setup_plugin.py +1 -0
- pymodaq/updater.py +107 -0
- pymodaq/utils/chrono_timer.py +6 -7
- pymodaq/utils/daq_utils.py +6 -3
- pymodaq/utils/data.py +21 -17
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/gui_utils/loader_utils.py +29 -2
- pymodaq/utils/gui_utils/utils.py +9 -12
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/module_saving.py +5 -2
- pymodaq/utils/leco/daq_move_LECODirector.py +22 -16
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +15 -9
- pymodaq/utils/leco/leco_director.py +4 -3
- pymodaq/utils/leco/pymodaq_listener.py +9 -13
- pymodaq/utils/leco/utils.py +40 -7
- pymodaq/utils/managers/modules_manager.py +22 -12
- pymodaq/utils/managers/overshoot_manager.py +45 -1
- pymodaq/utils/managers/preset_manager.py +22 -46
- pymodaq/utils/managers/preset_manager_utils.py +17 -13
- pymodaq/utils/managers/remote_manager.py +1 -1
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +5 -1
- pymodaq/utils/tcp_ip/mysocket.py +4 -110
- pymodaq/utils/tcp_ip/serializer.py +4 -769
- pymodaq/utils/tcp_ip/tcp_server_client.py +5 -5
- pymodaq-5.0.2.dist-info/METADATA +242 -0
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/RECORD +54 -55
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/WHEEL +1 -1
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/entry_points.txt +1 -0
- pymodaq/examples/custom_app.py +0 -255
- pymodaq/examples/custom_viewer.py +0 -112
- pymodaq/examples/parameter_ex.py +0 -158
- pymodaq/examples/preset_MockCamera.xml +0 -1
- pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py +0 -142
- pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui +0 -232
- pymodaq/post_treatment/daq_measurement/daq_measurement_main.py +0 -391
- pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -2
- pymodaq-5.0.0.dist-info/METADATA +0 -166
- /pymodaq/{post_treatment/daq_measurement → daq_utils}/__init__.py +0 -0
- {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from functools import partial # needed for the button to sync setpoint with currpoint
|
|
3
|
+
from typing import Dict, List, TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
import numpy as np
|
|
4
6
|
|
|
5
7
|
from qtpy import QtGui, QtWidgets
|
|
@@ -9,13 +11,14 @@ from simple_pid import PID
|
|
|
9
11
|
|
|
10
12
|
from pymodaq_utils.logger import set_logger, get_module_name
|
|
11
13
|
from pymodaq_utils.utils import ThreadCommand, find_dict_in_list_from_key_val
|
|
14
|
+
from pymodaq.utils.exceptions import DetectorError, ActuatorError, PIDError
|
|
12
15
|
|
|
13
16
|
from pymodaq_gui.parameter import utils as putils
|
|
14
17
|
from pymodaq_gui.parameter import Parameter, ParameterTree
|
|
15
18
|
from pymodaq_gui.plotting.data_viewers.viewer0D import Viewer0D
|
|
16
19
|
from pymodaq_gui.utils.widgets import QLED, LabelWithFont, SpinBox
|
|
17
20
|
from pymodaq_gui.utils.dock import DockArea, Dock
|
|
18
|
-
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
from pymodaq_data.data import DataToExport, DataCalculated, DataRaw
|
|
21
24
|
from pymodaq_utils.config import Config
|
|
@@ -23,6 +26,11 @@ from pymodaq_utils.config import Config
|
|
|
23
26
|
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
24
27
|
from pymodaq.extensions.pid.utils import get_models
|
|
25
28
|
from pymodaq.utils.data import DataActuator, DataToActuators
|
|
29
|
+
from pymodaq.extensions.pid.actuator_controller import PIDController
|
|
30
|
+
from pymodaq.extensions.utils import CustomExt
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from pymodaq.control_modules.daq_move import DAQ_Move
|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
config = Config()
|
|
@@ -38,7 +46,7 @@ def convert_output_limits(lim_min=-10., min_status=False, lim_max=10., max_statu
|
|
|
38
46
|
return output
|
|
39
47
|
|
|
40
48
|
|
|
41
|
-
class DAQ_PID(
|
|
49
|
+
class DAQ_PID(CustomExt):
|
|
42
50
|
"""
|
|
43
51
|
"""
|
|
44
52
|
command_pid = Signal(ThreadCommand)
|
|
@@ -158,7 +166,7 @@ class DAQ_PID(CustomApp):
|
|
|
158
166
|
def process_output(self, data: DataToExport):
|
|
159
167
|
inputs: DataRaw = data.get_data_from_name('inputs')
|
|
160
168
|
outputs: DataRaw = data.get_data_from_name('outputs')
|
|
161
|
-
self.curr_points = [float(
|
|
169
|
+
self.curr_points = [float(array[0]) for array in inputs]
|
|
162
170
|
self.output_viewer.show_data(outputs)
|
|
163
171
|
self.input_viewer.show_data(inputs)
|
|
164
172
|
|
|
@@ -170,7 +178,7 @@ class DAQ_PID(CustomApp):
|
|
|
170
178
|
self.set_action_enabled('run', enable)
|
|
171
179
|
self.set_action_enabled('pause', enable)
|
|
172
180
|
|
|
173
|
-
def setup_menu(self):
|
|
181
|
+
def setup_menu(self, menubar: QtWidgets.QMenuBar = None):
|
|
174
182
|
'''
|
|
175
183
|
to be subclassed
|
|
176
184
|
create menu for actions contained into the self.actions_manager, for instance:
|
|
@@ -241,6 +249,7 @@ class DAQ_PID(CustomApp):
|
|
|
241
249
|
logger.debug('connecting actions and other')
|
|
242
250
|
self.connect_action('quit', self.quit_fun, )
|
|
243
251
|
self.connect_action('ini_model', self.ini_model)
|
|
252
|
+
self.connect_action('create_setp_actuators', self.create_setp_actuators)
|
|
244
253
|
self.connect_action('ini_pid', self.ini_PID)
|
|
245
254
|
self.connect_action('run', self.run_PID)
|
|
246
255
|
self.connect_action('pause', self.pause_PID)
|
|
@@ -252,12 +261,21 @@ class DAQ_PID(CustomApp):
|
|
|
252
261
|
self.add_widget('model_label', QtWidgets.QLabel, 'Init Model:')
|
|
253
262
|
self.add_action('ini_model', 'Init Model', 'ini', tip='Initialize the selected model: algo/data conversion')
|
|
254
263
|
self.add_widget('model_led', QLED, toolbar=self.toolbar)
|
|
264
|
+
|
|
265
|
+
self.add_action('create_setp_actuators', 'Create SetPoint Actuators', 'Add_Step',
|
|
266
|
+
tip='Create a DAQ_Move Control Module for each SetPoint allowing to'
|
|
267
|
+
'control them from the DashBoard, therefore within other extensions')
|
|
268
|
+
|
|
255
269
|
self.add_widget('model_label', QtWidgets.QLabel, 'Init PID Runner:')
|
|
256
|
-
self.add_action('ini_pid', 'Init the PID loop', 'ini', tip='Init the PID thread',
|
|
270
|
+
self.add_action('ini_pid', 'Init the PID loop', 'ini', tip='Init the PID thread',
|
|
271
|
+
checkable=True)
|
|
257
272
|
self.add_widget('pid_led', QLED, toolbar=self.toolbar)
|
|
258
|
-
self.add_action('run', 'Run The PID loop', 'run2', tip='run or stop the pid loop',
|
|
259
|
-
|
|
273
|
+
self.add_action('run', 'Run The PID loop', 'run2', tip='run or stop the pid loop',
|
|
274
|
+
checkable=True)
|
|
275
|
+
self.add_action('pause', 'Pause the PID loop', 'pause', tip='Pause the PID loop',
|
|
276
|
+
checkable=True)
|
|
260
277
|
self.set_action_checked('pause', True)
|
|
278
|
+
self.set_action_enabled('create_setp_actuators', False)
|
|
261
279
|
logger.debug('actions set')
|
|
262
280
|
|
|
263
281
|
def setup_docks(self):
|
|
@@ -297,6 +315,17 @@ class DAQ_PID(CustomApp):
|
|
|
297
315
|
|
|
298
316
|
self.dock_pid.addWidget(widget)
|
|
299
317
|
|
|
318
|
+
def create_setp_actuators(self):
|
|
319
|
+
# Now that we have the module manager, load PID if it is checked in managers
|
|
320
|
+
try:
|
|
321
|
+
for setp in self.model_class.setpoints_names:
|
|
322
|
+
self.dashboard.add_move_from_extension(setp, 'PID', PIDController(self))
|
|
323
|
+
self.set_action_enabled('create_setp_actuators', False)
|
|
324
|
+
|
|
325
|
+
except Exception as e:
|
|
326
|
+
raise PIDError('Could not load the PID extension and create setpoints actuators'
|
|
327
|
+
f'{str(e)}')
|
|
328
|
+
|
|
300
329
|
def get_set_model_params(self, model_name):
|
|
301
330
|
self.settings.child('models', 'model_params').clearChildren()
|
|
302
331
|
models = get_models()
|
|
@@ -355,6 +384,7 @@ class DAQ_PID(CustomApp):
|
|
|
355
384
|
self.enable_controls_pid(True)
|
|
356
385
|
self.get_action('model_led').set_as_true()
|
|
357
386
|
self.set_action_enabled('ini_model', False)
|
|
387
|
+
self.set_action_enabled('create_setp_actuators', True)
|
|
358
388
|
|
|
359
389
|
except Exception as e:
|
|
360
390
|
logger.exception(str(e))
|
|
@@ -368,10 +398,10 @@ class DAQ_PID(CustomApp):
|
|
|
368
398
|
for ind, sp in enumerate(self.setpoints_sb):
|
|
369
399
|
sp.setValue(values[ind])
|
|
370
400
|
|
|
371
|
-
def setpoints_external(self, values_dict):
|
|
401
|
+
def setpoints_external(self, values_dict: Dict[str, DataActuator]):
|
|
372
402
|
for key in values_dict:
|
|
373
403
|
index = self.model_class.setpoints_names.index(key)
|
|
374
|
-
self.setpoints_sb[index].setValue(values_dict[key])
|
|
404
|
+
self.setpoints_sb[index].setValue(values_dict[key].value())
|
|
375
405
|
|
|
376
406
|
@property
|
|
377
407
|
def curr_points(self):
|
|
@@ -643,38 +673,16 @@ class PIDRunner(QObject):
|
|
|
643
673
|
|
|
644
674
|
|
|
645
675
|
if __name__ == '__main__':
|
|
646
|
-
import
|
|
647
|
-
from
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
win = QtWidgets.QMainWindow()
|
|
658
|
-
area = DockArea()
|
|
659
|
-
win.setCentralWidget(area)
|
|
660
|
-
win.resize(1000, 500)
|
|
661
|
-
win.setWindowTitle('PyMoDAQ Dashboard')
|
|
662
|
-
|
|
663
|
-
dashboard = DashBoard(area)
|
|
664
|
-
pid = None
|
|
665
|
-
file = Path(get_set_preset_path()).joinpath(f"{config('presets', 'default_preset_for_pid')}.xml")
|
|
666
|
-
if file.exists():
|
|
667
|
-
dashboard.set_preset_mode(file)
|
|
668
|
-
pid = dashboard.load_pid_module()
|
|
669
|
-
else:
|
|
670
|
-
msgBox = QtWidgets.QMessageBox()
|
|
671
|
-
msgBox.setText(f"The default file specified in the configuration file does not exists!\n"
|
|
672
|
-
f"{file}\n"
|
|
673
|
-
f"Impossible to load the DAQ_PID Module")
|
|
674
|
-
msgBox.setStandardButtons(msgBox.Ok)
|
|
675
|
-
ret = msgBox.exec()
|
|
676
|
-
|
|
677
|
-
sys.exit(app.exec_())
|
|
676
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
677
|
+
from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
|
|
678
|
+
|
|
679
|
+
app = mkQApp('DAQ_PID')
|
|
680
|
+
preset_file_name = config('presets', f'default_preset_for_pid')
|
|
681
|
+
|
|
682
|
+
dashboard, extension, win = load_dashboard_with_preset(preset_file_name, 'DAQ_PID')
|
|
683
|
+
|
|
684
|
+
app.exec()
|
|
685
|
+
|
|
678
686
|
|
|
679
687
|
|
|
680
688
|
|
pymodaq/extensions/pid/utils.py
CHANGED
|
@@ -27,6 +27,7 @@ DAQ_2DViewer_Det_types = get_plugins('daq_2Dviewer')
|
|
|
27
27
|
DAQ_NDViewer_Det_types = get_plugins('daq_NDviewer')
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
|
|
30
31
|
class DataToActuatorPID(DataToActuators):
|
|
31
32
|
|
|
32
33
|
def __init__(self, *args, **kwargs):
|
|
@@ -136,38 +137,14 @@ class PIDModelGeneric:
|
|
|
136
137
|
|
|
137
138
|
|
|
138
139
|
def main(xmlfile):
|
|
139
|
-
from pymodaq.
|
|
140
|
-
from pymodaq.utils.config import get_set_preset_path
|
|
140
|
+
from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
|
|
141
141
|
from pathlib import Path
|
|
142
142
|
from qtpy import QtWidgets
|
|
143
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
143
144
|
|
|
144
145
|
import sys
|
|
145
|
-
app =
|
|
146
|
-
win =
|
|
147
|
-
area = DockArea()
|
|
148
|
-
win.setCentralWidget(area)
|
|
149
|
-
win.resize(1000, 500)
|
|
150
|
-
win.setWindowTitle('PyMoDAQ Dashboard')
|
|
151
|
-
|
|
152
|
-
dashboard = DashBoard(area)
|
|
153
|
-
file = Path(get_set_preset_path()).joinpath(xmlfile)
|
|
154
|
-
if file.exists():
|
|
155
|
-
dashboard.set_preset_mode(file)
|
|
156
|
-
# prog.load_scan_module()
|
|
157
|
-
pid_area = DockArea()
|
|
158
|
-
pid_window = QtWidgets.QMainWindow()
|
|
159
|
-
pid_window.setCentralWidget(pid_area)
|
|
160
|
-
|
|
161
|
-
prog = dashboard.load_pid_module(pid_window)
|
|
162
|
-
|
|
163
|
-
else:
|
|
164
|
-
msgBox = QtWidgets.QMessageBox()
|
|
165
|
-
msgBox.setText(f"The default file specified in the configuration file does not exists!\n"
|
|
166
|
-
f"{file}\n"
|
|
167
|
-
f"Impossible to load the DAQ_PID Module")
|
|
168
|
-
msgBox.setStandardButtons(msgBox.Ok)
|
|
169
|
-
ret = msgBox.exec()
|
|
170
|
-
|
|
146
|
+
app = mkQApp('BeamSteering')
|
|
147
|
+
dashboard, extension, win = load_dashboard_with_preset(xmlfile, 'DAQ_PID')
|
|
171
148
|
sys.exit(app.exec_())
|
|
172
149
|
|
|
173
150
|
|
|
@@ -179,10 +156,9 @@ def get_models(model_name=None):
|
|
|
179
156
|
-------
|
|
180
157
|
list: list of disct containting the name and python module of the found models
|
|
181
158
|
"""
|
|
182
|
-
from pymodaq.extensions.pid.utils import PIDModelGeneric
|
|
183
159
|
models_import = []
|
|
184
|
-
discovered_models = get_entrypoints(group='pymodaq.pid_models')
|
|
185
|
-
discovered_models
|
|
160
|
+
discovered_models = list(get_entrypoints(group='pymodaq.pid_models'))
|
|
161
|
+
discovered_models.extend(list(get_entrypoints(group='pymodaq.models')))
|
|
186
162
|
if len(discovered_models) > 0:
|
|
187
163
|
for pkg in discovered_models:
|
|
188
164
|
try:
|
pymodaq/extensions/utils.py
CHANGED
|
@@ -8,14 +8,22 @@ import importlib
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import pkgutil
|
|
10
10
|
import warnings
|
|
11
|
+
from typing import Union, TYPE_CHECKING
|
|
11
12
|
|
|
12
|
-
from
|
|
13
|
+
from qtpy import QtCore, QtWidgets
|
|
13
14
|
|
|
15
|
+
from pymodaq_gui.utils.dock import DockArea
|
|
16
|
+
from pymodaq_utils.utils import get_entrypoints
|
|
14
17
|
from pymodaq_utils import logger as logger_module
|
|
18
|
+
from pymodaq_gui.utils.custom_app import CustomApp
|
|
15
19
|
|
|
20
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
16
21
|
|
|
17
22
|
logger = logger_module.set_logger(logger_module.get_module_name(__file__))
|
|
18
23
|
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from pymodaq.dashboard import DashBoard
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
def get_ext_modules(path: Path):
|
|
21
29
|
modules = []
|
|
@@ -47,13 +55,39 @@ def get_extensions():
|
|
|
47
55
|
module = importlib.import_module(pkg.value)
|
|
48
56
|
modules = get_ext_modules(Path(module.__path__[0]).joinpath('extensions'))
|
|
49
57
|
for mod in modules:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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})
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
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)}')
|
|
57
71
|
|
|
58
72
|
return extension_import
|
|
59
73
|
|
|
74
|
+
|
|
75
|
+
class CustomExt(CustomApp):
|
|
76
|
+
|
|
77
|
+
def __init__(self, parent: Union[DockArea, QtWidgets.QWidget], dashboard: 'DashBoard'):
|
|
78
|
+
super().__init__(parent)
|
|
79
|
+
|
|
80
|
+
self.dashboard = dashboard
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def modules_manager(self) -> ModulesManager:
|
|
84
|
+
"""useful tool to interact with DAQ_Moves and DAQ_Viewers
|
|
85
|
+
|
|
86
|
+
Will be available if a DashBoard has been set
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
ModulesManager
|
|
91
|
+
"""
|
|
92
|
+
if self.dashboard is not None:
|
|
93
|
+
return self.dashboard.modules_manager
|
|
@@ -70,7 +70,7 @@ class LoaderPlotter:
|
|
|
70
70
|
def load_data(self, filter_dims: List[Union[DataDim, str]] = None,
|
|
71
71
|
filter_full_names: List[str] = None, remove_navigation: bool = True,
|
|
72
72
|
group_0D=False, average_axis: int=None, average_index: int = 0,
|
|
73
|
-
last_step=False):
|
|
73
|
+
last_step=False, separate_average=False):
|
|
74
74
|
"""Load Data from the h5 node of the dataloader and apply some filtering/manipulation before
|
|
75
75
|
plotting
|
|
76
76
|
|
|
@@ -92,6 +92,8 @@ class LoaderPlotter:
|
|
|
92
92
|
which step in the averaging process are we in.
|
|
93
93
|
last_step: bool
|
|
94
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
|
|
95
97
|
|
|
96
98
|
Returns
|
|
97
99
|
-------
|
|
@@ -102,7 +104,8 @@ class LoaderPlotter:
|
|
|
102
104
|
self.dataloader.load_all('/', self._data)
|
|
103
105
|
|
|
104
106
|
if average_axis is not None:
|
|
105
|
-
self.average_axis(average_axis, average_index, last_step=last_step
|
|
107
|
+
self.average_axis(average_axis, average_index, last_step=last_step,
|
|
108
|
+
separate_average=separate_average)
|
|
106
109
|
|
|
107
110
|
if filter_dims is not None:
|
|
108
111
|
filter_dims[:] = [enum_checker(DataDim, dim) for dim in filter_dims]
|
|
@@ -113,14 +116,15 @@ class LoaderPlotter:
|
|
|
113
116
|
filter_full_names]
|
|
114
117
|
|
|
115
118
|
if group_0D: # 0D initial data
|
|
116
|
-
self.group_0D_data()
|
|
119
|
+
self.group_0D_data(separate_average=separate_average)
|
|
117
120
|
|
|
118
121
|
if remove_navigation:
|
|
119
122
|
self.remove_navigation_axes()
|
|
120
123
|
|
|
121
124
|
return self._data
|
|
122
125
|
|
|
123
|
-
def average_axis(self, average_axis, average_index, last_step=False
|
|
126
|
+
def average_axis(self, average_axis, average_index, last_step=False,
|
|
127
|
+
separate_average=False) -> None:
|
|
124
128
|
""" Average the data along their average axis
|
|
125
129
|
|
|
126
130
|
Parameters
|
|
@@ -132,7 +136,12 @@ class LoaderPlotter:
|
|
|
132
136
|
which step in the averaging process are we in.
|
|
133
137
|
last_step: bool
|
|
134
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
|
+
|
|
135
142
|
"""
|
|
143
|
+
if separate_average and average_index > 0:
|
|
144
|
+
averaged_data = DataToExport('Averaged')
|
|
136
145
|
for ind, data in enumerate(self._data):
|
|
137
146
|
current_data = data.inav[average_index, ...]
|
|
138
147
|
if average_index > 0:
|
|
@@ -143,10 +152,16 @@ class LoaderPlotter:
|
|
|
143
152
|
data_to_append = data.inav[0, ...]
|
|
144
153
|
else:
|
|
145
154
|
data_to_append = data.inav[0:average_index, ...].mean(axis=average_axis)
|
|
146
|
-
|
|
155
|
+
data_to_append.name = f'{data_to_append.name}_averaged'
|
|
147
156
|
data_to_append.labels = [f'{label}_averaged' for label in data_to_append.labels]
|
|
148
|
-
|
|
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)
|
|
149
161
|
self._data[ind] = current_data
|
|
162
|
+
if separate_average and average_index > 0:
|
|
163
|
+
self._data.append(averaged_data.data)
|
|
164
|
+
|
|
150
165
|
|
|
151
166
|
def remove_navigation_axes(self):
|
|
152
167
|
"""Make the navigation axes as signal axes
|
|
@@ -159,18 +174,27 @@ class LoaderPlotter:
|
|
|
159
174
|
data.transpose() # because usual ND data should be plotted here as 2D with the nav axes as the minor
|
|
160
175
|
# (horizontal)
|
|
161
176
|
|
|
162
|
-
def group_0D_data(self):
|
|
177
|
+
def group_0D_data(self, separate_average=False):
|
|
163
178
|
"""Group in a single DataFromPlugins all data that are initialy Data0D
|
|
164
179
|
|
|
165
180
|
"""
|
|
166
181
|
data = self._data.get_data_from_sig_axes(0)
|
|
167
182
|
if len(data) > 0:
|
|
168
183
|
data0D_arrays = []
|
|
184
|
+
data0D_arrays_averaged = []
|
|
169
185
|
labels = []
|
|
186
|
+
labels_averaged = []
|
|
170
187
|
for dwa in data:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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)
|
|
174
198
|
|
|
175
199
|
data0D = DataFromPlugins(self.grouped_data0D_fullname.split('/')[1],
|
|
176
200
|
data=data0D_arrays, labels=labels,
|
|
@@ -179,6 +203,15 @@ class LoaderPlotter:
|
|
|
179
203
|
axes=dwa.axes, nav_indexes=dwa.nav_indexes,
|
|
180
204
|
)
|
|
181
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)
|
|
182
215
|
|
|
183
216
|
def load_plot_data(self, **kwargs):
|
|
184
217
|
"""Load and plot all data from the current H5Saver
|
|
@@ -23,6 +23,7 @@ def setup(path: Path):
|
|
|
23
23
|
name=PLUGIN_NAME,
|
|
24
24
|
description=config['plugin-info']['description'],
|
|
25
25
|
long_description=long_description,
|
|
26
|
+
long_description_content_type='text/x-rst',
|
|
26
27
|
license=config['plugin-info']['license'],
|
|
27
28
|
url=config['plugin-info']['package-url'],
|
|
28
29
|
author=config['plugin-info']['author'],
|
pymodaq/updater.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
10
|
+
|
|
11
|
+
logger = set_logger(get_module_name(__file__))
|
|
12
|
+
logger.addHandler(logging.StreamHandler(sys.stdout))
|
|
13
|
+
|
|
14
|
+
def wait_for_parent():
|
|
15
|
+
'''
|
|
16
|
+
A function to wait for its parent to terminate execution.
|
|
17
|
+
|
|
18
|
+
In order to achieve that, this process has to be started with
|
|
19
|
+
stdin replaced by a piped stream from its parent. When the
|
|
20
|
+
parent terminates, stdin will close and either return from read
|
|
21
|
+
or throw an exception. De facto creating a way to wait for its
|
|
22
|
+
parent's termination.
|
|
23
|
+
|
|
24
|
+
It then sleep for 2 seconds, to let the parent process complete
|
|
25
|
+
termination.
|
|
26
|
+
|
|
27
|
+
CAUTION: If the process was not started by piping stdin AND
|
|
28
|
+
the --wait option is set, this function will hang forever.
|
|
29
|
+
|
|
30
|
+
We could use `psutil` or a similar lib to check for parent's process
|
|
31
|
+
existance with its pid.
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
logger.info("Waiting for parent process to stop.")
|
|
35
|
+
try:
|
|
36
|
+
sys.stdin.read()
|
|
37
|
+
except:
|
|
38
|
+
pass
|
|
39
|
+
logger.debug("Parent process closed stdin")
|
|
40
|
+
time.sleep(2)
|
|
41
|
+
logger.info("Parent process stopped.")
|
|
42
|
+
|
|
43
|
+
def process_args():
|
|
44
|
+
'''
|
|
45
|
+
Declare arguments for updater.py, parse them and returns them in an object.
|
|
46
|
+
The arguments are:
|
|
47
|
+
--file <file> to request a python program to (re)start after update if needed (optional)
|
|
48
|
+
--wait to wait for the starting process to terminate before updating (optional, defaults to False)
|
|
49
|
+
packages the package list to install/update (they should contain the version in a pip accepted format)
|
|
50
|
+
'''
|
|
51
|
+
parser = argparse.ArgumentParser(description='Update pymodaq using pip.')
|
|
52
|
+
parser.add_argument('--file', type=str, help='the pymodaq script to restart after update')
|
|
53
|
+
parser.add_argument("--wait", action="store_true", help="enable waiting for pymodaq to finish mode (default is disabled).")
|
|
54
|
+
parser.add_argument('packages', type=str, nargs='+', help='package list')
|
|
55
|
+
return parser.parse_args()
|
|
56
|
+
|
|
57
|
+
def restart_if_command_launch(args):
|
|
58
|
+
'''
|
|
59
|
+
Try to detect if this process if launched using the declared command (i.e. `pymodaq_updater`)
|
|
60
|
+
or using the script file (`updater.py`). If it uses the command, it restart the process to
|
|
61
|
+
force it to use the script file, thus preventing a locked file during update on windows systems.
|
|
62
|
+
'''
|
|
63
|
+
python_file_path = Path(__file__) # Should be the path to `updater.py`
|
|
64
|
+
started_path = Path(sys.argv[0]) # Either `updater.py` or `pymodaq_updater`
|
|
65
|
+
|
|
66
|
+
# If they're different we'll restart using the script file
|
|
67
|
+
if started_path.absolute() != python_file_path.absolute():
|
|
68
|
+
logger.info("Started as pymodaq_updater, need to restart using python to prevent lock.")
|
|
69
|
+
# We HAVE to wait for this process to stop in the restarted process
|
|
70
|
+
new_args = ['--wait'] + sys.argv[1:]
|
|
71
|
+
if args.wait:
|
|
72
|
+
wait_for_parent()
|
|
73
|
+
|
|
74
|
+
subprocess.Popen([sys.executable, str(python_file_path.absolute())] + new_args, stdin=subprocess.PIPE)
|
|
75
|
+
sys.exit(0)
|
|
76
|
+
|
|
77
|
+
def main():
|
|
78
|
+
args = process_args()
|
|
79
|
+
logger.info(f"Arguments processed: {args}")
|
|
80
|
+
|
|
81
|
+
restart_if_command_launch(args)
|
|
82
|
+
|
|
83
|
+
if args.wait:
|
|
84
|
+
wait_for_parent()
|
|
85
|
+
|
|
86
|
+
packages_str = ', '.join(args.packages)
|
|
87
|
+
logger.info(f'Updating packages: {packages_str}')
|
|
88
|
+
|
|
89
|
+
with subprocess.Popen([sys.executable, '-m', 'pip', 'install'] + args.packages, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as pip:
|
|
90
|
+
for line in pip.stdout:
|
|
91
|
+
# Can't decode as some characters are not valid and make the whole process fail
|
|
92
|
+
logger.info(line[:-1])
|
|
93
|
+
ret_code = pip.wait()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if ret_code == 0:
|
|
97
|
+
logger.info(f'Succesfully updated {packages_str}')
|
|
98
|
+
else:
|
|
99
|
+
logger.error(f'Error while updating {packages_str}, pip returned {ret_code}')
|
|
100
|
+
|
|
101
|
+
if args.file is not None:
|
|
102
|
+
logger.info(f"Restarting {args.file} script after update.")
|
|
103
|
+
subprocess.Popen([sys.executable, args.file])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
if __name__ == "__main__":
|
|
107
|
+
main()
|
pymodaq/utils/chrono_timer.py
CHANGED
|
@@ -6,8 +6,7 @@ from qtpy import QtGui, QtWidgets
|
|
|
6
6
|
from qtpy.QtCore import Qt, QObject, QTimer
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
from pymodaq_gui.utils import DockArea, Dock
|
|
10
|
-
from pymodaq_gui.QtDesigner_Ressources import QtDesigner_ressources_rc
|
|
9
|
+
from pymodaq_gui.utils.dock import DockArea, Dock
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class PushButtonShortcut(QtWidgets.QPushButton):
|
|
@@ -64,7 +63,7 @@ class ChronoTimer(QObject):
|
|
|
64
63
|
|
|
65
64
|
self.dock_chrono_timer.setAutoFillBackground(True)
|
|
66
65
|
palette = self.dock_chrono_timer.palette()
|
|
67
|
-
palette.setColor(palette.
|
|
66
|
+
palette.setColor(palette.Window, QtGui.QColor(0, 0, 0))
|
|
68
67
|
self.dock_chrono_timer.setPalette(palette)
|
|
69
68
|
|
|
70
69
|
self.time_lcd = QtWidgets.QLCDNumber(8)
|
|
@@ -92,7 +91,7 @@ class ChronoTimer(QObject):
|
|
|
92
91
|
self.controls_layout.addWidget(hor_widget)
|
|
93
92
|
|
|
94
93
|
icon = QtGui.QIcon()
|
|
95
|
-
icon.addPixmap(QtGui.QPixmap("
|
|
94
|
+
icon.addPixmap(QtGui.QPixmap("icons:run2.png"), QtGui.QIcon.Normal,
|
|
96
95
|
QtGui.QIcon.Off)
|
|
97
96
|
self.start_pb = PushButtonShortcut(icon, 'Start',
|
|
98
97
|
shortcut='Home', shortcut_widget=self.area)
|
|
@@ -102,7 +101,7 @@ class ChronoTimer(QObject):
|
|
|
102
101
|
hor_layout.addWidget(self.start_pb)
|
|
103
102
|
|
|
104
103
|
icon = QtGui.QIcon()
|
|
105
|
-
icon.addPixmap(QtGui.QPixmap("
|
|
104
|
+
icon.addPixmap(QtGui.QPixmap("icons:pause.png"), QtGui.QIcon.Normal,
|
|
106
105
|
QtGui.QIcon.Off)
|
|
107
106
|
self.pause_pb = PushButtonShortcut(icon, 'Pause',
|
|
108
107
|
shortcut='Ctrl+p', shortcut_widget=self.area)
|
|
@@ -112,7 +111,7 @@ class ChronoTimer(QObject):
|
|
|
112
111
|
hor_layout.addWidget(self.pause_pb)
|
|
113
112
|
|
|
114
113
|
icon = QtGui.QIcon()
|
|
115
|
-
icon.addPixmap(QtGui.QPixmap("
|
|
114
|
+
icon.addPixmap(QtGui.QPixmap("icons:Refresh2.png"), QtGui.QIcon.Normal,
|
|
116
115
|
QtGui.QIcon.Off)
|
|
117
116
|
self.reset_pb = PushButtonShortcut(icon, 'Reset',
|
|
118
117
|
shortcut='F5', shortcut_widget=self.area)
|
|
@@ -184,7 +183,7 @@ class ChronoTimer(QObject):
|
|
|
184
183
|
# lcd.setPalette(QtGui.QPalette(Qt.red))
|
|
185
184
|
if hasattr(Qt, color):
|
|
186
185
|
palette.setBrush(palette.WindowText, getattr(Qt, color))
|
|
187
|
-
palette.setColor(palette.
|
|
186
|
+
palette.setColor(palette.Window, QtGui.QColor(0, 0, 0))
|
|
188
187
|
lcd.setPalette(palette)
|
|
189
188
|
|
|
190
189
|
|
pymodaq/utils/daq_utils.py
CHANGED
|
@@ -5,9 +5,11 @@ import pkgutil
|
|
|
5
5
|
import platform
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
-
from pymodaq_utils import logger as logger_module
|
|
9
8
|
from pymodaq_utils.config import Config
|
|
10
|
-
from pymodaq_utils.utils import get_entrypoints, ThreadCommand, getLineInfo
|
|
9
|
+
from pymodaq_utils.utils import get_entrypoints, ThreadCommand, getLineInfo, find_keys_from_val, is_64bits, timer # for backcompat
|
|
10
|
+
from pymodaq_utils.logger import set_logger, get_module_name # for backcompat
|
|
11
|
+
|
|
12
|
+
from pymodaq.utils.data import DataFromPlugins # for backcompat
|
|
11
13
|
|
|
12
14
|
from pymodaq.utils.config import get_set_preset_path
|
|
13
15
|
|
|
@@ -18,7 +20,7 @@ else:
|
|
|
18
20
|
from functools import lru_cache as cache
|
|
19
21
|
|
|
20
22
|
|
|
21
|
-
logger =
|
|
23
|
+
logger = set_logger(get_module_name(__file__))
|
|
22
24
|
|
|
23
25
|
config = Config()
|
|
24
26
|
|
|
@@ -122,6 +124,7 @@ def get_instrument_plugins(): # pragma: no cover
|
|
|
122
124
|
except Exception as e:
|
|
123
125
|
logger.debug(f'Impossible to import PID utility plugin: {str(e)}')
|
|
124
126
|
|
|
127
|
+
plugins_import.sort(key=lambda mod: mod['name'])
|
|
125
128
|
return plugins_import
|
|
126
129
|
|
|
127
130
|
|