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.

Files changed (92) hide show
  1. pymodaq/__init__.py +23 -11
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +458 -246
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/{daq_move_ui.py → daq_move_ui/ui_base.py} +168 -210
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +113 -101
  13. pymodaq/control_modules/daq_viewer_ui.py +41 -31
  14. pymodaq/control_modules/mocks.py +2 -2
  15. pymodaq/control_modules/move_utility_classes.py +113 -41
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +107 -63
  19. pymodaq/control_modules/viewer_utility_classes.py +13 -17
  20. pymodaq/dashboard.py +1294 -625
  21. pymodaq/examples/qt_less_standalone_module.py +48 -11
  22. pymodaq/extensions/__init__.py +8 -3
  23. pymodaq/extensions/adaptive/__init__.py +2 -0
  24. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  25. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  26. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  27. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  28. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  29. pymodaq/extensions/adaptive/utils.py +123 -0
  30. pymodaq/extensions/bayesian/__init__.py +1 -1
  31. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  32. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  33. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  34. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  35. pymodaq/extensions/bayesian/utils.py +71 -297
  36. pymodaq/extensions/daq_logger/daq_logger.py +7 -12
  37. pymodaq/extensions/daq_logger/h5logging.py +1 -1
  38. pymodaq/extensions/daq_scan.py +30 -55
  39. pymodaq/extensions/data_mixer/__init__.py +0 -0
  40. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  41. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  42. pymodaq/extensions/data_mixer/model.py +108 -0
  43. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  44. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  45. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  46. pymodaq/extensions/data_mixer/parser.py +53 -0
  47. pymodaq/extensions/data_mixer/utils.py +23 -0
  48. pymodaq/extensions/h5browser.py +3 -34
  49. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  50. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  51. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  52. pymodaq/extensions/optimizers_base/utils.py +427 -0
  53. pymodaq/extensions/pid/actuator_controller.py +3 -2
  54. pymodaq/extensions/pid/daq_move_PID.py +107 -30
  55. pymodaq/extensions/pid/pid_controller.py +613 -287
  56. pymodaq/extensions/pid/utils.py +8 -5
  57. pymodaq/extensions/utils.py +17 -2
  58. pymodaq/resources/config_template.toml +57 -0
  59. pymodaq/resources/preset_default.xml +1 -1
  60. pymodaq/utils/config.py +13 -4
  61. pymodaq/utils/daq_utils.py +14 -0
  62. pymodaq/utils/data.py +1 -0
  63. pymodaq/utils/gui_utils/loader_utils.py +25 -15
  64. pymodaq/utils/h5modules/module_saving.py +134 -22
  65. pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
  66. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
  67. pymodaq/utils/leco/director_utils.py +32 -16
  68. pymodaq/utils/leco/leco_director.py +104 -27
  69. pymodaq/utils/leco/pymodaq_listener.py +186 -97
  70. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  71. pymodaq/utils/leco/utils.py +25 -25
  72. pymodaq/utils/managers/batchscan_manager.py +12 -11
  73. pymodaq/utils/managers/modules_manager.py +74 -33
  74. pymodaq/utils/managers/overshoot_manager.py +11 -10
  75. pymodaq/utils/managers/preset_manager.py +100 -64
  76. pymodaq/utils/managers/preset_manager_utils.py +163 -107
  77. pymodaq/utils/managers/remote_manager.py +21 -16
  78. pymodaq/utils/scanner/scan_factory.py +18 -4
  79. pymodaq/utils/scanner/scan_selector.py +1 -3
  80. pymodaq/utils/scanner/scanner.py +35 -6
  81. pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
  82. pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
  83. pymodaq/utils/scanner/scanners/sequential.py +50 -31
  84. pymodaq/utils/scanner/scanners/tabular.py +45 -28
  85. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
  86. pymodaq-5.1.0.dist-info/RECORD +154 -0
  87. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
  88. pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -685
  89. pymodaq/utils/leco/desktop.ini +0 -2
  90. pymodaq-5.0.17.dist-info/RECORD +0 -121
  91. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
  92. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
pymodaq/dashboard.py CHANGED
@@ -8,12 +8,19 @@ import logging
8
8
  from pathlib import Path
9
9
  from importlib import import_module
10
10
  from packaging import version as version_mod
11
- from typing import Tuple, List, Any, TYPE_CHECKING
11
+ from typing import Tuple, Union, List, Any, TYPE_CHECKING, Sequence
12
+ import argparse
12
13
 
13
14
 
14
15
  from qtpy import QtGui, QtWidgets, QtCore
15
- from qtpy.QtCore import Qt, QObject, Slot, QThread, Signal, QSize
16
- from qtpy.QtWidgets import QTableWidget, QTableWidgetItem, QCheckBox, QWidget, QLabel, QDialogButtonBox, QDialog
16
+ from qtpy.QtCore import Qt, QThread, Signal, QSize
17
+ from qtpy.QtWidgets import (
18
+ QTableWidget,
19
+ QTableWidgetItem,
20
+ QLabel,
21
+ QDialogButtonBox,
22
+ QMessageBox,
23
+ )
17
24
  from time import perf_counter
18
25
  import numpy as np
19
26
 
@@ -45,12 +52,17 @@ from pymodaq.utils import config as config_mod_pymodaq
45
52
 
46
53
  from pymodaq.control_modules.daq_move import DAQ_Move
47
54
  from pymodaq.control_modules.daq_viewer import DAQ_Viewer
55
+ from pymodaq.control_modules.daq_move_ui.factory import ActuatorUIFactory
48
56
  from pymodaq_gui.utils.splash import get_splash_sc
49
-
50
57
  from pymodaq import extensions as extmod
58
+ from pymodaq.utils.config import Config as ControlModulesConfig
59
+
51
60
 
52
61
  logger = set_logger(get_module_name(__file__))
53
- config = configmod.Config()
62
+
63
+ config_utils = configmod.Config()
64
+ config = ControlModulesConfig()
65
+
54
66
 
55
67
  get_instrument_plugins()
56
68
  extensions = extmod.get_extensions()
@@ -72,11 +84,13 @@ class ManagerEnums(BaseEnum):
72
84
  overshoot = 2
73
85
  roi = 3
74
86
 
87
+
75
88
  class PymodaqUpdateTableWidget(QTableWidget):
76
- '''
77
- A class to represent PyMoDAQ and its subpackages'
78
- available updates as a table.
79
- '''
89
+ """
90
+ A class to represent PyMoDAQ and its subpackages'
91
+ available updates as a table.
92
+ """
93
+
80
94
  def __init__(self):
81
95
  super().__init__()
82
96
  self._row = 0
@@ -84,7 +98,7 @@ class PymodaqUpdateTableWidget(QTableWidget):
84
98
  def setHorizontalHeaderLabels(self, labels):
85
99
  super().setHorizontalHeaderLabels(labels)
86
100
  self.setColumnCount(len(labels))
87
-
101
+
88
102
  def append_row(self, package, current_version, available_version):
89
103
  # Add labels
90
104
  self.setItem(self._row, 0, QTableWidgetItem(str(package)))
@@ -96,48 +110,97 @@ class PymodaqUpdateTableWidget(QTableWidget):
96
110
  def sizeHint(self):
97
111
  self.resizeColumnsToContents()
98
112
  self.resizeRowsToContents()
99
-
113
+
100
114
  # Compute the size to adapt the window (header + borders + sum of all the elements)
101
- width = self.verticalHeader().width() \
102
- + self.frameWidth() * 2 \
103
- + sum([self.columnWidth(i) for i in range(self.columnCount())])
104
-
105
- height = self.horizontalHeader().height() \
106
- + self.frameWidth() * 2 \
107
- + sum([self.rowHeight(i) for i in range(self.rowCount())])
115
+ width = (
116
+ self.verticalHeader().width()
117
+ + self.frameWidth() * 2
118
+ + sum([self.columnWidth(i) for i in range(self.columnCount())])
119
+ )
120
+
121
+ height = (
122
+ self.horizontalHeader().height()
123
+ + self.frameWidth() * 2
124
+ + sum([self.rowHeight(i) for i in range(self.rowCount())])
125
+ )
108
126
 
109
127
  return QSize(width, height)
110
128
 
129
+
111
130
  class DashBoard(CustomApp):
112
131
  """
113
- Main class initializing a DashBoard interface to display det and move modules and logger """
132
+ Main class initializing a DashBoard interface to display det and move modules and logger"""
133
+
114
134
  status_signal = Signal(str)
115
135
  preset_loaded_signal = Signal(bool)
116
136
  new_preset_created = Signal()
117
137
 
118
- settings_name = 'dashboard_settings'
138
+ settings_name = "dashboard_settings"
119
139
  _splash_sc = None
120
140
 
121
141
  params = [
122
- {'title': 'Log level', 'name': 'log_level', 'type': 'list',
123
- 'value': config('general', 'debug_level'),
124
- 'limits': config('general', 'debug_levels')},
125
-
126
- {'title': 'Loaded presets', 'name': 'loaded_files', 'type': 'group', 'children': [
127
- {'title': 'Preset file', 'name': 'preset_file', 'type': 'str', 'value': '',
128
- 'readonly': True},
129
- {'title': 'Overshoot file', 'name': 'overshoot_file', 'type': 'str', 'value': '',
130
- 'readonly': True},
131
- {'title': 'Layout file', 'name': 'layout_file', 'type': 'str', 'value': '',
132
- 'readonly': True},
133
- {'title': 'ROI file', 'name': 'roi_file', 'type': 'str', 'value': '',
134
- 'readonly': True},
135
- {'title': 'Remote file', 'name': 'remote_file', 'type': 'str', 'value': '',
136
- 'readonly': True},
137
- ]},
138
- {'title': 'Actuators Init.', 'name': 'actuators', 'type': 'group', 'children': []},
139
- {'title': 'Detectors Init.', 'name': 'detectors', 'type': 'group', 'children': []},
140
- ]
142
+ {
143
+ "title": "Log level",
144
+ "name": "log_level",
145
+ "type": "list",
146
+ "value": config_utils("general", "debug_levels")[0],
147
+ "limits": config_utils("general", "debug_levels"),
148
+ },
149
+ {
150
+ "title": "Loaded presets",
151
+ "name": "loaded_files",
152
+ "type": "group",
153
+ "children": [
154
+ {
155
+ "title": "Preset file",
156
+ "name": "preset_file",
157
+ "type": "str",
158
+ "value": "",
159
+ "readonly": True,
160
+ },
161
+ {
162
+ "title": "Overshoot file",
163
+ "name": "overshoot_file",
164
+ "type": "str",
165
+ "value": "",
166
+ "readonly": True,
167
+ },
168
+ {
169
+ "title": "Layout file",
170
+ "name": "layout_file",
171
+ "type": "str",
172
+ "value": "",
173
+ "readonly": True,
174
+ },
175
+ {
176
+ "title": "ROI file",
177
+ "name": "roi_file",
178
+ "type": "str",
179
+ "value": "",
180
+ "readonly": True,
181
+ },
182
+ {
183
+ "title": "Remote file",
184
+ "name": "remote_file",
185
+ "type": "str",
186
+ "value": "",
187
+ "readonly": True,
188
+ },
189
+ ],
190
+ },
191
+ {
192
+ "title": "Actuators Init.",
193
+ "name": "actuators",
194
+ "type": "group",
195
+ "children": [],
196
+ },
197
+ {
198
+ "title": "Detectors Init.",
199
+ "name": "detectors",
200
+ "type": "group",
201
+ "children": [],
202
+ },
203
+ ]
141
204
 
142
205
  def __init__(self, dockarea):
143
206
  """
@@ -146,10 +209,10 @@ class DashBoard(CustomApp):
146
209
  ----------
147
210
  parent: (dockarea) instance of the modified pyqtgraph Dockarea (see daq_utils)
148
211
  """
149
-
212
+
150
213
  super().__init__(dockarea)
151
214
 
152
- logger.info('Initializing Dashboard')
215
+ logger.info("Initializing Dashboard")
153
216
  self.extra_params = []
154
217
  self.preset_path = preset_path
155
218
  self.wait_time = 1000
@@ -164,7 +227,7 @@ class DashBoard(CustomApp):
164
227
 
165
228
  self.dockarea.dock_signal.connect(self.save_layout_state_auto)
166
229
 
167
- self.title = ''
230
+ self.title = ""
168
231
 
169
232
  self.overshoot_manager = None
170
233
  self.preset_manager = None
@@ -183,22 +246,23 @@ class DashBoard(CustomApp):
183
246
  self.actuators_modules = []
184
247
  self.detector_modules = []
185
248
 
249
+ self.compact_actuator_dock: Dock = None
250
+
186
251
  self.setup_ui()
187
252
 
188
253
  self.mainwindow.setVisible(True)
189
254
 
190
- logger.info('Dashboard Initialized')
255
+ logger.info("Dashboard Initialized")
191
256
 
192
- if config('general', 'check_version'):
257
+ if config_utils("general", "check_version"):
193
258
  if self.check_update(show=False):
194
259
  sys.exit(0)
195
260
 
196
- @classmethod
197
261
  @property
198
- def splash_sc(cls) -> QtWidgets.QSplashScreen:
199
- if cls._splash_sc is None:
200
- cls._splash_sc = get_splash_sc()
201
- return cls._splash_sc
262
+ def splash_sc(self) -> QtWidgets.QSplashScreen:
263
+ if not hasattr(self, "_splash_sc") or self._splash_sc is None:
264
+ self._splash_sc = get_splash_sc()
265
+ return self._splash_sc
202
266
 
203
267
  def set_preset_path(self, path):
204
268
  self.preset_path = path
@@ -207,47 +271,176 @@ class DashBoard(CustomApp):
207
271
 
208
272
  def set_extra_preset_params(self, params, param_options=[]):
209
273
  self.extra_params = params
210
- self.preset_manager = PresetManager(path=self.preset_path, extra_params=params,
211
- param_options=param_options)
274
+ self.preset_manager = PresetManager(
275
+ path=self.preset_path, extra_params=params, param_options=param_options
276
+ )
212
277
 
213
278
  def add_status(self, txt):
214
279
  """
215
- Add the QListWisgetItem initialized with txt informations to the User Interface
216
- logger_list and to the save_parameters.logger array.
280
+ Add the QListWisgetItem initialized with txt informations to the User Interface
281
+ logger_list and to the save_parameters.logger array.
217
282
 
