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.

Files changed (63) hide show
  1. pymodaq/__init__.py +55 -89
  2. pymodaq/control_modules/daq_move.py +129 -55
  3. pymodaq/control_modules/daq_move_ui.py +42 -11
  4. pymodaq/control_modules/daq_viewer.py +32 -13
  5. pymodaq/control_modules/move_utility_classes.py +346 -79
  6. pymodaq/control_modules/utils.py +26 -9
  7. pymodaq/control_modules/viewer_utility_classes.py +51 -14
  8. pymodaq/daq_utils/daq_utils.py +6 -0
  9. pymodaq/dashboard.py +532 -263
  10. pymodaq/examples/qt_less_standalone_module.py +128 -0
  11. pymodaq/extensions/bayesian/bayesian_optimisation.py +30 -21
  12. pymodaq/extensions/bayesian/utils.py +6 -3
  13. pymodaq/extensions/daq_logger/__init__.py +1 -0
  14. pymodaq/extensions/daq_logger/daq_logger.py +4 -5
  15. pymodaq/extensions/daq_scan.py +28 -8
  16. pymodaq/extensions/daq_scan_ui.py +7 -9
  17. pymodaq/extensions/pid/__init__.py +0 -1
  18. pymodaq/extensions/pid/actuator_controller.py +13 -0
  19. pymodaq/extensions/pid/daq_move_PID.py +25 -46
  20. pymodaq/extensions/pid/pid_controller.py +49 -41
  21. pymodaq/extensions/pid/utils.py +7 -31
  22. pymodaq/extensions/utils.py +41 -7
  23. pymodaq/post_treatment/load_and_plot.py +43 -10
  24. pymodaq/resources/setup_plugin.py +1 -0
  25. pymodaq/updater.py +107 -0
  26. pymodaq/utils/chrono_timer.py +6 -7
  27. pymodaq/utils/daq_utils.py +6 -3
  28. pymodaq/utils/data.py +21 -17
  29. pymodaq/utils/enums.py +6 -0
  30. pymodaq/utils/gui_utils/loader_utils.py +29 -2
  31. pymodaq/utils/gui_utils/utils.py +9 -12
  32. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  33. pymodaq/utils/h5modules/module_saving.py +5 -2
  34. pymodaq/utils/leco/daq_move_LECODirector.py +22 -16
  35. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +15 -9
  36. pymodaq/utils/leco/leco_director.py +4 -3
  37. pymodaq/utils/leco/pymodaq_listener.py +9 -13
  38. pymodaq/utils/leco/utils.py +40 -7
  39. pymodaq/utils/managers/modules_manager.py +22 -12
  40. pymodaq/utils/managers/overshoot_manager.py +45 -1
  41. pymodaq/utils/managers/preset_manager.py +22 -46
  42. pymodaq/utils/managers/preset_manager_utils.py +17 -13
  43. pymodaq/utils/managers/remote_manager.py +1 -1
  44. pymodaq/utils/messenger.py +6 -0
  45. pymodaq/utils/parameter/__init__.py +5 -1
  46. pymodaq/utils/tcp_ip/mysocket.py +4 -110
  47. pymodaq/utils/tcp_ip/serializer.py +4 -769
  48. pymodaq/utils/tcp_ip/tcp_server_client.py +5 -5
  49. pymodaq-5.0.2.dist-info/METADATA +242 -0
  50. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/RECORD +54 -55
  51. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/WHEEL +1 -1
  52. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/entry_points.txt +1 -0
  53. pymodaq/examples/custom_app.py +0 -255
  54. pymodaq/examples/custom_viewer.py +0 -112
  55. pymodaq/examples/parameter_ex.py +0 -158
  56. pymodaq/examples/preset_MockCamera.xml +0 -1
  57. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py +0 -142
  58. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui +0 -232
  59. pymodaq/post_treatment/daq_measurement/daq_measurement_main.py +0 -391
  60. pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -2
  61. pymodaq-5.0.0.dist-info/METADATA +0 -166
  62. /pymodaq/{post_treatment/daq_measurement → daq_utils}/__init__.py +0 -0
  63. {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
- from pymodaq_gui.utils.custom_app import CustomApp
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(CustomApp):
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(d) for d in inputs]
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', checkable=True)
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', checkable=True)
259
- self.add_action('pause', 'Pause the PID loop', 'pause', tip='Pause the PID loop', checkable=True)
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 sys
647
- from pathlib import Path
648
- from pymodaq.utils.daq_utils import get_set_preset_path
649
-
650
- app = QtWidgets.QApplication(sys.argv)
651
- if config['style']['darkstyle']:
652
- import qdarkstyle
653
- app.setStyleSheet(qdarkstyle.load_stylesheet())
654
-
655
- from pymodaq.dashboard import DashBoard
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
 
@@ -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.dashboard import DashBoard
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 = QtWidgets.QApplication(sys.argv)
146
- win = QtWidgets.QMainWindow()
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 = get_entrypoints(group='pymodaq.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:
@@ -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 pymodaq_utils.utils import get_entrypoints
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
- mod_in = importlib.import_module(f'{pkg.value}.extensions.{mod}')
51
- if hasattr(mod_in, 'EXTENSION_NAME'):
52
- extension_import.append({'pkg': pkg.value, 'module': mod, 'name': mod_in.EXTENSION_NAME,
53
- 'class_name': mod_in.CLASS_NAME})
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
- except Exception as e: # pragma: no cover
56
- logger.warning(f'Impossible to import the {pkg.value}.extensions.{mod} extension: {str(e)}')
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) -> None:
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
- current_data.append(data_to_append)
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
- data0D_arrays.extend(dwa.data)
172
- labels.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
173
- self._data.remove(dwa)
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()
@@ -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.Background, QtGui.QColor(0, 0, 0))
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(":/icons/Icon_Library/run2.png"), QtGui.QIcon.Normal,
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(":/icons/Icon_Library/pause.png"), QtGui.QIcon.Normal,
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(":/icons/Icon_Library/Refresh2.png"), QtGui.QIcon.Normal,
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.Background, QtGui.QColor(0, 0, 0))
186
+ palette.setColor(palette.Window, QtGui.QColor(0, 0, 0))
188
187
  lcd.setPalette(palette)
189
188
 
190
189
 
@@ -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 = logger_module.set_logger(logger_module.get_module_name(__file__))
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