218
- =============== =========== ======================
219
- **Parameters** **Type** **Description**
220
- *txt* string the log info to add.
221
- =============== =========== ======================
283
+ =============== =========== ======================
284
+ **Parameters** **Type** **Description**
285
+ *txt* string the log info to add.
286
+ =============== =========== ======================
222
287
  """
223
288
  try:
224
289
  now = datetime.datetime.now()
225
- new_item = QtWidgets.QListWidgetItem(now.strftime('%Y/%m/%d %H:%M:%S') + ": " + txt)
290
+ new_item = QtWidgets.QListWidgetItem(
291
+ now.strftime("%Y/%m/%d %H:%M:%S") + ": " + txt
292
+ )
226
293
  self.logger_list.addItem(new_item)
227
294
 
228
295
  except Exception as e:
229
296
  logger.exception(str(e))
230
297
 
298
+ def remove_detectors(self, detector_modules: List[DAQ_Viewer] = None):
299
+ """
300
+ Remove the given list of detectors from the dashboard.
301
+ Parameters
302
+ ----------
303
+ detector_modules: List[DAQ_Viewer]
304
+ List of DAQ_Viewer instances to be removed.
305
+ """
306
+ if detector_modules is None:
307
+ detector_modules = []
308
+ try:
309
+ for detector_module in detector_modules:
310
+ if detector_module in self.detector_modules:
311
+ self.detector_modules.remove(detector_module)
312
+ detector_module.quit_fun()
313
+ dock = self.dockarea.docks.get(
314
+ f"{detector_module.title} settings", None
315
+ )
316
+ if dock:
317
+ dock.close()
318
+ dock = self.dockarea.docks.get(f"{detector_module.title} viewer", None)
319
+ if dock:
320
+ dock.close()
321
+ self.update_module_manager()
322
+ except Exception as e:
323
+ logger.exception(str(e))
324
+
325
+ def remove_actuators(self, actuator_modules: List[DAQ_Move] = None):
326
+ """
327
+ Remove the given list of actuators from the dashboard.
328
+ Parameters
329
+ ----------
330
+ actuator_modules: List[DAQ_Move]
331
+ List of DAQ_Move instances to be removed.
332
+ """
333
+ if actuator_modules is None:
334
+ actuator_modules = []
335
+ try:
336
+ for actuator_module in actuator_modules:
337
+ if actuator_module in self.actuators_modules:
338
+ self.actuators_modules.remove(actuator_module)
339
+ actuator_module.quit_fun()
340
+ dock = self.dockarea.docks.get(actuator_module.title, None)
341
+ if dock:
342
+ dock.close()
343
+ self.update_module_manager()
344
+ except Exception as e:
345
+ logger.exception(str(e))
346
+
347
+ def get_docks_from_modules(
348
+ self, modules: Sequence[Union["DAQ_Move", "DAQ_Viewer"]]
349
+ ) -> List[Dock]:
350
+ """
351
+ Get a list of Dock instances from the given modules.
352
+
353
+ Parameters
354
+ ----------
355
+ modules: Sequence[DAQ_Move/DAQ_Viewer]
356
+ Sequence of DAQ_Move or DAQ_Viewer instances.
357
+
358
+ Returns
359
+ -------
360
+ List[Dock]
361
+ List of Dock instances corresponding to the given modules.
362
+ """
363
+ docks = []
364
+ for module in modules:
365
+ if hasattr(module, "dock"):
366
+ docks.append(module.dock)
367
+ return docks
368
+
369
+ def remove_modules(
370
+ self, modules: List[Union["DAQ_Move", "DAQ_Viewer", "str"]] = None
371
+ ):
372
+ """
373
+ Remove the given list of actuators/detectors from the dashboard.
374
+
375
+ Parameters
376
+ ----------
377
+ modules: List[DAQ_Move/DAQ_Viewer]
378
+ List of DAQ_Move/DAQ_Viewer instances to be removed.
379
+ """
380
+ if modules is None:
381
+ modules = []
382
+ try:
383
+ actuators_modules = []
384
+ detector_modules = []
385
+ for module in modules:
386
+ if isinstance(
387
+ module, DAQ_Move
388
+ ): # Test if module is an instance of DAQ_Move
389
+ actuators_modules.append(module)
390
+ elif isinstance(
391
+ module, DAQ_Viewer
392
+ ): # Test if module is an instance of DAQ_Viewer
393
+ detector_modules.append(module)
394
+ if isinstance(
395
+ module, str
396
+ ): # Test if module is a string (name of the module)
397
+ actuators_modules.extend(
398
+ self.modules_manager.get_mods_from_names(
399
+ [
400
+ module,
401
+ ],
402
+ "act",
403
+ ) # For actuators
404
+ )
405
+ detector_modules.extend(
406
+ self.modules_manager.get_mods_from_names(
407
+ [
408
+ module,
409
+ ],
410
+ "det",
411
+ ) # For detectors
412
+ )
413
+ if (hasattr(self, "actuators_modules")) & (
414
+ self.actuators_modules is not None
415
+ ): # Remove actuators
416
+ self.remove_actuators(actuators_modules)
417
+ if (hasattr(self, "detector_modules")) & (
418
+ self.detector_modules is not None
419
+ ): # Remove detectors
420
+ self.remove_detectors(detector_modules)
421
+ except Exception as e:
422
+ logger.exception(str(e))
423
+
231
424
  def clear_move_det_controllers(self):
232
425
  """
233
- Remove all docks containing Moves or Viewers.
426
+ Remove all docks containing Moves or Viewers.
234
427
 
235
- See Also
236
- --------
237
- quit_fun, update_status
428
+ See Also
429
+ --------
430
+ quit_fun, update_status
238
431
  """
239
432
  try:
240
433
  # remove all docks containing Moves or Viewers
241
- if hasattr(self, 'actuators_modules'):
242
- if self.actuators_modules is not None:
243
- for module in self.actuators_modules:
244
- module.quit_fun()
434
+ if hasattr(self, "actuators_modules") & (
435
+ self.actuators_modules is not None
436
+ ):
437
+ for module in self.actuators_modules:
438
+ module.quit_fun()
245
439
  self.actuators_modules = []
246
440
 
247
- if hasattr(self, 'detector_modules'):
248
- if self.detector_modules is not None:
249
- for module in self.detector_modules:
250
- module.quit_fun()
441
+ if hasattr(self, "detector_modules") & (self.detector_modules is not None):
442
+ for module in self.detector_modules:
443
+ module.quit_fun()
251
444
  self.detector_modules = []
252
445
  except Exception as e:
253
446
  logger.exception(str(e))
@@ -256,11 +449,18 @@ class DashBoard(CustomApp):
256
449
  if win is None:
257
450
  win = QtWidgets.QMainWindow()
258
451
  area = DockArea()
452
+ win.setWindowFlags(
453
+ Qt.Window
454
+ | Qt.WindowTitleHint
455
+ | Qt.WindowMinimizeButtonHint
456
+ | Qt.WindowMaximizeButtonHint
457
+ )
259
458
  win.setCentralWidget(area)
459
+ win.setWindowTitle("Scanner")
260
460
  self.scan_module = extmod.DAQScan(dockarea=area, dashboard=self)
261
- self.extensions['DAQScan'] = self.scan_module
461
+ self.extensions["DAQScan"] = self.scan_module
262
462
  self.scan_module.status_signal.connect(self.add_status)
263
- #win.setWindowTitle("DAQScan")
463
+ # win.setWindowTitle("DAQScan")
264
464
  win.show()
265
465
  return self.scan_module
266
466
 
@@ -268,9 +468,16 @@ class DashBoard(CustomApp):
268
468
  if win is None:
269
469
  win = QtWidgets.QMainWindow()
270
470
  area = DockArea()
471
+ win.setWindowFlags(
472
+ Qt.Window
473
+ | Qt.WindowTitleHint
474
+ | Qt.WindowMinimizeButtonHint
475
+ | Qt.WindowMaximizeButtonHint
476
+ )
271
477
  win.setCentralWidget(area)
478
+ win.setWindowTitle("Logger")
272
479
  self.log_module = extmod.DAQ_Logger(dockarea=area, dashboard=self)
273
- self.extensions['DAQ_Logger'] = self.log_module
480
+ self.extensions["DAQ_Logger"] = self.log_module
274
481
  self.log_module.status_signal.connect(self.add_status)
275
482
  win.show()
276
483
  return self.log_module
@@ -280,22 +487,30 @@ class DashBoard(CustomApp):
280
487
  self.pid_window = QtWidgets.QMainWindow()
281
488
  else:
282
489
  self.pid_window = win
490
+ self.pid_window.setWindowFlags(
491
+ Qt.Window
492
+ | Qt.WindowTitleHint
493
+ | Qt.WindowMinimizeButtonHint
494
+ | Qt.WindowMaximizeButtonHint
495
+ )
283
496
  dockarea = DockArea()
284
497
  self.pid_window.setCentralWidget(dockarea)
285
- self.pid_window.setWindowTitle('PID Controller')
498
+ self.pid_window.setWindowTitle("PID Controller")
286
499
  self.pid_module = extmod.DAQ_PID(dockarea=dockarea, dashboard=self)
287
- self.extensions['DAQ_PID'] = self.pid_module
500
+ self.extensions["DAQ_PID"] = self.pid_module
288
501
  self.pid_window.show()
289
502
  return self.pid_module
290
503
 
291
504
  def load_console(self):
292
- dock_console = Dock('QTConsole')
293
- self.dockarea.addDock(dock_console, 'bottom')
294
- qtconsole = extmod.QtConsole(style_sheet=config('style', 'syntax_highlighting'),
295
- syntax_style=config('style', 'syntax_highlighting'),
296
- custom_banner=extmod.console.BANNER)
505
+ dock_console = Dock("QTConsole")
506
+ self.dockarea.addDock(dock_console, "bottom")
507
+ qtconsole = extmod.QtConsole(
508
+ style_sheet=config_utils("style", "syntax_highlighting"),
509
+ syntax_style=config_utils("style", "syntax_highlighting"),
510
+ custom_banner=extmod.console.BANNER,
511
+ )
297
512
  dock_console.addWidget(qtconsole)
298
- self.extensions['qtconsole'] = qtconsole
513
+ self.extensions["qtconsole"] = qtconsole
299
514
 
300
515
  qtconsole.push_variables(dict(dashboard=self, mods=self.modules_manager, np=np))
301
516
 
@@ -306,19 +521,108 @@ class DashBoard(CustomApp):
306
521
  self.bayesian_window = QtWidgets.QMainWindow()
307
522
  else:
308
523
  self.bayesian_window = win
524
+ self.bayesian_window.setWindowFlags(
525
+ Qt.Window
526
+ | Qt.WindowTitleHint
527
+ | Qt.WindowMinimizeButtonHint
528
+ | Qt.WindowMaximizeButtonHint
529
+ )
309
530
  dockarea = DockArea()
310
531
  self.bayesian_window.setCentralWidget(dockarea)
311
- self.bayesian_window.setWindowTitle('Bayesian Optimiser')
312
- self.bayesian_module = extmod.BayesianOptimisation(dockarea=dockarea, dashboard=self)
313
- self.extensions['bayesian'] = self.bayesian_module
314
- self.bayesian_window.show()
532
+ self.bayesian_window.setWindowTitle("Bayesian Optimiser")
533
+ self.bayesian_module = extmod.BayesianOptimization(
534
+ dockarea=dockarea, dashboard=self
535
+ )
536
+ self.extensions["bayesian"] = self.bayesian_module
537
+
538
+ if self.bayesian_module.validate_config():
539
+ self.bayesian_window.show()
540
+ else:
541
+ messagebox(
542
+ severity="critical",
543
+ title="Bayesian Optimisation error",
544
+ text=f"""
545
+ <p>Saved Bayesian Optimisation configuration file is not compatible anymore.</p>
546
+ <p>Please delete the file at <b>{self.bayesian_module.config_path}</b>.</p>
547
+ """,
548
+ )
549
+ self.bayesian_module.quit()
315
550
  return self.bayesian_module
316
551
 
552
+ def load_adaptive(self, win=None):
553
+ if win is None:
554
+ self.adaptive_window = QtWidgets.QMainWindow()
555
+ else:
556
+ self.adaptive_window = win
557
+ self.adaptive_window.setWindowFlags(
558
+ Qt.Window
559
+ | Qt.WindowTitleHint
560
+ | Qt.WindowMinimizeButtonHint
561
+ | Qt.WindowMaximizeButtonHint
562
+ )
563
+ dockarea = DockArea()
564
+ self.adaptive_window.setCentralWidget(dockarea)
565
+ self.adaptive_window.setWindowTitle("Adaptive Scan")
566
+ self.adaptive_module = extmod.AdaptiveOptimisation(
567
+ dockarea=dockarea, dashboard=self
568
+ )
569
+ self.extensions["adaptive"] = self.adaptive_module
570
+
571
+ if self.adaptive_module.validate_config():
572
+ self.adaptive_window.show()
573
+ else:
574
+ messagebox(
575
+ severity="critical",
576
+ title="Adaptive Optimisation error",
577
+ text=f"""
578
+ <p>Saved Adaptive Optimisation configuration file is not compatible anymore.</p>
579
+ <p>Please delete the file at <b>{self.adaptive_module.config_path}</b>.</p>
580
+ """,
581
+ )
582
+ self.adaptive_module.quit()
583
+ return self.adaptive_module
584
+
585
+ def load_datamixer(self, win=None):
586
+ if win is None:
587
+ self.datamixer_window = QtWidgets.QMainWindow()
588
+ else:
589
+ self.datamixer_window = win
590
+ self.datamixer_window.setWindowFlags(
591
+ Qt.Window
592
+ | Qt.WindowTitleHint
593
+ | Qt.WindowMinimizeButtonHint
594
+ | Qt.WindowMaximizeButtonHint
595
+ )
596
+ dockarea = DockArea()
597
+ self.datamixer_window.setCentralWidget(dockarea)
598
+ self.datamixer_window.setWindowTitle("DataMixer")
599
+ self.datamixer_module = extmod.DataMixer(
600
+ parent=dockarea, dashboard=self
601
+ )
602
+ self.extensions["datamixer"] = self.datamixer_module
603
+
604
+ if self.datamixer_module.validate_config():
605
+ self.datamixer_window.show()
606
+ else:
607
+ messagebox(
608
+ severity="critical",
609
+ title="DataMixer error",
610
+ text=f"""
611
+ <p>Saved DataMixer configuration file is not compatible anymore.</p>
612
+ <p>Please delete the file at <b>{self.datamixer_module.config_path}</b>.</p>
613
+ """,
614
+ )
615
+ self.datamixer_module.quit()
616
+ return self.datamixer_module
617
+
618
+
317
619
  def load_extension_from_name(self, name: str) -> dict:
318
- return self.load_extensions_module(find_dict_in_list_from_key_val(extensions, 'name', name))
620
+ return self.load_extensions_module(
621
+ find_dict_in_list_from_key_val(extensions, "name", name)
622
+ )
319
623
 
320
624
  def load_extensions_module(self, ext: dict):
321
- """ Init and load an extension from a plugin package
625
+ """Init and load an extension from a plugin package
322
626
 
323
627
  ext: dict
324
628
  dictionary containing info on the extension plugin package and class to be loaded,
@@ -339,266 +643,395 @@ class DashBoard(CustomApp):
339
643
  area = DockArea()
340
644
  self.extension_windows[-1].setCentralWidget(area)
341
645
  self.extension_windows[-1].resize(1000, 500)
342
- self.extension_windows[-1].setWindowTitle(ext['name'])
646
+ self.extension_windows[-1].setWindowTitle(ext["name"])
343
647
  module = import_module(f"{ext['pkg']}.extensions.{ext['module']}")
344
- klass = getattr(module, ext['class_name'])
345
- self.extensions[ext['class_name']] = klass(area, dashboard=self)
648
+ klass = getattr(module, ext["class_name"])
649
+ self.extensions[ext["class_name"]] = klass(area, dashboard=self)
346
650
  self.extension_windows[-1].show()
347
- return self.extensions[ext['class_name']]
651
+ return self.extensions[ext["class_name"]]
348
652
 
349
653
  def setup_actions(self):
350
- self.add_action('log', 'Log File', '', "Show Log File in default editor",
351
- auto_toolbar=False)
352
- self.add_action('quit', 'Quit', 'close2', "Quit program")
654
+ self.add_action(
655
+ "log", "Log File", "", "Show Log File in default editor", auto_toolbar=False
656
+ )
657
+ self.add_action("quit", "Quit", "close2", "Quit program")
353
658
  self.toolbar.addSeparator()
354
- self.add_action('config', 'Configuration file', 'tree', "General Settings")
355
- self.add_action('restart', 'Restart', '', "Restart the Dashboard",
356
- auto_toolbar=False)
357
- self.add_action('leco', 'Run Leco Coordinator', '', 'Run a Coordinator on this localhost',
358
- auto_toolbar=False)
359
- self.add_action('load_layout', 'Load Layout', '',
360
- 'Load the Saved Docks layout corresponding to the current preset',
361
- auto_toolbar=False)
362
- self.add_action('save_layout', 'Save Layout', '',
363
- 'Save the Saved Docks layout corresponding to the current preset',
364
- auto_toolbar=False)
365
- self.add_action('log_window', 'Show/hide log window', '', checkable=True,
366
- auto_toolbar=False)
367
- self.add_action('new_preset', 'New Preset', '',
368
- 'Create a new experimental setup configuration file: a "preset"',
369
- auto_toolbar=False)
370
- self.add_action('modify_preset', 'Modify Preset', '',
371
- 'Modify an existing experimental setup configuration file: a "preset"',
372
- auto_toolbar=False)
373
-
374
- self.add_widget('preset_list', QtWidgets.QComboBox, toolbar=self.toolbar,
375
- signal_str='currentTextChanged', slot=self.update_preset_action)
376
- self.add_action('load_preset', 'LOAD', 'Open',
377
- tip='Load the selected Preset: ')
659
+ self.add_action(
660
+ "config_utils",
661
+ "Utils Config.",
662
+ "tree",
663
+ tip="Show utility configuration file",
664
+ )
665
+ self.add_action(
666
+ "config",
667
+ "Controls/Extensions Config.",
668
+ "tree",
669
+ tip="Show Control Modules and Extensions configuration file",
670
+ )
671
+ self.add_action(
672
+ "restart", "Restart", "", "Restart the Dashboard", auto_toolbar=False
673
+ )
674
+ self.add_action(
675
+ "leco",
676
+ "Run Leco Coordinator",
677
+ "",
678
+ "Run a Coordinator on this localhost",
679
+ auto_toolbar=False,
680
+ )
681
+ self.add_action(
682
+ "load_layout",
683
+ "Load Layout",
684
+ "",
685
+ "Load the Saved Docks layout corresponding to the current preset",
686
+ auto_toolbar=False,
687
+ )
688
+ self.add_action(
689
+ "save_layout",
690
+ "Save Layout",
691
+ "",
692
+ "Save the Saved Docks layout corresponding to the current preset",
693
+ auto_toolbar=False,
694
+ )
695
+ self.add_action(
696
+ "log_window", "Show/hide log window", "", checkable=True, auto_toolbar=False
697
+ )
698
+ self.add_action(
699
+ "new_preset",
700
+ "New Preset",
701
+ "",
702
+ 'Create a new experimental setup configuration file: a "preset"',
703
+ auto_toolbar=False,
704
+ )
705
+ self.add_action(
706
+ "modify_preset",
707
+ "Modify Preset",
708
+ "",
709
+ 'Modify an existing experimental setup configuration file: a "preset"',
710
+ auto_toolbar=False,
711
+ )
712
+
713
+ self.add_widget(
714
+ "preset_list",
715
+ QtWidgets.QComboBox,
716
+ toolbar=self.toolbar,
717
+ signal_str="currentTextChanged",
718
+ slot=self.update_preset_action,
719
+ )
720
+ self.add_action("load_preset", "LOAD", "Open", tip="Load the selected Preset: ")
378
721
  self.update_preset_action_list()
379
722
 
380
- self.add_action('new_overshoot', 'New Overshoot', '',
381
- 'Create a new experimental setup overshoot configuration file',
382
- auto_toolbar=False)
383
- self.add_action('modify_overshoot', 'Modify Overshoot', '',
384
- 'Modify an existing experimental setup overshoot configuration file',
385
- auto_toolbar=False)
386
-
387
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_overshoot_path().iterdir()):
388
- if file.suffix == '.xml':
389
- self.add_action(self.get_action_from_file(file, ManagerEnums.overshoot), file.stem,
390
- auto_toolbar=False)
391
-
392
- self.add_action('save_roi', 'Save ROIs as a file', '', auto_toolbar=False)
393
- self.add_action('modify_roi', 'Modify ROI file', '', auto_toolbar=False)
394
-
395
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_roi_path().iterdir()):
396
- if file.suffix == '.xml':
397
- self.add_action(self.get_action_from_file(file, ManagerEnums.roi), file.stem,
398
- '', auto_toolbar=False)
399
-
400
- self.add_action('new_remote', 'Create New Remote', '', auto_toolbar=False)
401
- self.add_action('modify_remote', 'Modify Remote file', '', auto_toolbar=False)
402
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_remote_path().iterdir()):
403
- if file.suffix == '.xml':
404
- self.add_action(self.get_action_from_file(file, ManagerEnums.remote),
405
- file.stem, '', auto_toolbar=False)
406
- self.add_action('activate_overshoot', 'Activate overshoot', 'Error',
407
- tip='if activated, apply an overshoot if one is configured',
408
- checkable=True, enabled=False)
723
+ self.add_action(
724
+ "new_overshoot",
725
+ "New Overshoot",
726
+ "",
727
+ "Create a new experimental setup overshoot configuration file",
728
+ auto_toolbar=False,
729
+ )
730
+ self.add_action(
731
+ "modify_overshoot",
732
+ "Modify Overshoot",
733
+ "",
734
+ "Modify an existing experimental setup overshoot configuration file",
735
+ auto_toolbar=False,
736
+ )
737
+
738
+ for ind_file, file in enumerate(
739
+ config_mod_pymodaq.get_set_overshoot_path().iterdir()
740
+ ):
741
+ if file.suffix == ".xml":
742
+ self.add_action(
743
+ self.get_action_from_file(file, ManagerEnums.overshoot),
744
+ file.stem,
745
+ auto_toolbar=False,
746
+ )
747
+
748
+ self.add_action("save_roi", "Save ROIs as a file", "", auto_toolbar=False)
749
+ self.add_action("modify_roi", "Modify ROI file", "", auto_toolbar=False)
750
+
751
+ for ind_file, file in enumerate(
752
+ config_mod_pymodaq.get_set_roi_path().iterdir()
753
+ ):
754
+ if file.suffix == ".xml":
755
+ self.add_action(
756
+ self.get_action_from_file(file, ManagerEnums.roi),
757
+ file.stem,
758
+ "",
759
+ auto_toolbar=False,
760
+ )
761
+
762
+ self.add_action("new_remote", "Create New Remote", "", auto_toolbar=False)
763
+ self.add_action("modify_remote", "Modify Remote file", "", auto_toolbar=False)
764
+ for ind_file, file in enumerate(
765
+ config_mod_pymodaq.get_set_remote_path().iterdir()
766
+ ):
767
+ if file.suffix == ".xml":
768
+ self.add_action(
769
+ self.get_action_from_file(file, ManagerEnums.remote),
770
+ file.stem,
771
+ "",
772
+ auto_toolbar=False,
773
+ )
774
+ self.add_action(
775
+ "activate_overshoot",
776
+ "Activate overshoot",
777
+ "Error",
778
+ tip="if activated, apply an overshoot if one is configured",
779
+ checkable=True,
780
+ enabled=False,
781
+ )
409
782
  self.toolbar.addSeparator()
410
- self.add_action('do_scan', 'Do Scans', 'surfacePlot',
411
- tip='Open the DAQ Scan extension to acquire data as a function of '
412
- 'one or more parameter')
783
+ self.add_action(
784
+ "do_scan",
785
+ "Do Scans",
786
+ "surfacePlot",
787
+ tip="Open the DAQ Scan extension to acquire data as a function of "
788
+ "one or more parameter",
789
+ )
413
790
  self.toolbar.addSeparator()
414
- self.add_action('do_log', 'Log data', '', auto_toolbar=False)
415
- self.add_action('do_pid', 'PID module', auto_toolbar=False)
416
- self.add_action('console', 'IPython Console', auto_toolbar=False)
417
- self.add_action('bayesian', 'Bayesian Optimisation', auto_toolbar=False)
418
-
419
- self.add_action('about', 'About', 'information2')
420
- self.add_action('help', 'Help', 'help1')
421
- self.get_action('help').setShortcut(QtGui.QKeySequence('F1'))
422
- self.add_action('check_update', 'Check Updates', '', auto_toolbar=False)
791
+ self.add_action("do_log", "Log data", "", auto_toolbar=False)
792
+ self.add_action("do_pid", "PID module", auto_toolbar=False)
793
+ self.add_action("console", "IPython Console", auto_toolbar=False)
794
+ self.add_action("bayesian", "Bayesian Optimisation", auto_toolbar=False)
795
+ self.add_action("adaptive", "Adaptive Scan", auto_toolbar=False)
796
+ self.add_action("datamixer", "DataMixer", auto_toolbar=False)
797
+
798
+ self.add_action("about", "About", "information2")
799
+ self.add_action("help", "Help", "help1")
800
+ self.get_action("help").setShortcut(QtGui.QKeySequence("F1"))
801
+ self.add_action("check_update", "Check Updates", "", auto_toolbar=False)
423
802
  self.toolbar.addSeparator()
424
- self.add_action('plugin_manager', 'Plugin Manager', '')
803
+ self.add_action("plugin_manager", "Plugin Manager", "")
425
804
 
426
805
  def update_preset_action_list(self):
427
806
  presets = []
428
- self.get_action('preset_list').clear()
807
+ self.get_action("preset_list").clear()
429
808
  for ind_file, file in enumerate(self.preset_path.iterdir()):
430
- if file.suffix == '.xml':
809
+ if file.suffix == ".xml":
431
810
  filestem = file.stem
432
- if not self.has_action(self.get_action_from_file(file, ManagerEnums.preset)):
433
- self.add_action(self.get_action_from_file(file, ManagerEnums.preset),
434
- filestem, '', f'Load the {filestem}.xml preset',
435
- auto_toolbar=False)
811
+ if not self.has_action(
812
+ self.get_action_from_file(file, ManagerEnums.preset)
813
+ ):
814
+ self.add_action(
815
+ self.get_action_from_file(file, ManagerEnums.preset),
816
+ filestem,
817
+ "",
818
+ f"Load the {filestem}.xml preset",
819
+ auto_toolbar=False,
820
+ )
436
821
  presets.append(filestem)
437
822
 
438
- self.get_action('preset_list').addItems(presets)
823
+ self.get_action("preset_list").addItems(presets)
439
824
 
440
825
  def update_preset_action(self, preset_name: str):
441
- self.get_action('load_preset').setToolTip(f'Load the {preset_name}.xml preset file!')
826
+ self.get_action("load_preset").setToolTip(
827
+ f"Load the {preset_name}.xml preset file!"
828
+ )
442
829
 
443
830
  def connect_things(self):
444
831
  self.status_signal[str].connect(self.add_status)
445
- self.connect_action('log', self.show_log)
446
- self.connect_action('config', self.show_config)
447
- self.connect_action('quit', self.quit_fun)
448
- self.connect_action('restart', self.restart_fun)
449
- self.connect_action('leco', start_coordinator)
450
- self.connect_action('load_layout', self.load_layout_state)
451
- self.connect_action('save_layout', self.save_layout_state)
452
- self.connect_action('log_window', self.logger_dock.setVisible)
453
- self.connect_action('new_preset', self.create_preset)
454
- self.connect_action('modify_preset', self.modify_preset)
832
+ self.connect_action("log", self.show_log)
833
+ self.connect_action("config_utils", lambda: self.show_config(config_utils))
834
+ self.connect_action("config", lambda: self.show_config(config))
835
+ self.connect_action("quit", self.quit_fun)
836
+ self.connect_action("restart", self.restart_fun)
837
+ self.connect_action("leco", start_coordinator)
838
+ self.connect_action("load_layout", self.load_layout_state)
839
+ self.connect_action("save_layout", self.save_layout_state)
840
+ self.connect_action("log_window", self.logger_dock.setVisible)
841
+ self.connect_action("new_preset", self.create_preset)
842
+ self.connect_action("modify_preset", self.modify_preset)
455
843
 
456
844
  for ind_file, file in enumerate(self.preset_path.iterdir()):
457
- if file.suffix == '.xml':
458
- self.connect_action(self.get_action_from_file(file, ManagerEnums.preset),
459
- self.create_menu_slot(self.preset_path.joinpath(file)))
460
- self.connect_action('load_preset',
461
- lambda: self.set_preset_mode(
462
- self.preset_path.joinpath(
463
- f"{self.get_action('preset_list').currentText()}.xml")))
464
- self.connect_action('new_overshoot', self.create_overshoot)
465
- self.connect_action('modify_overshoot', self.modify_overshoot)
466
- self.connect_action('activate_overshoot', self.activate_overshoot)
467
-
468
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_overshoot_path().iterdir()):
469
- if file.suffix == '.xml':
470
- self.connect_action(self.get_action_from_file(file, ManagerEnums.overshoot),
845
+ if file.suffix == ".xml":
846
+ self.connect_action(
847
+ self.get_action_from_file(file, ManagerEnums.preset),
848
+ self.create_menu_slot(self.preset_path.joinpath(file)),
849
+ )
850
+ self.connect_action(
851
+ "load_preset",
852
+ lambda: self.set_preset_mode(
853
+ self.preset_path.joinpath(
854
+ f"{self.get_action('preset_list').currentText()}.xml"
855
+ )
856
+ ),
857
+ )
858
+ self.connect_action("new_overshoot", self.create_overshoot)
859
+ self.connect_action("modify_overshoot", self.modify_overshoot)
860
+ self.connect_action("activate_overshoot", self.activate_overshoot)
861
+
862
+ for ind_file, file in enumerate(
863
+ config_mod_pymodaq.get_set_overshoot_path().iterdir()
864
+ ):
865
+ if file.suffix == ".xml":
866
+ self.connect_action(
867
+ self.get_action_from_file(file, ManagerEnums.overshoot),
471
868
  self.create_menu_slot_over(
472
- config_mod_pymodaq.get_set_overshoot_path().joinpath(file)))
473
-
474
- self.connect_action('save_roi', self.create_roi_file)
475
- self.connect_action('modify_roi', self.modify_roi)
476
-
477
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_roi_path().iterdir()):
478
- if file.suffix == '.xml':
479
- self.connect_action(self.get_action_from_file(file, ManagerEnums.roi),
480
- self.create_menu_slot_roi(config_mod_pymodaq.get_set_roi_path().joinpath(file)))
481
-
482
- self.connect_action('new_remote', self.create_remote)
483
- self.connect_action('modify_remote', self.modify_remote)
484
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_remote_path().iterdir()):
485
- if file.suffix == '.xml':
486
- self.connect_action(self.get_action_from_file(file, ManagerEnums.remote),
869
+ config_mod_pymodaq.get_set_overshoot_path().joinpath(file)
870
+ ),
871
+ )
872
+
873
+ self.connect_action("save_roi", self.create_roi_file)
874
+ self.connect_action("modify_roi", self.modify_roi)
875
+
876
+ for ind_file, file in enumerate(
877
+ config_mod_pymodaq.get_set_roi_path().iterdir()
878
+ ):
879
+ if file.suffix == ".xml":
880
+ self.connect_action(
881
+ self.get_action_from_file(file, ManagerEnums.roi),
882
+ self.create_menu_slot_roi(
883
+ config_mod_pymodaq.get_set_roi_path().joinpath(file)
884
+ ),
885
+ )
886
+
887
+ self.connect_action("new_remote", self.create_remote)
888
+ self.connect_action("modify_remote", self.modify_remote)
889
+ for ind_file, file in enumerate(
890
+ config_mod_pymodaq.get_set_remote_path().iterdir()
891
+ ):
892
+ if file.suffix == ".xml":
893
+ self.connect_action(
894
+ self.get_action_from_file(file, ManagerEnums.remote),
487
895
  self.create_menu_slot_remote(
488
- config_mod_pymodaq.get_set_remote_path().joinpath(file)))
489
-
490
- self.connect_action('do_scan', lambda: self.load_scan_module())
491
- self.connect_action('do_log', lambda: self.load_log_module())
492
- self.connect_action('do_pid', lambda: self.load_pid_module())
493
- self.connect_action('console', lambda: self.load_console())
494
- self.connect_action('bayesian', lambda: self.load_bayesian())
495
-
496
- self.connect_action('about', self.show_about)
497
- self.connect_action('help', self.show_help)
498
- self.connect_action('check_update', lambda: self.check_update(True))
499
- self.connect_action('plugin_manager', self.start_plugin_manager)
896
+ config_mod_pymodaq.get_set_remote_path().joinpath(file)
897
+ ),
898
+ )
899
+
900
+ self.connect_action("do_scan", lambda: self.load_scan_module())
901
+ self.connect_action("do_log", lambda: self.load_log_module())
902
+ self.connect_action("do_pid", lambda: self.load_pid_module())
903
+ self.connect_action("console", lambda: self.load_console())
904
+ self.connect_action("bayesian", lambda: self.load_bayesian())
905
+ self.connect_action("adaptive", lambda: self.load_adaptive())
906
+ self.connect_action("datamixer", lambda: self.load_datamixer())
907
+
908
+ self.connect_action("about", self.show_about)
909
+ self.connect_action("help", self.show_help)
910
+ self.connect_action("check_update", lambda: self.check_update(True))
911
+ self.connect_action("plugin_manager", self.start_plugin_manager)
500
912
 
501
913
  def setup_menu(self, menubar: QtWidgets.QMenuBar = None):
502
914
  """
503
- Create the menubar object looking like :
915
+ Create the menubar object looking like :
504
916
  """
505
917
  menubar.clear()
506
918
 
507
919
  # %% create Settings menu
508
- self.file_menu = menubar.addMenu('File')
509
- self.file_menu.addAction(self.get_action('log'))
510
- self.file_menu.addAction(self.get_action('config'))
920
+ self.file_menu = menubar.addMenu("File")
921
+ self.file_menu.addAction(self.get_action("log"))
922
+ self.file_menu.addAction(self.get_action("config_utils"))
923
+ self.file_menu.addAction(self.get_action("config"))
511
924
  self.file_menu.addSeparator()
512
- self.file_menu.addAction(self.get_action('quit'))
513
- self.file_menu.addAction(self.get_action('restart'))
925
+ self.file_menu.addAction(self.get_action("quit"))
926
+ self.file_menu.addAction(self.get_action("restart"))
514
927
 
515
- self.settings_menu = menubar.addMenu('Settings')
516
- self.settings_menu.addAction(self.get_action('leco'))
517
- docked_menu = self.settings_menu.addMenu('Docked windows')
518
- docked_menu.addAction(self.get_action('load_layout'))
519
- docked_menu.addAction(self.get_action('save_layout'))
928
+ self.settings_menu = menubar.addMenu("Settings")
929
+ self.settings_menu.addAction(self.get_action("leco"))
930
+ docked_menu = self.settings_menu.addMenu("Docked windows")
931
+ docked_menu.addAction(self.get_action("load_layout"))
932
+ docked_menu.addAction(self.get_action("save_layout"))
520
933
 
521
934
  docked_menu.addSeparator()
522
- docked_menu.addAction(self.get_action('log_window'))
935
+ docked_menu.addAction(self.get_action("log_window"))
523
936
 
524
- self.preset_menu = menubar.addMenu('Preset Modes')
525
- self.preset_menu.addAction(self.get_action('new_preset'))
526
- self.preset_menu.addAction(self.get_action('modify_preset'))
937
+ self.preset_menu = menubar.addMenu("Preset Modes")
938
+ self.preset_menu.addAction(self.get_action("new_preset"))
939
+ self.preset_menu.addAction(self.get_action("modify_preset"))
527
940
  self.preset_menu.addSeparator()
528
- self.load_preset_menu = self.preset_menu.addMenu('Load presets')
941
+ self.load_preset_menu = self.preset_menu.addMenu("Load presets")
529
942
 
530
943
  for ind_file, file in enumerate(self.preset_path.iterdir()):
531
- if file.suffix == '.xml':
944
+ if file.suffix == ".xml":
532
945
  self.load_preset_menu.addAction(
533
- self.get_action(self.get_action_from_file(file, ManagerEnums.preset)))
534
-
535
- self.overshoot_menu = menubar.addMenu('Overshoot Modes')
536
- self.overshoot_menu.addAction(self.get_action('new_overshoot'))
537
- self.overshoot_menu.addAction(self.get_action('modify_overshoot'))
538
- self.overshoot_menu.addAction(self.get_action('activate_overshoot'))
946
+ self.get_action(
947
+ self.get_action_from_file(file, ManagerEnums.preset)
948
+ )
949
+ )
950
+
951
+ self.overshoot_menu = menubar.addMenu("Overshoot Modes")
952
+ self.overshoot_menu.addAction(self.get_action("new_overshoot"))
953
+ self.overshoot_menu.addAction(self.get_action("modify_overshoot"))
954
+ self.overshoot_menu.addAction(self.get_action("activate_overshoot"))
539
955
  self.overshoot_menu.addSeparator()
540
- load_overshoot_menu = self.overshoot_menu.addMenu('Load Overshoots')
956
+ load_overshoot_menu = self.overshoot_menu.addMenu("Load Overshoots")
541
957
 
542
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_overshoot_path().iterdir()):
543
- if file.suffix == '.xml':
958
+ for ind_file, file in enumerate(
959
+ config_mod_pymodaq.get_set_overshoot_path().iterdir()
960
+ ):
961
+ if file.suffix == ".xml":
544
962
  load_overshoot_menu.addAction(
545
- self.get_action(self.get_action_from_file(file, ManagerEnums.overshoot)))
546
-
547
- self.roi_menu = menubar.addMenu('ROI Modes')
548
- self.roi_menu.addAction(self.get_action('save_roi'))
549
- self.roi_menu.addAction(self.get_action('modify_roi'))
963
+ self.get_action(
964
+ self.get_action_from_file(file, ManagerEnums.overshoot)
965
+ )
966
+ )
967
+
968
+ self.roi_menu = menubar.addMenu("ROI Modes")
969
+ self.roi_menu.addAction(self.get_action("save_roi"))
970
+ self.roi_menu.addAction(self.get_action("modify_roi"))
550
971
  self.roi_menu.addSeparator()
551
- load_roi_menu = self.roi_menu.addMenu('Load roi configs')
972
+ load_roi_menu = self.roi_menu.addMenu("Load roi configs")
552
973
 
553
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_roi_path().iterdir()):
554
- if file.suffix == '.xml':
974
+ for ind_file, file in enumerate(
975
+ config_mod_pymodaq.get_set_roi_path().iterdir()
976
+ ):
977
+ if file.suffix == ".xml":
555
978
  load_roi_menu.addAction(
556
- self.get_action(self.get_action_from_file(file, ManagerEnums.roi)))
979
+ self.get_action(self.get_action_from_file(file, ManagerEnums.roi))
980
+ )
557
981
 
558
- self.remote_menu = menubar.addMenu('Remote/Shortcuts Control')
559
- self.remote_menu.addAction('New remote config.', self.create_remote)
560
- self.remote_menu.addAction('Modify remote config.', self.modify_remote)
982
+ self.remote_menu = menubar.addMenu("Remote/Shortcuts Control")
983
+ self.remote_menu.addAction("New remote config.", self.create_remote)
984
+ self.remote_menu.addAction("Modify remote config.", self.modify_remote)
561
985
  self.remote_menu.addSeparator()
562
- load_remote_menu = self.remote_menu.addMenu('Load remote config.')
986
+ load_remote_menu = self.remote_menu.addMenu("Load remote config.")
563
987
 
564
- for ind_file, file in enumerate(config_mod_pymodaq.get_set_remote_path().iterdir()):
565
- if file.suffix == '.xml':
988
+ for ind_file, file in enumerate(
989
+ config_mod_pymodaq.get_set_remote_path().iterdir()
990
+ ):
991
+ if file.suffix == ".xml":
566
992
  load_remote_menu.addAction(
567
- self.get_action(self.get_action_from_file(file, ManagerEnums.remote)))
993
+ self.get_action(
994
+ self.get_action_from_file(file, ManagerEnums.remote)
995
+ )
996
+ )
568
997
 
569
998
  # extensions menu
570
- self.extensions_menu = menubar.addMenu('Extensions')
571
- self.extensions_menu.addAction(self.get_action('do_scan'))
572
- self.extensions_menu.addAction(self.get_action('do_log'))
573
- self.extensions_menu.addAction(self.get_action('do_pid'))
574
- self.extensions_menu.addAction(self.get_action('console'))
575
- self.extensions_menu.addAction(self.get_action('bayesian'))
999
+ self.extensions_menu = menubar.addMenu("Extensions")
1000
+ self.extensions_menu.addAction(self.get_action("do_scan"))
1001
+ self.extensions_menu.addAction(self.get_action("do_log"))
1002
+ self.extensions_menu.addAction(self.get_action("do_pid"))
1003
+ self.extensions_menu.addAction(self.get_action("console"))
1004
+ self.extensions_menu.addAction(self.get_action("bayesian"))
1005
+ self.extensions_menu.addAction(self.get_action("adaptive"))
1006
+ self.extensions_menu.addAction(self.get_action("datamixer"))
576
1007
 
577
1008
  # extensions from plugins
578
1009
  extensions_actions = []
579
1010
  for ext in extensions:
580
- extensions_actions.append(self.extensions_menu.addAction(ext['name']))
1011
+ extensions_actions.append(self.extensions_menu.addAction(ext["name"]))
581
1012
  extensions_actions[-1].triggered.connect(self.create_menu_slot_ext(ext))
582
1013
 
583
1014
  # help menu
584
- help_menu = menubar.addMenu('?')
585
- help_menu.addAction(self.get_action('about'))
586
- help_menu.addAction(self.get_action('help'))
1015
+ help_menu = menubar.addMenu("?")
1016
+ help_menu.addAction(self.get_action("about"))
1017
+ help_menu.addAction(self.get_action("help"))
587
1018
  help_menu.addSeparator()
588
- help_menu.addAction(self.get_action('check_update'))
589
- help_menu.addAction(self.get_action('plugin_manager'))
1019
+ help_menu.addAction(self.get_action("check_update"))
1020
+ help_menu.addAction(self.get_action("plugin_manager"))
590
1021
 
591
- self.overshoot_menu.setEnabled(False)
592
- self.roi_menu.setEnabled(False)
593
- self.remote_menu.setEnabled(False)
594
- self.extensions_menu.setEnabled(False)
1022
+ status = self.preset_file is None
1023
+
1024
+ self.overshoot_menu.setEnabled(not status)
1025
+ self.roi_menu.setEnabled(not status)
1026
+ self.remote_menu.setEnabled(not status)
1027
+ self.extensions_menu.setEnabled(not status)
595
1028
  self.file_menu.setEnabled(True)
596
1029
  self.settings_menu.setEnabled(True)
597
- self.preset_menu.setEnabled(True)
1030
+ self.preset_menu.setEnabled(status)
598
1031
 
599
1032
  def start_plugin_manager(self):
600
1033
  self.win_plug_manager = QtWidgets.QMainWindow()
601
- self.win_plug_manager.setWindowTitle('PyMoDAQ Plugin Manager')
1034
+ self.win_plug_manager.setWindowTitle("PyMoDAQ Plugin Manager")
602
1035
  widget = QtWidgets.QWidget()
603
1036
  self.win_plug_manager.setCentralWidget(widget)
604
1037
  self.plugin_manager = PluginManager(widget)
@@ -625,10 +1058,19 @@ class DashBoard(CustomApp):
625
1058
  try:
626
1059
  if self.preset_file is not None:
627
1060
  self.roi_saver.set_new_roi(self.preset_file.stem)
628
- self.add_action(self.get_action_from_file(self.preset_file,
629
- ManagerEnums.roi),
630
- self.preset_file.stem, '')
1061
+ self.add_action(
1062
+ self.get_action_from_file(self.preset_file, ManagerEnums.roi),
1063
+ self.preset_file.stem,
1064
+ "",
1065
+ )
631
1066
  self.setup_menu(self.menubar)
1067
+ self.connect_action(
1068
+ self.get_action_from_file(self.preset_file, ManagerEnums.roi),
1069
+ self.create_menu_slot_roi(
1070
+ config_mod_pymodaq.get_set_roi_path().joinpath(self.preset_file.name)
1071
+ ),
1072
+ )
1073
+
632
1074
 
633
1075
  except Exception as e:
634
1076
  logger.exception(str(e))
@@ -637,10 +1079,18 @@ class DashBoard(CustomApp):
637
1079
  try:
638
1080
  if self.preset_file is not None:
639
1081
  self.remote_manager.set_new_remote(self.preset_file.stem)
640
- self.add_action(self.get_action_from_file(self.preset_file,
641
- ManagerEnums.remote),
642
- self.preset_file.stem, '')
1082
+ self.add_action(
1083
+ self.get_action_from_file(self.preset_file, ManagerEnums.remote),
1084
+ self.preset_file.stem,
1085
+ "",
1086
+ )
643
1087
  self.setup_menu(self.menubar)
1088
+ self.connect_action(
1089
+ self.get_action_from_file(self.preset_file, ManagerEnums.remote),
1090
+ self.create_menu_slot_remote(
1091
+ config_mod_pymodaq.get_set_remote_path().joinpath(self.preset_file.name)
1092
+ ),
1093
+ )
644
1094
 
645
1095
  except Exception as e:
646
1096
  logger.exception(str(e))
@@ -649,10 +1099,18 @@ class DashBoard(CustomApp):
649
1099
  try:
650
1100
  if self.preset_file is not None:
651
1101
  self.overshoot_manager.set_new_overshoot(self.preset_file.stem)
652
- self.add_action(self.get_action_from_file(self.preset_file,
653
- ManagerEnums.overshoot),
654
- self.preset_file.stem, '')
1102
+ self.add_action(
1103
+ self.get_action_from_file(self.preset_file, ManagerEnums.overshoot),
1104
+ self.preset_file.stem,
1105
+ "",
1106
+ )
655
1107
  self.setup_menu(self.menubar)
1108
+ self.connect_action(
1109
+ self.get_action_from_file(self.preset_file, ManagerEnums.overshoot),
1110
+ self.create_menu_slot_over(
1111
+ config_mod_pymodaq.get_set_overshoot_path().joinpath(self.preset_file.name)
1112
+ ),
1113
+ )
656
1114
  except Exception as e:
657
1115
  logger.exception(str(e))
658
1116
 
@@ -668,13 +1126,16 @@ class DashBoard(CustomApp):
668
1126
 
669
1127
  @staticmethod
670
1128
  def get_action_from_file(file: Path, manager: ManagerEnums):
671
- return f'{file.stem}_{manager.name}'
1129
+ return f"{file.stem}_{manager.name}"
672
1130
 
673
1131
  def modify_remote(self):
674
1132
  try:
675
- path = select_file(start_path=config_mod_pymodaq.get_set_remote_path(), save=False,
676
- ext='xml')
677
- if path != '':
1133
+ path = select_file(
1134
+ start_path=config_mod_pymodaq.get_set_remote_path(),
1135
+ save=False,
1136
+ ext="xml",
1137
+ )
1138
+ if path != "":
678
1139
  self.remote_manager.set_file_remote(path)
679
1140
 
680
1141
  else: # cancel
@@ -684,9 +1145,12 @@ class DashBoard(CustomApp):
684
1145
 
685
1146
  def modify_overshoot(self):
686
1147
  try:
687
- path = select_file(start_path=config_mod_pymodaq.get_set_overshoot_path(),
688
- save=False, ext='xml')
689
- if path != '':
1148
+ path = select_file(
1149
+ start_path=config_mod_pymodaq.get_set_overshoot_path(),
1150
+ save=False,
1151
+ ext="xml",
1152
+ )
1153
+ if path != "":
690
1154
  self.overshoot_manager.set_file_overshoot(path)
691
1155
 
692
1156
  else: # cancel
@@ -696,9 +1160,10 @@ class DashBoard(CustomApp):
696
1160
 
697
1161
  def modify_roi(self):
698
1162
  try:
699
- path = select_file(start_path=config_mod_pymodaq.get_set_roi_path(),
700
- save=False, ext='xml')
701
- if path != '':
1163
+ path = select_file(
1164
+ start_path=config_mod_pymodaq.get_set_roi_path(), save=False, ext="xml"
1165
+ )
1166
+ if path != "":
702
1167
  self.roi_saver.set_file_roi(path)
703
1168
 
704
1169
  else: # cancel
@@ -708,19 +1173,21 @@ class DashBoard(CustomApp):
708
1173
 
709
1174
  def modify_preset(self):
710
1175
  try:
711
- path = select_file(start_path=self.preset_path, save=False, ext='xml')
712
- if path != '':
1176
+ path = select_file(start_path=self.preset_path, save=False, ext="xml")
1177
+ if path != "":
713
1178
  modified = self.preset_manager.set_file_preset(path)
714
1179
 
715
1180
  if modified:
716
1181
  self.remove_preset_related_files(path.name)
717
1182
  if self.detector_modules:
718
- mssg = QtWidgets.QMessageBox()
719
- mssg.setText('You have to restart the application to take the modifications'
720
- ' into account!\n\n'
721
- 'The related files: ROI, Layout, Overshoot and Remote will be'
722
- ' deleted if existing!\n\n'
723
- 'Quitting the application...')
1183
+ mssg = QMessageBox()
1184
+ mssg.setText(
1185
+ "You have to restart the application to take the modifications"
1186
+ " into account!\n\n"
1187
+ "The related files: ROI, Layout, Overshoot and Remote will be"
1188
+ " deleted if existing!\n\n"
1189
+ "Quitting the application..."
1190
+ )
724
1191
  mssg.exec()
725
1192
  self.restart_fun()
726
1193
 
@@ -732,22 +1199,24 @@ class DashBoard(CustomApp):
732
1199
  def remove_preset_related_files(self, name):
733
1200
  config_mod_pymodaq.get_set_roi_path().joinpath(name).unlink(missing_ok=True)
734
1201
  config_mod_pymodaq.get_set_layout_path().joinpath(name).unlink(missing_ok=True)
735
- config_mod_pymodaq.get_set_overshoot_path().joinpath(name).unlink(missing_ok=True)
1202
+ config_mod_pymodaq.get_set_overshoot_path().joinpath(name).unlink(
1203
+ missing_ok=True
1204
+ )
736
1205
  config_mod_pymodaq.get_set_remote_path().joinpath(name).unlink(missing_ok=True)
737
1206
 
738
1207
  def quit_fun(self):
739
1208
  """
740
- Quit the current instance of DAQ_scan and close on cascade move and detector modules.
1209
+ Quit the current instance of DAQ_scan and close on cascade move and detector modules.
741
1210
 
742
- See Also
743
- --------
744
- quit_fun
1211
+ See Also
1212
+ --------
1213
+ quit_fun
745
1214
  """
746
1215
  try:
747
1216
  self.remote_timer.stop()
748
1217
 
749
1218
  for ext in self.extensions:
750
- if hasattr(self.extensions[ext], 'quit_fun'):
1219
+ if hasattr(self.extensions[ext], "quit_fun"):
751
1220
  self.extensions[ext].quit_fun()
752
1221
  for mov in self.actuators_modules:
753
1222
  try:
@@ -784,7 +1253,7 @@ class DashBoard(CustomApp):
784
1253
  QThread.msleep(1000)
785
1254
  QtWidgets.QApplication.processEvents()
786
1255
 
787
- if hasattr(self, 'mainwindow'):
1256
+ if hasattr(self, "mainwindow"):
788
1257
  self.mainwindow.close()
789
1258
 
790
1259
  if self.pid_window is not None:
@@ -795,40 +1264,42 @@ class DashBoard(CustomApp):
795
1264
 
796
1265
  def restart_fun(self, ask=False):
797
1266
  ret = False
798
- mssg = QtWidgets.QMessageBox()
1267
+ mssg = QMessageBox()
799
1268
  if ask:
800
- mssg.setText('You have to restart the application to take the'
801
- ' modifications into account!')
1269
+ mssg.setText(
1270
+ "You have to restart the application to take the"
1271
+ " modifications into account!"
1272
+ )
802
1273
  mssg.setInformativeText("Do you want to restart?")
803
- mssg.setStandardButtons(mssg.Ok | mssg.Cancel)
1274
+ mssg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel)
804
1275
  ret = mssg.exec()
805
1276
 
806
- if ret == mssg.Ok or not ask:
1277
+ if ret == QMessageBox.StandardButton.Ok or not ask:
807
1278
  self.quit_fun()
808
1279
  subprocess.call([sys.executable, __file__])
809
1280
 
810
1281
  def load_layout_state(self, file=None):
811
1282
  """
812
- Load and restore a layout state from the select_file obtained pathname file.
1283
+ Load and restore a layout state from the select_file obtained pathname file.
813
1284
 
814
- See Also
815
- --------
816
- utils.select_file
1285
+ See Also
1286
+ --------
1287
+ utils.select_file
817
1288
  """
818
1289
  try:
819
1290
  file = layout_mod.load_layout_state(self.dockarea, file)
820
- self.settings.child('loaded_files', 'layout_file').setValue(file)
1291
+ self.settings.child("loaded_files", "layout_file").setValue(file)
821
1292
  except Exception as e:
822
1293
  logger.exception(str(e))
823
1294
 
824
1295
  def save_layout_state(self, file=None):
825
1296
  """
826
- Save the current layout state in the select_file obtained pathname file.
827
- Once done dump the pickle.
1297
+ Save the current layout state in the select_file obtained pathname file.
1298
+ Once done dump the pickle.
828
1299
 
829
- See Also
830
- --------
831
- utils.select_file
1300
+ See Also
1301
+ --------
1302
+ utils.select_file
832
1303
  """
833
1304
  try:
834
1305
  layout_mod.save_layout_state(self.dockarea, file)
@@ -837,42 +1308,91 @@ class DashBoard(CustomApp):
837
1308
 
838
1309
  def save_layout_state_auto(self):
839
1310
  if self.preset_file is not None:
840
- path = layout_path.joinpath(self.preset_file.stem + '.dock')
1311
+ path = layout_path.joinpath(self.preset_file.stem + ".dock")
841
1312
  self.save_layout_state(path)
842
1313
 
843
- def add_move(self, plug_name, plug_settings, plug_type, move_docks, move_forms,
844
- actuators_modules) -> DAQ_Move:
1314
+ def add_move(
1315
+ self,
1316
+ plug_name: str = None,
1317
+ plug_settings: Parameter = None,
1318
+ plug_type: str = None,
1319
+ move_docks: list[Dock] = None,
1320
+ move_forms: list[QtWidgets.QWidget] = None,
1321
+ actuators_modules: list[DAQ_Move] = None,
1322
+ ui_identifier: str = None,
1323
+ **kwargs
1324
+ ) -> DAQ_Move:
1325
+ if move_docks is None:
1326
+ move_docks = []
1327
+ if move_forms is None:
1328
+ move_forms = []
1329
+ if actuators_modules is None:
1330
+ actuators_modules = []
845
1331
 
846
- move_docks.append(Dock(plug_name, size=(150, 250)))
847
- if len(move_docks) == 1:
848
- self.dockarea.addDock(move_docks[-1], 'right', self.logger_dock)
1332
+ if ui_identifier is not None:
1333
+ pass
1334
+ elif plug_settings is None:
1335
+ ui_identifier = config("actuator", "ui")
1336
+ else:
1337
+ try:
1338
+ ui_identifier = plug_settings["main_settings", "ui_type"]
1339
+ except KeyError:
1340
+ ui_identifier = config("actuator", "ui")
1341
+
1342
+ is_compact = (
1343
+ ActuatorUIFactory.get(ui_identifier).is_compact
1344
+ if ui_identifier is not None
1345
+ else False
1346
+ )
1347
+
1348
+ if is_compact:
1349
+ if self.compact_actuator_dock is None:
1350
+ self.compact_actuator_dock = Dock("Simple Actuators")
1351
+ self.compact_actuator_dock.layout.setSpacing(0)
1352
+ self.compact_actuator_dock.layout.setContentsMargins(0, 0, 0, 0)
1353
+
1354
+ dock = self.compact_actuator_dock
1355
+ self.logger_dock.area.addDock(dock, "bottom")
849
1356
  else:
850
- self.dockarea.addDock(move_docks[-1], 'above', move_docks[-2])
1357
+ dock = Dock(plug_name, size=(150, 250))
1358
+ move_docks.append(dock)
1359
+
1360
+ if len(move_docks) == 1:
1361
+ self.dockarea.addDock(dock, "right", self.logger_dock)
1362
+ else:
1363
+ self.dockarea.addDock(dock, "above", move_docks[-2])
1364
+
851
1365
  move_forms.append(QtWidgets.QWidget())
852
- mov_mod_tmp = DAQ_Move(move_forms[-1], plug_name)
1366
+ mov_mod_tmp = DAQ_Move(move_forms[-1], plug_name, ui_identifier=ui_identifier)
853
1367
 
854
1368
  mov_mod_tmp.actuator = plug_type
855
1369
  QtWidgets.QApplication.processEvents()
856
- mov_mod_tmp.manage_ui_actions('quit', 'setEnabled', False)
1370
+ mov_mod_tmp.manage_ui_actions("quit", "setEnabled", False)
857
1371
 
858
1372
  if plug_settings is not None:
859
1373
  try:
860
1374
  putils.set_param_from_param(mov_mod_tmp.settings, plug_settings)
861
1375
  except KeyError as e:
862
- mssg = f'Could not set this setting: {str(e)}\n' \
863
- f'The Preset is no more compatible with the plugin {plug_type}'
1376
+ mssg = (
1377
+ f"Could not set this setting: {str(e)}\n"
1378
+ f"The Preset is no more compatible with the plugin {plug_type}"
1379
+ )
864
1380
  logger.warning(mssg)
865
1381
  self.splash_sc.showMessage(mssg)
866
1382
  QtWidgets.QApplication.processEvents()
867
1383
 
868
1384
  mov_mod_tmp.bounds_signal[bool].connect(self.do_stuff_from_out_bounds)
869
- move_docks[-1].addWidget(move_forms[-1])
1385
+ dock.addWidget(move_forms[-1])
1386
+
870
1387
  actuators_modules.append(mov_mod_tmp)
871
1388
  return mov_mod_tmp
872
1389
 
873
- def add_move_from_extension(self, name: str, instrument_name: str,
874
- instrument_controller: Any):
875
- """ Specific method to add a DAQ_Move within the Dashboard. This Particular actuator
1390
+ def add_move_from_extension(
1391
+ self, name: str, instrument_name: str, instrument_controller: Any,
1392
+ ui_identifier = None,
1393
+ **kwargs
1394
+ ):
1395
+ """Specific method to add a DAQ_Move within the Dashboard. This Particular actuator
876
1396
  should be defined in the plugin of the extension and is used to mimic an actuator while
877
1397
  move_abs is actually triggering an action on the extension which loaded it
878
1398
 
@@ -888,8 +1408,13 @@ class DashBoard(CustomApp):
888
1408
  instrument_controller: object
889
1409
  whatever object is used to communicate between the instrument module and the extension
890
1410
  which created it
1411
+ ui_identifier: str
1412
+ One of the possible registered UI
1413
+ kwargs: named arguments to be passed to add_move
891
1414
  """
892
- actuator = self.add_move(name, None, instrument_name, [], [], [])
1415
+ actuator = self.add_move(name, None, instrument_name, [], [], [],
1416
+ ui_identifier=ui_identifier,
1417
+ **kwargs)
893
1418
  actuator.controller = instrument_controller
894
1419
  actuator.master = False
895
1420
  actuator.init_hardware_ui()
@@ -901,48 +1426,83 @@ class DashBoard(CustomApp):
901
1426
  self.actuators_modules.append(actuator)
902
1427
  self.update_module_manager()
903
1428
 
904
- def add_det(self, plug_name, plug_settings, det_docks_settings, det_docks_viewer,
905
- detector_modules, plug_type: str = None, plug_subtype: str = None) -> DAQ_Viewer:
1429
+ def add_det(
1430
+ self,
1431
+ plug_name,
1432
+ plug_settings,
1433
+ det_docks_settings,
1434
+ det_docks_viewer,
1435
+ detector_modules,
1436
+ plug_type: str = None,
1437
+ plug_subtype: str = None,
1438
+ ) -> DAQ_Viewer:
906
1439
  if plug_type is None:
907
- plug_type = plug_settings.child('main_settings', 'DAQ_type').value()
1440
+ plug_type = plug_settings.child("main_settings", "DAQ_type").value()
908
1441
  if plug_subtype is None:
909
- plug_subtype = plug_settings.child('main_settings', 'detector_type').value()
1442
+ plug_subtype = plug_settings.child("main_settings", "detector_type").value()
910
1443
  det_docks_settings.append(Dock(plug_name + " settings", size=(150, 250)))
911
1444
  det_docks_viewer.append(Dock(plug_name + " viewer", size=(350, 350)))
912
1445
  if len(detector_modules) == 0:
913
- self.logger_dock.area.addDock(det_docks_settings[-1], 'bottom')
1446
+ self.logger_dock.area.addDock(det_docks_settings[-1], "bottom")
914
1447
  # dockarea of the logger dock
915
1448
  else:
916
- self.dockarea.addDock(det_docks_settings[-1], 'right',
917
- detector_modules[-1].viewer_docks[-1])
918
- self.dockarea.addDock(det_docks_viewer[-1], 'right', det_docks_settings[-1])
919
- det_mod_tmp = DAQ_Viewer(self.dockarea, title=plug_name, daq_type=plug_type,
920
- dock_settings=det_docks_settings[-1],
921
- dock_viewer=det_docks_viewer[-1])
1449
+ self.dockarea.addDock(
1450
+ det_docks_settings[-1], "right", detector_modules[-1].viewer_docks[-1]
1451
+ )
1452
+ self.dockarea.addDock(det_docks_viewer[-1], "right", det_docks_settings[-1])
1453
+ det_mod_tmp = DAQ_Viewer(
1454
+ self.dockarea,
1455
+ title=plug_name,
1456
+ daq_type=plug_type,
1457
+ dock_settings=det_docks_settings[-1],
1458
+ dock_viewer=det_docks_viewer[-1],
1459
+ )
922
1460
  QtWidgets.QApplication.processEvents()
923
1461
  det_mod_tmp.detector = plug_subtype
924
1462
  QtWidgets.QApplication.processEvents()
925
- det_mod_tmp.manage_ui_actions('quit', 'setEnabled', False)
1463
+ det_mod_tmp.manage_ui_actions("quit", "setEnabled", False)
926
1464
 
927
1465
  if plug_settings is not None:
928
1466
  try:
929
1467
  putils.set_param_from_param(det_mod_tmp.settings, plug_settings)
930
1468
  except KeyError as e:
931
- mssg = f'Could not set this setting: {str(e)}\n' \
932
- f'The Preset is no more compatible with the plugin {plug_subtype}'
1469
+ mssg = (
1470
+ f"Could not set this setting: {str(e)}\n"
1471
+ f"The Preset is no more compatible with the plugin {plug_subtype}"
1472
+ )
933
1473
  logger.warning(mssg)
934
1474
  self.splash_sc.showMessage(mssg)
935
1475
 
936
1476
  detector_modules.append(det_mod_tmp)
937
1477
  return det_mod_tmp
938
1478
 
939
- def add_det_from_extension(self, name: str, daq_type: str, instrument_name: str,
940
- instrument_controller: Any):
941
- """ Specific method to add a DAQ_Viewer within the Dashboard. This Particular detector
1479
+ def override_det_from_extension(self, overriden_grabbers: Sequence[str] = None):
1480
+ """(Experimental) If an extension adding detectors within the Dashboard need to,
1481
+ it could call this method.
1482
+
1483
+ Then if some other extension trigger a grab from it, the request of a grab won't be done twice
1484
+
1485
+ Parameters
1486
+ ----------
1487
+ overriden_grabbers: Sequence[str]
1488
+ sequence of detector names whose corresponding modules should set their
1489
+ attribute override_grab_from_extension to True.
1490
+ """
1491
+ if overriden_grabbers is not None:
1492
+ for mod_name in overriden_grabbers:
1493
+ mod = self.modules_manager.get_mod_from_name(mod_name, "det")
1494
+ if mod is not None:
1495
+ mod.override_grab_from_extension = True
1496
+
1497
+ def add_det_from_extension(
1498
+ self, name: str, daq_type: str, instrument_name: str, instrument_controller: Any
1499
+ ):
1500
+ """Specific method to add a DAQ_Viewer within the Dashboard. This Particular detector
942
1501
  should be defined in the plugin of the extension and is used to mimic a grab while data
943
1502
  are actually coming from the extension which loaded it
944
1503
 
945
1504
  For an exemple, see the pymodaq_plugins_datamixer plugin and its DataMixer extension
1505
+ or the DAQ_PID extension
946
1506
 
947
1507
  Parameters
948
1508
  ----------
@@ -957,9 +1517,9 @@ class DashBoard(CustomApp):
957
1517
  whatever object is used to communicate between the instrument module and the extension
958
1518
  which created it
959
1519
  """
960
- detector = self.add_det(name, None, [], [], [],
961
- plug_type=daq_type,
962
- plug_subtype=instrument_name)
1520
+ detector = self.add_det(
1521
+ name, None, [], [], [], plug_type=daq_type, plug_subtype=instrument_name
1522
+ )
963
1523
  detector.controller = instrument_controller
964
1524
  detector.master = False
965
1525
  detector.init_hardware_ui()
@@ -973,33 +1533,35 @@ class DashBoard(CustomApp):
973
1533
 
974
1534
  def update_module_manager(self):
975
1535
  if self.modules_manager is None:
976
- self.modules_manager = ModulesManager(self.detector_modules, self.actuators_modules)
1536
+ self.modules_manager = ModulesManager(
1537
+ self.detector_modules, self.actuators_modules, parent_name="Dashboard"
1538
+ )
977
1539
  else:
978
1540
  self.modules_manager.actuators_all = self.actuators_modules
979
1541
  self.modules_manager.detectors_all = self.detector_modules
980
1542
 
981
1543
  def set_file_preset(self, filename) -> Tuple[List[DAQ_Move], List[DAQ_Viewer]]:
982
1544
  """
983
- Set a file managers from the converted xml file given by the filename parameter.
1545
+ Set a file managers from the converted xml file given by the filename parameter.
984
1546
 
985
1547
 
986
- =============== =========== ===================================================
987
- **Parameters** **Type** **Description**
988
- *filename* string the name of the xml file to be converted/treated
989
- =============== =========== ===================================================
1548
+ =============== =========== ===================================================
1549
+ **Parameters** **Type** **Description**
1550
+ *filename* string the name of the xml file to be converted/treated
1551
+ =============== =========== ===================================================
990
1552
 
991
- Returns
992
- -------
993
- (Object list, Object list) tuple
994
- The updated (Move modules list, Detector modules list).
1553
+ Returns
1554
+ -------
1555
+ (Object list, Object list) tuple
1556
+ The updated (Move modules list, Detector modules list).
995
1557
 
996
- """
1558
+ """
997
1559
  actuators_modules = []
998
1560
  detector_modules = []
999
1561
  if not isinstance(filename, Path):
1000
1562
  filename = Path(filename)
1001
1563
 
1002
- if filename.suffix == '.xml':
1564
+ if filename.suffix == ".xml":
1003
1565
  self.preset_file = filename
1004
1566
  self.preset_manager.set_file_preset(filename, show=False)
1005
1567
  move_docks = []
@@ -1010,32 +1572,47 @@ class DashBoard(CustomApp):
1010
1572
  # ################################################################
1011
1573
  # ##### sort plugins by IDs and within the same IDs by Master and Slave status
1012
1574
  plugins = []
1013
- plugins += [{'type': 'move', 'value': child} for child in
1014
- self.preset_manager.preset_params.child('Moves').children()]
1015
- plugins += [{'type': 'det', 'value': child} for child in
1016
- self.preset_manager.preset_params.child('Detectors').children()]
1017
-
1575
+ plugins += [
1576
+ {"type": "move", "value": child}
1577
+ for child in self.preset_manager.preset_params.child("Moves").children()
1578
+ ]
1579
+ plugins += [
1580
+ {"type": "det", "value": child}
1581
+ for child in self.preset_manager.preset_params.child(
1582
+ "Detectors"
1583
+ ).children()
1584
+ ]
1018
1585
  for plug in plugins:
1019
- if plug["type"] == 'det':
1020
- plug['ID'] = plug['value']['params', 'detector_settings', 'controller_ID']
1021
- plug['status'] = plug['value']['params', 'detector_settings',
1022
- 'controller_status']
1586
+ if plug["type"] == "det":
1587
+ try:
1588
+ plug["ID"] = plug["value"][
1589
+ "params", "detector_settings", "controller_ID"
1590
+ ]
1591
+ plug["status"] = plug["value"][
1592
+ "params", "detector_settings", "controller_status"
1593
+ ]
1594
+ except KeyError as e:
1595
+ raise DetectorError
1023
1596
  else:
1024
- plug['ID'] = plug['value']['params', 'move_settings',
1025
- 'multiaxes', 'controller_ID']
1026
- plug['status'] = plug['value'][
1027
- 'params', 'move_settings', 'multiaxes', 'multi_status']
1028
-
1029
-
1030
- IDs = list(set([plug['ID'] for plug in plugins]))
1597
+ try:
1598
+ plug["ID"] = plug["value"][
1599
+ "params", "move_settings", "multiaxes", "controller_ID"
1600
+ ]
1601
+ plug["status"] = plug["value"][
1602
+ "params", "move_settings", "multiaxes", "multi_status"
1603
+ ]
1604
+ except KeyError as e:
1605
+ raise ActuatorError
1606
+
1607
+ IDs = list(set([plug["ID"] for plug in plugins]))
1031
1608
  # %%
1032
1609
  plugins_sorted = []
1033
1610
  for id in IDs:
1034
1611
  plug_Ids = []
1035
1612
  for plug in plugins:
1036
- if plug['ID'] == id:
1613
+ if plug["ID"] == id:
1037
1614
  plug_Ids.append(plug)
1038
- plug_Ids.sort(key=lambda status: status['status'])
1615
+ plug_Ids.sort(key=lambda status: status["status"])
1039
1616
  plugins_sorted.append(plug_Ids)
1040
1617
  #################################################################
1041
1618
  #######################
@@ -1043,37 +1620,50 @@ class DashBoard(CustomApp):
1043
1620
  ind_det = -1
1044
1621
  for plug_IDs in plugins_sorted:
1045
1622
  for ind_plugin, plugin in enumerate(plug_IDs):
1046
- plug_name = plugin['value'].child('name').value()
1047
- plug_init = plugin['value'].child('init').value()
1048
- plug_settings = plugin['value'].child('params')
1623
+ plug_name = plugin["value"].child("name").value()
1624
+ plug_init = plugin["value"].child("init").value()
1625
+ plug_settings = plugin["value"].child("params")
1049
1626
  self.splash_sc.showMessage(
1050
- 'Loading {:s} module: {:s}'.format(plugin['type'], plug_name))
1051
-
1052
- if plugin['type'] == 'move':
1053
- plug_type = plug_settings.child('main_settings', 'move_type').value()
1054
- self.add_move(plug_name, plug_settings, plug_type, move_docks, move_forms,
1055
- actuators_modules)
1627
+ "Loading {:s} module: {:s}".format(plugin["type"], plug_name)
1628
+ )
1629
+
1630
+ if plugin["type"] == "move":
1631
+ plug_type = plug_settings.child(
1632
+ "main_settings", "move_type"
1633
+ ).value()
1634
+ self.add_move(
1635
+ plug_name,
1636
+ plug_settings,
1637
+ plug_type,
1638
+ move_docks,
1639
+ move_forms,
1640
+ actuators_modules,
1641
+ )
1056
1642
 
1057
1643
  if ind_plugin == 0: # should be a master type plugin
1058
- if plugin['status'] != "Master":
1059
- raise MasterSlaveError(f'The instrument {plug_name} should'
1060
- f' be defined as Master')
1644
+ if plugin["status"] != "Master":
1645
+ raise MasterSlaveError(
1646
+ f"The instrument {plug_name} should"
1647
+ f" be defined as Master"
1648
+ )
1061
1649
  if plug_init:
1062
1650
  actuators_modules[-1].init_hardware_ui()
1063
1651
  QtWidgets.QApplication.processEvents()
1064
1652
  self.poll_init(actuators_modules[-1])
1065
1653
  QtWidgets.QApplication.processEvents()
1066
1654
  master_controller = actuators_modules[-1].controller
1067
- elif plugin['status'] == "Master" and len(plug_IDs) > 1:
1655
+ elif plugin["status"] == "Master" and len(plug_IDs) > 1:
1068
1656
  raise MasterSlaveError(
1069
- f'The instrument {plug_name} defined as Master has to be '
1070
- f'initialized (init checked in the preset) in order to init '
1071
- f'its associated slave instrument'
1657
+ f"The instrument {plug_name} defined as Master has to be "
1658
+ f"initialized (init checked in the preset) in order to init "
1659
+ f"its associated slave instrument"
1072
1660
  )
1073
1661
  else:
1074
- if plugin['status'] != "Slave":
1075
- raise MasterSlaveError(f'The instrument {plug_name} should'
1076
- f' be defined as slave')
1662
+ if plugin["status"] != "Slave":
1663
+ raise MasterSlaveError(
1664
+ f"The instrument {plug_name} should"
1665
+ f" be defined as slave"
1666
+ )
1077
1667
  if plug_init:
1078
1668
  actuators_modules[-1].controller = master_controller
1079
1669
  actuators_modules[-1].init_hardware_ui()
@@ -1082,30 +1672,39 @@ class DashBoard(CustomApp):
1082
1672
  QtWidgets.QApplication.processEvents()
1083
1673
  else:
1084
1674
  ind_det += 1
1085
- self.add_det(plug_name, plug_settings, det_docks_settings, det_docks_viewer,
1086
- detector_modules)
1675
+ self.add_det(
1676
+ plug_name,
1677
+ plug_settings,
1678
+ det_docks_settings,
1679
+ det_docks_viewer,
1680
+ detector_modules,
1681
+ )
1087
1682
  QtWidgets.QApplication.processEvents()
1088
1683
 
1089
1684
  if ind_plugin == 0: # should be a master type plugin
1090
- if plugin['status'] != "Master":
1091
- raise MasterSlaveError(f'The instrument {plug_name} should'
1092
- f' be defined as Master')
1685
+ if plugin["status"] != "Master":
1686
+ raise MasterSlaveError(
1687
+ f"The instrument {plug_name} should"
1688
+ f" be defined as Master"
1689
+ )
1093
1690
  if plug_init:
1094
1691
  detector_modules[-1].init_hardware_ui()
1095
1692
  QtWidgets.QApplication.processEvents()
1096
1693
  self.poll_init(detector_modules[-1])
1097
1694
  QtWidgets.QApplication.processEvents()
1098
1695
  master_controller = detector_modules[-1].controller
1099
- elif plugin['status'] == "Master" and len(plug_IDs) > 1:
1696
+ elif plugin["status"] == "Master" and len(plug_IDs) > 1:
1100
1697
  raise MasterSlaveError(
1101
- f'The instrument {plug_name} defined as Master has to be '
1102
- f'initialized (init checked in the preset) in order to init '
1103
- f'its associated slave instrument'
1698
+ f"The instrument {plug_name} defined as Master has to be "
1699
+ f"initialized (init checked in the preset) in order to init "
1700
+ f"its associated slave instrument"
1104
1701
  )
1105
1702
  else:
1106
- if plugin['status'] != "Slave":
1107
- raise MasterSlaveError(f'The instrument {plug_name} should'
1108
- f' be defined as Slave')
1703
+ if plugin["status"] != "Slave":
1704
+ raise MasterSlaveError(
1705
+ f"The instrument {plug_name} should"
1706
+ f" be defined as Slave"
1707
+ )
1109
1708
  if plug_init:
1110
1709
  detector_modules[-1].controller = master_controller
1111
1710
  detector_modules[-1].init_hardware_ui()
@@ -1113,23 +1712,27 @@ class DashBoard(CustomApp):
1113
1712
  self.poll_init(detector_modules[-1])
1114
1713
  QtWidgets.QApplication.processEvents()
1115
1714
 
1116
- detector_modules[-1].settings.child('main_settings', 'overshoot').show()
1117
- detector_modules[-1].overshoot_signal[bool].connect(self.stop_moves_from_overshoot)
1715
+ detector_modules[-1].settings.child(
1716
+ "main_settings", "overshoot"
1717
+ ).show()
1718
+ detector_modules[-1].overshoot_signal[bool].connect(
1719
+ self.stop_moves_from_overshoot
1720
+ )
1118
1721
 
1119
1722
  QtWidgets.QApplication.processEvents()
1120
1723
  # restore dock state if saved
1121
1724
 
1122
1725
  self.title = self.preset_file.stem
1123
- path = layout_path.joinpath(self.title + '.dock')
1726
+ path = layout_path.joinpath(self.title + ".dock")
1124
1727
  if path.is_file():
1125
1728
  self.load_layout_state(path)
1126
1729
 
1127
- self.mainwindow.setWindowTitle(f'PyMoDAQ Dashboard: {self.title}')
1730
+ self.mainwindow.setWindowTitle(f"PyMoDAQ Dashboard: {self.title}")
1128
1731
  if self.pid_module is not None:
1129
1732
  self.pid_module.set_module_manager(detector_modules, actuators_modules)
1130
1733
  return actuators_modules, detector_modules
1131
1734
  else:
1132
- logger.error('Invalid file selected')
1735
+ logger.error("Invalid file selected")
1133
1736
  return actuators_modules, detector_modules
1134
1737
 
1135
1738
  def poll_init(self, module):
@@ -1147,11 +1750,13 @@ class DashBoard(CustomApp):
1147
1750
  if not isinstance(filename, Path):
1148
1751
  filename = Path(filename)
1149
1752
  try:
1150
- if filename.suffix == '.xml':
1753
+ if filename.suffix == ".xml":
1151
1754
  file = filename.stem
1152
- self.settings.child('loaded_files', 'roi_file').setValue(file)
1153
- self.update_status('ROI configuration ({}) has been loaded'.format(file),
1154
- log_type='log')
1755
+ self.settings.child("loaded_files", "roi_file").setValue(file)
1756
+ self.update_status(
1757
+ "ROI configuration ({}) has been loaded".format(file),
1758
+ log_type="log",
1759
+ )
1155
1760
  self.roi_saver.set_file_roi(filename, show=False)
1156
1761
 
1157
1762
  except Exception as e:
@@ -1161,11 +1766,11 @@ class DashBoard(CustomApp):
1161
1766
  if not isinstance(filename, Path):
1162
1767
  filename = Path(filename)
1163
1768
  ext = filename.suffix
1164
- if ext == '.xml':
1769
+ if ext == ".xml":
1165
1770
  self.remote_file = filename
1166
1771
  self.remote_manager.remote_changed.connect(self.activate_remote)
1167
1772
  self.remote_manager.set_file_remote(filename, show=False)
1168
- self.settings.child('loaded_files', 'remote_file').setValue(filename)
1773
+ self.settings.child("loaded_files", "remote_file").setValue(filename)
1169
1774
  self.remote_manager.set_remote_configuration()
1170
1775
  self.remote_dock.addWidget(self.remote_manager.remote_settings_tree)
1171
1776
  self.remote_dock.setVisible(True)
@@ -1187,25 +1792,31 @@ class DashBoard(CustomApp):
1187
1792
  module_name=module, module_type=module_type)
1188
1793
 
1189
1794
  """
1190
- if remote_action['action_type'] == 'shortcut':
1191
- if remote_action['action_name'] not in self.shortcuts:
1192
- self.shortcuts[remote_action['action_name']] = \
1193
- QtWidgets.QShortcut(
1194
- QtGui.QKeySequence(remote_action['action_dict']['shortcut']), self.dockarea)
1195
- self.activate_shortcut(self.shortcuts[remote_action['action_name']],
1196
- remote_action['action_dict'],
1197
- activate=remote_action['action_dict']['activated'])
1198
-
1199
- elif remote_action['action_type'] == 'joystick':
1795
+ if remote_action["action_type"] == "shortcut":
1796
+ if remote_action["action_name"] not in self.shortcuts:
1797
+ self.shortcuts[remote_action["action_name"]] = QtWidgets.QShortcut(
1798
+ QtGui.QKeySequence(remote_action["action_dict"]["shortcut"]),
1799
+ self.dockarea,
1800
+ )
1801
+ self.activate_shortcut(
1802
+ self.shortcuts[remote_action["action_name"]],
1803
+ remote_action["action_dict"],
1804
+ activate=remote_action["action_dict"]["activated"],
1805
+ )
1806
+
1807
+ elif remote_action["action_type"] == "joystick":
1200
1808
  if not self.ispygame_init:
1201
1809
  self.init_pygame()
1202
1810
 
1203
- if remote_action['action_name'] not in self.joysticks:
1204
- self.joysticks[remote_action['action_name']] = remote_action['action_dict']
1811
+ if remote_action["action_name"] not in self.joysticks:
1812
+ self.joysticks[remote_action["action_name"]] = remote_action[
1813
+ "action_dict"
1814
+ ]
1205
1815
 
1206
1816
  def init_pygame(self):
1207
1817
  try:
1208
1818
  import pygame
1819
+
1209
1820
  self.pygame = pygame
1210
1821
  pygame.init()
1211
1822
  pygame.joystick.init()
@@ -1213,15 +1824,15 @@ class DashBoard(CustomApp):
1213
1824
  self.joysticks_obj = []
1214
1825
  for ind in range(joystick_count):
1215
1826
  self.joysticks_obj.append(dict(obj=pygame.joystick.Joystick(ind)))
1216
- self.joysticks_obj[-1]['obj'].init()
1217
- self.joysticks_obj[-1]['id'] = self.joysticks_obj[-1]['obj'].get_id()
1827
+ self.joysticks_obj[-1]["obj"].init()
1828
+ self.joysticks_obj[-1]["id"] = self.joysticks_obj[-1]["obj"].get_id()
1218
1829
 
1219
1830
  self.remote_timer.timeout.connect(self.pygame_loop)
1220
1831
  self.ispygame_init = True
1221
1832
  self.remote_timer.start(10)
1222
1833
 
1223
1834
  except ImportError as e:
1224
- logger.warning('No pygame module installed. Needed for joystick control')
1835
+ logger.warning("No pygame module installed. Needed for joystick control")
1225
1836
 
1226
1837
  def pygame_loop(self):
1227
1838
  """
@@ -1235,24 +1846,33 @@ class DashBoard(CustomApp):
1235
1846
  """
1236
1847
 
1237
1848
  for action_dict in self.joysticks.values():
1238
- if action_dict['activated'] and action_dict['actionner_type'].lower() == 'axis':
1239
- if action_dict['module_type'] == 'act':
1240
- joy = utils.find_dict_in_list_from_key_val(self.joysticks_obj, 'id',
1241
- action_dict['joystickID'])
1242
- val = joy['obj'].get_axis(action_dict['actionnerID'])
1849
+ if (
1850
+ action_dict["activated"]
1851
+ and action_dict["actionner_type"].lower() == "axis"
1852
+ ):
1853
+ if action_dict["module_type"] == "act":
1854
+ joy = utils.find_dict_in_list_from_key_val(
1855
+ self.joysticks_obj, "id", action_dict["joystickID"]
1856
+ )
1857
+ val = joy["obj"].get_axis(action_dict["actionnerID"])
1243
1858
  if abs(val) > 1e-4:
1244
1859
  module = self.modules_manager.get_mod_from_name(
1245
- action_dict['module_name'],
1246
- mod=action_dict['module_type'])
1247
- action = getattr(module, action_dict['action'])
1860
+ action_dict["module_name"], mod=action_dict["module_type"]
1861
+ )
1862
+ action = getattr(module, action_dict["action"])
1248
1863
  if module.move_done_bool:
1249
- action(val * 1 * module.settings.child(
1250
- 'move_settings', 'epsilon').value())
1864
+ action(
1865
+ val
1866
+ * 1
1867
+ * module.settings.child(
1868
+ "move_settings", "epsilon"
1869
+ ).value()
1870
+ )
1251
1871
 
1252
1872
  # # For other actions use the event loop
1253
1873
  for event in self.pygame.event.get(): # User did something.
1254
1874
  selection = dict([])
1255
- if 'joy' in event.dict:
1875
+ if "joy" in event.dict:
1256
1876
  selection.update(dict(joy=event.joy))
1257
1877
  if event.type == self.pygame.JOYBUTTONDOWN:
1258
1878
  selection.update(dict(button=event.button))
@@ -1262,24 +1882,28 @@ class DashBoard(CustomApp):
1262
1882
  selection.update(dict(hat=event.hat, value=event.value))
1263
1883
  if len(selection) > 1:
1264
1884
  for action_dict in self.joysticks.values():
1265
- if action_dict['activated']:
1885
+ if action_dict["activated"]:
1266
1886
  module = self.modules_manager.get_mod_from_name(
1267
- action_dict['module_name'],
1268
- mod=action_dict['module_type'])
1269
- if action_dict['module_type'] == 'det':
1270
- action = getattr(module, action_dict['action'])
1887
+ action_dict["module_name"], mod=action_dict["module_type"]
1888
+ )
1889
+ if action_dict["module_type"] == "det":
1890
+ action = getattr(module, action_dict["action"])
1271
1891
  else:
1272
- action = getattr(module, action_dict['action'])
1273
-
1274
- if action_dict['joystickID'] == selection['joy']:
1275
- if (action_dict['actionner_type'].lower() == 'button' and
1276
- 'button' in selection):
1277
- if action_dict['actionnerID'] == selection['button']:
1892
+ action = getattr(module, action_dict["action"])
1893
+
1894
+ if action_dict["joystickID"] == selection["joy"]:
1895
+ if (
1896
+ action_dict["actionner_type"].lower() == "button"
1897
+ and "button" in selection
1898
+ ):
1899
+ if action_dict["actionnerID"] == selection["button"]:
1278
1900
  action()
1279
- elif (action_dict['actionner_type'].lower() == 'hat' and
1280
- 'hat' in selection):
1281
- if action_dict['actionnerID'] == selection['hat']:
1282
- action(selection['value'])
1901
+ elif (
1902
+ action_dict["actionner_type"].lower() == "hat"
1903
+ and "hat" in selection
1904
+ ):
1905
+ if action_dict["actionnerID"] == selection["hat"]:
1906
+ action(selection["value"])
1283
1907
 
1284
1908
  QtWidgets.QApplication.processEvents()
1285
1909
 
@@ -1299,8 +1923,7 @@ class DashBoard(CustomApp):
1299
1923
 
1300
1924
  """
1301
1925
  if activate:
1302
- shortcut.activated.connect(
1303
- self.create_activated_shortcut(action))
1926
+ shortcut.activated.connect(self.create_activated_shortcut(action))
1304
1927
  else:
1305
1928
  try:
1306
1929
  shortcut.activated.disconnect()
@@ -1308,40 +1931,43 @@ class DashBoard(CustomApp):
1308
1931
  pass
1309
1932
 
1310
1933
  def create_activated_shortcut(self, action):
1311
- module = self.modules_manager.get_mod_from_name(action['module_name'],
1312
- mod=action['module_type'])
1313
- if action['module_type'] == 'det':
1314
- return lambda: getattr(module, action['action'])()
1934
+ module = self.modules_manager.get_mod_from_name(
1935
+ action["module_name"], mod=action["module_type"]
1936
+ )
1937
+ if action["module_type"] == "det":
1938
+ return lambda: getattr(module, action["action"])()
1315
1939
  else:
1316
- return lambda: getattr(module, action['action'])()
1940
+ return lambda: getattr(module, action["action"])()
1317
1941
 
1318
1942
  def set_overshoot_configuration(self, filename):
1319
1943
  try:
1320
1944
  if not isinstance(filename, Path):
1321
1945
  filename = Path(filename)
1322
1946
 
1323
- if filename.suffix == '.xml':
1947
+ if filename.suffix == ".xml":
1324
1948
  file = filename.stem
1325
- self.settings.child('loaded_files', 'overshoot_file').setValue(file)
1326
- self.update_status('Overshoot configuration ({}) has been loaded'.format(file),
1327
- log_type='log')
1949
+ self.settings.child("loaded_files", "overshoot_file").setValue(file)
1950
+ self.update_status(
1951
+ "Overshoot configuration ({}) has been loaded".format(file),
1952
+ log_type="log",
1953
+ )
1328
1954
  self.overshoot_manager.set_file_overshoot(filename, show=False)
1329
- self.set_action_enabled('activate_overshoot', True)
1330
- self.set_action_checked('activate_overshoot', False)
1331
- self.get_action('activate_overshoot').trigger()
1955
+ self.set_action_enabled("activate_overshoot", True)
1956
+ self.set_action_checked("activate_overshoot", False)
1957
+ self.get_action("activate_overshoot").trigger()
1332
1958
 
1333
1959
  except Exception as e:
1334
1960
  logger.exception(str(e))
1335
1961
 
1336
1962
  def activate_overshoot(self, status: bool):
1337
1963
  try:
1338
- self.overshoot_manager.activate_overshoot(self.detector_modules,
1339
- self.actuators_modules,
1340
- status)
1964
+ self.overshoot_manager.activate_overshoot(
1965
+ self.detector_modules, self.actuators_modules, status
1966
+ )
1341
1967
  except Exception as e:
1342
- logger.warning(f'Could not load the overshoot file:\n{str(e)}')
1343
- self.set_action_checked('activate_overshoot', False)
1344
- self.set_action_enabled('activate_overshoot', False)
1968
+ logger.warning(f"Could not load the overshoot file:\n{str(e)}")
1969
+ self.set_action_checked("activate_overshoot", False)
1970
+ self.set_action_enabled("activate_overshoot", False)
1345
1971
 
1346
1972
  @property
1347
1973
  def move_modules(self):
@@ -1352,34 +1978,34 @@ class DashBoard(CustomApp):
1352
1978
 
1353
1979
  def set_preset_mode(self, filename):
1354
1980
  """
1355
- | Set the managers mode from the given filename.
1356
- |
1357
- | In case of "mock" or "canon" move, set the corresponding managers calling
1358
- set_(*)_preset procedure.
1359
- |
1360
- | Else set the managers file using set_file_preset function.
1361
- | Once done connect the move and detector modules to logger to recipe/transmit
1362
- informations.
1363
-
1364
- Finally update DAQ_scan_settings tree with :
1365
- * Detectors
1366
- * Move
1367
- * plot_form.
1368
-
1369
- =============== =========== =============================================
1370
- **Parameters** **Type** **Description**
1371
- *filename* string the name of the managers file to be treated
1372
- =============== =========== =============================================
1373
-
1374
- See Also
1375
- --------
1376
- set_Mock_preset, set_canon_preset, set_file_preset, add_status, update_status
1981
+ | Set the managers mode from the given filename.
1982
+ |
1983
+ | In case of "mock" or "canon" move, set the corresponding managers calling
1984
+ set_(*)_preset procedure.
1985
+ |
1986
+ | Else set the managers file using set_file_preset function.
1987
+ | Once done connect the move and detector modules to logger to recipe/transmit
1988
+ informations.
1989
+
1990
+ Finally update DAQ_scan_settings tree with :
1991
+ * Detectors
1992
+ * Move
1993
+ * plot_form.
1994
+
1995
+ =============== =========== =============================================
1996
+ **Parameters** **Type** **Description**
1997
+ *filename* string the name of the managers file to be treated
1998
+ =============== =========== =============================================
1999
+
2000
+ See Also
2001
+ --------
2002
+ set_Mock_preset, set_canon_preset, set_file_preset, add_status, update_status
1377
2003
  """
1378
2004
  try:
1379
2005
  if not isinstance(filename, Path):
1380
2006
  filename = Path(filename)
1381
2007
 
1382
- self.get_action('preset_list').setCurrentText(filename.stem)
2008
+ self.get_action("preset_list").setCurrentText(filename.stem)
1383
2009
 
1384
2010
  self.mainwindow.setVisible(False)
1385
2011
  for area in self.dockarea.tempAreas:
@@ -1388,12 +2014,12 @@ class DashBoard(CustomApp):
1388
2014
  self.splash_sc.show()
1389
2015
  QtWidgets.QApplication.processEvents()
1390
2016
  self.splash_sc.raise_()
1391
- self.splash_sc.showMessage('Loading Modules, please wait')
2017
+ self.splash_sc.showMessage("Loading Modules, please wait")
1392
2018
  QtWidgets.QApplication.processEvents()
1393
2019
  self.clear_move_det_controllers()
1394
2020
  QtWidgets.QApplication.processEvents()
1395
2021
 
1396
- logger.info(f'Loading Preset file: {filename}')
2022
+ logger.info(f"Loading Preset file: {filename}")
1397
2023
 
1398
2024
  try:
1399
2025
  actuators_modules, detector_modules = self.set_file_preset(filename)
@@ -1402,17 +2028,29 @@ class DashBoard(CustomApp):
1402
2028
  self.mainwindow.setVisible(True)
1403
2029
  for area in self.dockarea.tempAreas:
1404
2030
  area.window().setVisible(True)
1405
- messagebox(text=f'{str(error)}\nQuitting the application...',
1406
- title='Incompatibility')
2031
+ messagebox(
2032
+ severity="critical",
2033
+ title="Preset loading error",
2034
+ text=f"""
2035
+ <p>{error}</p>
2036
+ <p>This error may be related to:</p>
2037
+ <p>Saved preset file is not compatible anymore.</p>
2038
+ <p>Please recreate the preset at <b>{filename}</b>.</p>
2039
+ """,
2040
+ )
1407
2041
  logger.exception(str(error))
1408
2042
 
1409
2043
  self.quit_fun()
1410
2044
  return
1411
2045
 
1412
2046
  if not (not actuators_modules and not detector_modules):
1413
- self.update_status('Preset mode ({}) has been loaded'.format(filename.name),
1414
- log_type='log')
1415
- self.settings.child('loaded_files', 'preset_file').setValue(filename.name)
2047
+ self.update_status(
2048
+ "Preset mode ({}) has been loaded".format(filename.name),
2049
+ log_type="log",
2050
+ )
2051
+ self.settings.child("loaded_files", "preset_file").setValue(
2052
+ filename.name
2053
+ )
1416
2054
  self.actuators_modules = actuators_modules
1417
2055
  self.detector_modules = detector_modules
1418
2056
 
@@ -1421,7 +2059,8 @@ class DashBoard(CustomApp):
1421
2059
  #####################################################
1422
2060
  self.overshoot_manager = OvershootManager(
1423
2061
  det_modules=[det.title for det in detector_modules],
1424
- actuators_modules=[move.title for move in actuators_modules])
2062
+ actuators_modules=[move.title for move in actuators_modules],
2063
+ )
1425
2064
  # load overshoot if present
1426
2065
  file = filename.name
1427
2066
  path = overshoot_path.joinpath(file)
@@ -1430,7 +2069,8 @@ class DashBoard(CustomApp):
1430
2069
 
1431
2070
  self.remote_manager = RemoteManager(
1432
2071
  actuators=[move.title for move in actuators_modules],
1433
- detectors=[det.title for det in detector_modules])
2072
+ detectors=[det.title for det in detector_modules],
2073
+ )
1434
2074
  # load remote file if present
1435
2075
  file = filename.name
1436
2076
  path = remote_path.joinpath(file)
@@ -1457,8 +2097,8 @@ class DashBoard(CustomApp):
1457
2097
  self.pid_window.show()
1458
2098
 
1459
2099
  self.load_preset_menu.setEnabled(False)
1460
- self.set_action_enabled('load_preset', False)
1461
- self.set_action_enabled('preset_list', False)
2100
+ self.set_action_enabled("load_preset", False)
2101
+ self.set_action_enabled("preset_list", False)
1462
2102
  self.overshoot_menu.setEnabled(True)
1463
2103
  self.roi_menu.setEnabled(True)
1464
2104
  self.remote_menu.setEnabled(True)
@@ -1469,37 +2109,45 @@ class DashBoard(CustomApp):
1469
2109
 
1470
2110
  self.preset_loaded_signal.emit(True)
1471
2111
 
1472
- logger.info(f'Preset file: {filename} has been loaded')
2112
+ logger.info(f"Preset file: {filename} has been loaded")
1473
2113
 
1474
2114
  except Exception as e:
1475
2115
  logger.exception(str(e))
1476
2116
 
1477
2117
  def update_init_tree(self):
1478
2118
  for act in self.actuators_modules:
1479
- name = ''.join(act.title.split()) # remove empty spaces
1480
- if act.title not in [ac.title() for ac in putils.iter_children_params(
1481
- self.settings.child('actuators'), [])]:
1482
-
1483
- self.settings.child('actuators').addChild(
1484
- {'title': act.title, 'name': name, 'type': 'led', 'value': False})
2119
+ name = "".join(act.title.split()) # remove empty spaces
2120
+ if act.title not in [
2121
+ ac.title()
2122
+ for ac in putils.iter_children_params(
2123
+ self.settings.child("actuators"), []
2124
+ )
2125
+ ]:
2126
+ self.settings.child("actuators").addChild(
2127
+ {"title": act.title, "name": name, "type": "led", "value": False}
2128
+ )
1485
2129
  QtWidgets.QApplication.processEvents()
1486
- self.settings.child('actuators', name).setValue(act.initialized_state)
2130
+ self.settings.child("actuators", name).setValue(act.initialized_state)
1487
2131
 
1488
2132
  for det in self.detector_modules:
1489
- name = ''.join(det.title.split()) # remove empty spaces
1490
- if det.title not in [de.title() for de in putils.iter_children_params(
1491
- self.settings.child('detectors'), [])]:
1492
- self.settings.child('detectors').addChild(
1493
- {'title': det.title, 'name': name, 'type': 'led', 'value': False})
2133
+ name = "".join(det.title.split()) # remove empty spaces
2134
+ if det.title not in [
2135
+ de.title()
2136
+ for de in putils.iter_children_params(
2137
+ self.settings.child("detectors"), []
2138
+ )
2139
+ ]:
2140
+ self.settings.child("detectors").addChild(
2141
+ {"title": det.title, "name": name, "type": "led", "value": False}
2142
+ )
1494
2143
  QtWidgets.QApplication.processEvents()
1495
- self.settings.child('detectors', name).setValue(det.initialized_state)
2144
+ self.settings.child("detectors", name).setValue(det.initialized_state)
1496
2145
 
1497
2146
  def do_stuff_from_out_bounds(self, out_of_bounds: bool):
1498
-
1499
2147
  if out_of_bounds:
1500
- logger.warning(f'Some actuators reached their bounds')
2148
+ logger.warning(f"Some actuators reached their bounds")
1501
2149
  if self.scan_module is not None:
1502
- logger.warning(f'Stopping the DAQScan for out of bounds')
2150
+ logger.warning(f"Stopping the DAQScan for out of bounds")
1503
2151
  self.scan_module.stop_scan()
1504
2152
 
1505
2153
  def stop_moves_from_overshoot(self, overshoot):
@@ -1508,11 +2156,11 @@ class DashBoard(CustomApp):
1508
2156
 
1509
2157
  def stop_moves(self, *args, **kwargs):
1510
2158
  """
1511
- Foreach module of the move module object list, stop motion.
2159
+ Foreach module of the move module object list, stop motion.
1512
2160
 
1513
- See Also
1514
- --------
1515
- stop_scan, DAQ_Move_main.daq_move.stop_motion
2161
+ See Also
2162
+ --------
2163
+ stop_scan, DAQ_Move_main.daq_move.stop_motion
1516
2164
  """
1517
2165
  if self.scan_module is not None:
1518
2166
  self.scan_module.stop_scan()
@@ -1522,15 +2170,16 @@ class DashBoard(CustomApp):
1522
2170
 
1523
2171
  def show_log(self):
1524
2172
  import webbrowser
1525
- webbrowser.open(logging.getLogger('pymodaq').handlers[0].baseFilename)
1526
2173
 
1527
- def show_config(self):
2174
+ webbrowser.open(logging.getLogger("pymodaq").handlers[0].baseFilename)
2175
+
2176
+ def show_config(self, config):
1528
2177
  from pymodaq_gui.utils.widgets.tree_toml import TreeFromToml
1529
- config_tree = TreeFromToml()
2178
+
2179
+ config_tree = TreeFromToml(config)
1530
2180
  config_tree.show_dialog()
1531
2181
 
1532
2182
  def setup_docks(self):
1533
-
1534
2183
  # %% create logger dock
1535
2184
  self.logger_dock = Dock("Logger")
1536
2185
  self.logger_list = QtWidgets.QListWidget()
@@ -1541,13 +2190,15 @@ class DashBoard(CustomApp):
1541
2190
  splitter.addWidget(self.logger_list)
1542
2191
  self.logger_dock.addWidget(splitter)
1543
2192
 
1544
- self.remote_dock = Dock('Remote controls')
1545
- self.dockarea.addDock(self.remote_dock, 'top')
1546
- self.dockarea.addDock(self.logger_dock, 'above', self.remote_dock)
2193
+ self.remote_dock = Dock("Remote controls")
2194
+ self.dockarea.addDock(self.remote_dock, "top")
2195
+ self.dockarea.addDock(self.logger_dock, "above", self.remote_dock)
1547
2196
  self.logger_dock.setVisible(True)
1548
2197
 
1549
2198
  self.remote_dock.setVisible(False)
1550
- self.preset_manager = PresetManager(path=self.preset_path, extra_params=self.extra_params)
2199
+ self.preset_manager = PresetManager(
2200
+ path=self.preset_path, extra_params=self.extra_params
2201
+ )
1551
2202
 
1552
2203
  @property
1553
2204
  def menubar(self):
@@ -1555,13 +2206,13 @@ class DashBoard(CustomApp):
1555
2206
 
1556
2207
  def parameter_tree_changed(self, param, changes):
1557
2208
  """
1558
- Foreach value changed, update :
1559
- * Viewer in case of **DAQ_type** parameter name
1560
- * visibility of button in case of **show_averaging** parameter name
1561
- * visibility of naverage in case of **live_averaging** parameter name
1562
- * scale of axis **else** (in 2D pymodaq type)
2209
+ Foreach value changed, update :
2210
+ * Viewer in case of **DAQ_type** parameter name
2211
+ * visibility of button in case of **show_averaging** parameter name
2212
+ * visibility of naverage in case of **live_averaging** parameter name
2213
+ * scale of axis **else** (in 2D pymodaq type)
1563
2214
 
1564
- Once done emit the update settings signal to link the commit.
2215
+ Once done emit the update settings signal to link the commit.
1565
2216
 
1566
2217
 
1567
2218
  """
@@ -1569,15 +2220,15 @@ class DashBoard(CustomApp):
1569
2220
  for param, change, data in changes:
1570
2221
  path = self.settings.childPath(param)
1571
2222
  if path is not None:
1572
- childName = '.'.join(path)
2223
+ childName = ".".join(path)
1573
2224
  else:
1574
2225
  childName = param.name()
1575
- if change == 'childAdded':
2226
+ if change == "childAdded":
1576
2227
  pass
1577
- elif change == 'value':
1578
- if param.name() == 'log_level':
2228
+ elif change == "value":
2229
+ if param.name() == "log_level":
1579
2230
  logger.setLevel(param.value())
1580
- elif change == 'parent':
2231
+ elif change == "parent":
1581
2232
  pass
1582
2233
 
1583
2234
  def show_about(self):
@@ -1585,41 +2236,46 @@ class DashBoard(CustomApp):
1585
2236
  self.splash_sc.showMessage(
1586
2237
  f"PyMoDAQ version {get_version('pymodaq')}\n"
1587
2238
  f"Modular Acquisition with Python\n"
1588
- f"Written by Sébastien Weber")
2239
+ f"Written by Sébastien Weber"
2240
+ )
1589
2241
 
1590
2242
  def check_update(self, show=True):
1591
-
1592
2243
  try:
1593
- packages = ['pymodaq_utils', 'pymodaq_data', 'pymodaq_gui', 'pymodaq']
2244
+ packages = ["pymodaq_utils", "pymodaq_data", "pymodaq_gui", "pymodaq"]
1594
2245
  current_versions = [version_mod.parse(get_version(p)) for p in packages]
1595
- available_versions = [version_mod.parse(get_pypi_pymodaq(p)['version']) for p in packages]
2246
+ available_versions = [
2247
+ version_mod.parse(get_pypi_pymodaq(p)["version"]) for p in packages
2248
+ ]
1596
2249
  new_versions = np.greater(available_versions, current_versions)
1597
2250
  # Combine package and version information and select only the ones with a newer version available
1598
2251
 
1599
-
1600
- packages_data = np.array(list(zip(packages, current_versions, available_versions)))[new_versions]
2252
+ packages_data = np.array(
2253
+ list(zip(packages, current_versions, available_versions))
2254
+ )[new_versions]
1601
2255
 
1602
2256
  if len(packages_data) > 0:
1603
- #Create a QDialog window and different graphical components
2257
+ # Create a QDialog window and different graphical components
1604
2258
  dialog = QtWidgets.QDialog()
1605
2259
  dialog.setWindowTitle("Update check")
1606
-
2260
+
1607
2261
  vlayout = QtWidgets.QVBoxLayout()
1608
2262
 
1609
- message_label = QLabel("New versions of PyMoDAQ packages available!\nUse your package manager to update.")
2263
+ message_label = QLabel(
2264
+ "New versions of PyMoDAQ packages available!\nUse your package manager to update."
2265
+ )
1610
2266
  message_label.setAlignment(Qt.AlignCenter)
1611
-
1612
2267
 
1613
2268
  table = PymodaqUpdateTableWidget()
1614
- table.setRowCount(len(packages_data))
2269
+ table.setRowCount(len(packages_data))
1615
2270
  table.setColumnCount(3)
1616
- table.setHorizontalHeaderLabels(["Package", "Current version", "New version"])
1617
-
2271
+ table.setHorizontalHeaderLabels(
2272
+ ["Package", "Current version", "New version"]
2273
+ )
2274
+
1618
2275
  for p in packages_data:
1619
2276
  table.append_row(p[0], p[1], p[2])
1620
2277
 
1621
-
1622
- # The vlayout contains the message, the table and the buttons
2278
+ # The vlayout contains the message, the table and the buttons
1623
2279
  # and is connected to the dialog window
1624
2280
  vlayout.addWidget(message_label)
1625
2281
  vlayout.addWidget(table)
@@ -1629,7 +2285,7 @@ class DashBoard(CustomApp):
1629
2285
 
1630
2286
  else:
1631
2287
  if show:
1632
- msgBox = QtWidgets.QMessageBox()
2288
+ msgBox = QMessageBox()
1633
2289
  msgBox.setWindowTitle("Update check")
1634
2290
  msgBox.setText("Everything is up to date!")
1635
2291
  ret = msgBox.exec()
@@ -1638,48 +2294,48 @@ class DashBoard(CustomApp):
1638
2294
 
1639
2295
  return False
1640
2296
 
1641
- def show_file_attributes(self, type_info='dataset'):
2297
+ def show_file_attributes(self, type_info="dataset"):
1642
2298
  """
1643
- Switch the type_info value.
1644
-
1645
- In case of :
1646
- * *scan* : Set parameters showing top false
1647
- * *dataset* : Set parameters showing top false
1648
- * *managers* : Set parameters showing top false.
1649
- Add the save/cancel buttons to the accept/reject dialog
1650
- (to save managers parameters in a xml file).
1651
-
1652
- Finally, in case of accepted managers type info,
1653
- save the managers parameters in a xml file.
1654
-
1655
- =============== =========== ====================================
1656
- **Parameters** **Type** **Description**
1657
- *type_info* string The file type information between
1658
- * scan
1659
- * dataset
1660
- * managers
1661
- =============== =========== ====================================
2299
+ Switch the type_info value.
2300
+
2301
+ In case of :
2302
+ * *scan* : Set parameters showing top false
2303
+ * *dataset* : Set parameters showing top false
2304
+ * *managers* : Set parameters showing top false.
2305
+ Add the save/cancel buttons to the accept/reject dialog
2306
+ (to save managers parameters in a xml file).
2307
+
2308
+ Finally, in case of accepted managers type info,
2309
+ save the managers parameters in a xml file.
2310
+
2311
+ =============== =========== ====================================
2312
+ **Parameters** **Type** **Description**
2313
+ *type_info* string The file type information between
2314
+ * scan
2315
+ * dataset
2316
+ * managers
2317
+ =============== =========== ====================================
1662
2318
  """
1663
2319
  dialog = QtWidgets.QDialog()
1664
2320
  vlayout = QtWidgets.QVBoxLayout()
1665
2321
  tree = ParameterTree()
1666
2322
  tree.setMinimumWidth(400)
1667
2323
  tree.setMinimumHeight(500)
1668
- if type_info == 'scan':
2324
+ if type_info == "scan":
1669
2325
  tree.setParameters(self.scan_attributes, showTop=False)
1670
- elif type_info == 'dataset':
2326
+ elif type_info == "dataset":
1671
2327
  tree.setParameters(self.dataset_attributes, showTop=False)
1672
2328
 
1673
2329
  vlayout.addWidget(tree)
1674
2330
  dialog.setLayout(vlayout)
1675
- buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
1676
- buttonBox.addButton('Cancel', buttonBox.RejectRole)
1677
- buttonBox.addButton('Apply', buttonBox.AcceptRole)
2331
+ buttonBox = QDialogButtonBox(parent=dialog)
2332
+ buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
2333
+ buttonBox.addButton("Apply", QDialogButtonBox.ButtonRole.AcceptRole)
1678
2334
  buttonBox.rejected.connect(dialog.reject)
1679
2335
  buttonBox.accepted.connect(dialog.accept)
1680
2336
 
1681
2337
  vlayout.addWidget(buttonBox)
1682
- dialog.setWindowTitle('Fill in information about this {}'.format(type_info))
2338
+ dialog.setWindowTitle("Fill in information about this {}".format(type_info))
1683
2339
  res = dialog.exec()
1684
2340
  return res
1685
2341
 
@@ -1688,14 +2344,14 @@ class DashBoard(CustomApp):
1688
2344
 
1689
2345
  def update_status(self, txt, wait_time=0, log_type=None):
1690
2346
  """
1691
- Show the txt message in the status bar with a delay of wait_time ms.
1692
-
1693
- =============== =========== =======================
1694
- **Parameters** **Type** **Description**
1695
- *txt* string The message to show
1696
- *wait_time* int the delay of showing
1697
- *log_type* string the type of the log
1698
- =============== =========== =======================
2347
+ Show the txt message in the status bar with a delay of wait_time ms.
2348
+
2349
+ =============== =========== =======================
2350
+ **Parameters** **Type** **Description**
2351
+ *txt* string The message to show
2352
+ *wait_time* int the delay of showing
2353
+ *log_type* string the type of the log
2354
+ =============== =========== =======================
1699
2355
  """
1700
2356
  try:
1701
2357
  if log_type is not None:
@@ -1707,21 +2363,34 @@ class DashBoard(CustomApp):
1707
2363
 
1708
2364
  def main():
1709
2365
  from pymodaq_gui.utils.utils import mkQApp
2366
+ from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
1710
2367
 
2368
+ # Create application and main window
1711
2369
  app = mkQApp('Dashboard')
1712
2370
 
1713
2371
  win = QtWidgets.QMainWindow()
1714
2372
  area = DockArea()
1715
2373
  win.setCentralWidget(area)
1716
2374
  win.resize(1000, 500)
1717
- win.setWindowTitle('PyMoDAQ Dashboard')
2375
+ win.setWindowTitle("PyMoDAQ Dashboard")
2376
+
2377
+ # Command-line argument parsing
2378
+ parser = argparse.ArgumentParser(prog="dashboard", description="PyMoDAQ dashboard")
2379
+ parser.add_argument("-p", "--preset", metavar="PRESET_NAME", help="preset name to load")
2380
+ args = parser.parse_args()
1718
2381
 
1719
- prog = DashBoard(area)
2382
+ # If preset name is supplied, load dashboard with this preset
2383
+ if args.preset:
2384
+ load_dashboard_with_preset(args.preset)
1720
2385
 
1721
- win.show()
2386
+ # If no command-line arguments are supplied, start empty
2387
+ else:
2388
+ prog = DashBoard(area)
2389
+ win.show()
1722
2390
 
2391
+ # Run application
1723
2392
  app.exec()
1724
2393
 
1725
2394
 
1726
- if __name__ == '__main__':
2395
+ if __name__ == "__main__":
1727
2396
  main()