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
@@ -18,6 +18,9 @@ DAQ_1DViewer_Det_types = get_plugins('daq_1Dviewer')
18
18
  DAQ_2DViewer_Det_types = get_plugins('daq_2Dviewer')
19
19
  DAQ_NDViewer_Det_types = get_plugins('daq_NDviewer')
20
20
 
21
+ # Fixed names that will sort the plugin in remote/mock
22
+ REMOTE_ITEMS = {'LECODirector', 'TCPServer'}
23
+ MOCK_ITEMS = {}
21
24
 
22
25
  def iterative_show_pb(params):
23
26
  for param in params:
@@ -27,6 +30,136 @@ def iterative_show_pb(params):
27
30
  iterative_show_pb(param['children'])
28
31
 
29
32
 
33
+ def find_last_index(list_children:list=[], name_prefix ='',format_string='02.0f'):
34
+ # Custom function to find last available index
35
+ child_indexes = ([int(par.name()[len(name_prefix) + 1:]) for par in list_children if name_prefix in par.name()])
36
+ if child_indexes == []:
37
+ newindex = 0
38
+ else:
39
+ newindex = max(child_indexes) + 1
40
+ return f'{newindex:{format_string}}'
41
+
42
+
43
+ def categorize_items(item_list, remote_items=None, mock_items=None):
44
+ """
45
+ Core function: categorize any list of items into Mock/Plugin/Remote.
46
+
47
+ Args:
48
+ item_list: List of items to categorize
49
+ remote_items: Custom set of remote items (optional)
50
+ mock_items: Custom set of mock items (optional)
51
+
52
+ Returns: dict {category: [items]} with only non-empty categories
53
+ """
54
+ remote_items = remote_items or REMOTE_ITEMS
55
+ mock_items = mock_items or MOCK_ITEMS
56
+
57
+ categorized = {'Remote': [], 'Mock': [], 'Plugin': []}
58
+
59
+ for item in item_list:
60
+ if item in remote_items:
61
+ categorized['Remote'].append(item)
62
+ elif item in mock_items or 'mock' in item.lower():
63
+ categorized['Mock'].append(item)
64
+ else:
65
+ categorized['Plugin'].append(item)
66
+
67
+ # Return only non-empty categories
68
+ return {k: v for k, v in categorized.items() if v}
69
+
70
+
71
+ def add_category_layers(dimension_dict, remote_items=None, mock_items=None):
72
+ """
73
+ Add category layers to a dimension dictionary.
74
+ Uses categorize_items for each dimension.
75
+
76
+ Args:
77
+ dimension_dict: {dimension: [items]}
78
+
79
+ Returns: {dimension: {category: [items]}}
80
+ """
81
+ result = {}
82
+
83
+ for dimension, items in dimension_dict.items():
84
+ # Reuse the core categorization function
85
+ result[dimension] = categorize_items(items, remote_items, mock_items)
86
+
87
+ return result
88
+
89
+
90
+ def make_move_params(typ):
91
+ params = daq_move_params
92
+ iterative_show_pb(params)
93
+
94
+ parent_module = utils.find_dict_in_list_from_key_val(DAQ_Move_Stage_type, 'name', typ)
95
+ class_ = getattr(getattr(parent_module['module'], 'daq_move_' + typ),
96
+ 'DAQ_Move_' + typ)
97
+ params_hardware = getattr(class_, 'params')
98
+ iterative_show_pb(params_hardware)
99
+
100
+ for main_child in params:
101
+ if main_child['name'] == 'move_settings':
102
+ main_child['children'] = params_hardware
103
+ controller_dict = get_param_dict_from_name(params_hardware, 'controller_ID')
104
+ controller_dict['value'] = random.randint(0, 9999)
105
+
106
+ elif main_child['name'] == 'main_settings':
107
+ typ_dict = get_param_dict_from_name(main_child['children'], 'move_type')
108
+ typ_dict['value'] = typ
109
+
110
+ return params
111
+
112
+
113
+ def make_viewer_params(typ):
114
+ params = daq_viewer_params
115
+ iterative_show_pb(params)
116
+
117
+ for main_child in params:
118
+ if main_child['name'] == 'main_settings':
119
+ for child in main_child['children']:
120
+ if child['name'] == 'DAQ_type':
121
+ child['value'] = typ[0:5]
122
+ if child['name'] == 'detector_type':
123
+ child['value'] = typ[6:]
124
+ if child['name'] == 'controller_status':
125
+ child['visible'] = True
126
+
127
+ if '0D' in typ:
128
+ parent_module = utils.find_dict_in_list_from_key_val(DAQ_0DViewer_Det_types, 'name', typ[6:])
129
+ class_ = getattr(getattr(parent_module['module'], 'daq_0Dviewer_' + typ[6:]), 'DAQ_0DViewer_' + typ[6:])
130
+ elif '1D' in typ:
131
+ parent_module = utils.find_dict_in_list_from_key_val(DAQ_1DViewer_Det_types, 'name', typ[6:])
132
+ class_ = getattr(getattr(parent_module['module'], 'daq_1Dviewer_' + typ[6:]), 'DAQ_1DViewer_' + typ[6:])
133
+ elif '2D' in typ:
134
+ parent_module = utils.find_dict_in_list_from_key_val(DAQ_2DViewer_Det_types, 'name', typ[6:])
135
+ class_ = getattr(getattr(parent_module['module'], 'daq_2Dviewer_' + typ[6:]), 'DAQ_2DViewer_' + typ[6:])
136
+ elif 'ND' in typ:
137
+ parent_module = utils.find_dict_in_list_from_key_val(DAQ_NDViewer_Det_types, 'name', typ[6:])
138
+ class_ = getattr(getattr(parent_module['module'], 'daq_NDviewer_' + typ[6:]), 'DAQ_NDViewer_' + typ[6:])
139
+ for main_child in params:
140
+ if main_child['name'] == 'main_settings':
141
+ for child in main_child['children']:
142
+ if child['name'] == 'axes':
143
+ child['visible'] = True
144
+
145
+ params_hardware = getattr(class_, 'params')
146
+ iterative_show_pb(params_hardware)
147
+
148
+ for main_child in params:
149
+ # Was this condition useful?
150
+ # if main_child['name'] == 'detector_settings':
151
+ # while len(main_child['children']) > 0:
152
+ # for child in main_child['children']:
153
+ # main_child['children'].remove(child)
154
+
155
+ # main_child['children'].extend(params_hardware)
156
+ if main_child['name'] == 'detector_settings':
157
+ main_child['children'] = params_hardware
158
+ controller_dict = get_param_dict_from_name(main_child['children'], 'controller_ID')
159
+ controller_dict['value'] = random.randint(0, 9999)
160
+
161
+ return params
162
+
30
163
  class PresetScalableGroupMove(GroupParameter):
31
164
  """
32
165
  |
@@ -44,10 +177,10 @@ class PresetScalableGroupMove(GroupParameter):
44
177
  def __init__(self, **opts):
45
178
  opts['type'] = 'groupmove'
46
179
  opts['addText'] = "Add"
47
- opts['addList'] = [mov['name'] for mov in DAQ_Move_Stage_type]
180
+ opts['addMenu'] = categorize_items([mov['name'] for mov in DAQ_Move_Stage_type])
48
181
  super().__init__(**opts)
49
182
 
50
- def addNew(self, typ):
183
+ def addNew(self, typ:tuple):
51
184
  """
52
185
  Add a child.
53
186
 
@@ -57,47 +190,21 @@ class PresetScalableGroupMove(GroupParameter):
57
190
  =============== ===========
58
191
  """
59
192
  name_prefix = 'move'
60
-
61
- child_indexes = [int(par.name()[len(name_prefix) + 1:]) for par in self.children()]
62
-
63
- if child_indexes == []:
64
- newindex = 0
65
- else:
66
- newindex = max(child_indexes) + 1
67
-
68
- params = daq_move_params
69
- iterative_show_pb(params)
70
-
71
- parent_module = utils.find_dict_in_list_from_key_val(DAQ_Move_Stage_type, 'name', typ)
72
- class_ = getattr(getattr(parent_module['module'], 'daq_move_' + typ),
73
- 'DAQ_Move_' + typ)
74
- params_hardware = getattr(class_, 'params')
75
- iterative_show_pb(params_hardware)
76
-
77
- for main_child in params:
78
- if main_child['name'] == 'move_settings':
79
- main_child['children'] = params_hardware
80
- controller_dict = get_param_dict_from_name(params_hardware, 'controller_ID')
81
- controller_dict['value'] = random.randint(0, 9999)
82
-
83
- elif main_child['name'] == 'main_settings':
84
- typ_dict = get_param_dict_from_name(main_child['children'], 'move_type')
85
- typ_dict['value'] = typ
86
-
87
- child = {'title': 'Actuator {:02.0f}'.format(newindex),
88
- 'name': f'{name_prefix}{newindex:02.0f}',
89
- 'type': 'group',
90
- 'removable': True, 'renamable': False,
91
- 'children': [
92
- {'title': 'Name:', 'name': 'name', 'type': 'str',
93
- 'value': 'Move {:02.0f}'.format(newindex)},
94
- {'title': 'Init?:', 'name': 'init', 'type': 'bool', 'value': True},
95
- {'title': 'Settings:', 'name': 'params', 'type': 'group', 'children': params},
96
- ]}
97
-
193
+ typ = typ[-1] #Only need last entry here
194
+ new_index = find_last_index(self.children(), name_prefix, format_string='02.0f')
195
+ params = make_move_params(typ)
196
+ child = {'title': f'Actuator {new_index}',
197
+ 'name': f'{name_prefix}{new_index}',
198
+ 'type': 'group',
199
+ 'removable': True, 'renamable': False,
200
+ 'children': [
201
+ {'title': 'Name:', 'name': 'name', 'type': 'str',
202
+ 'value': f'Move {new_index}'},
203
+ {'title': 'Init?:', 'name': 'init', 'type': 'bool', 'value': True},
204
+ {'title': 'Settings:', 'name': 'params', 'type': 'group', 'children': params},
205
+ ]}
98
206
  self.addChild(child)
99
207
 
100
-
101
208
  registerParameterType('groupmove', PresetScalableGroupMove, override=True)
102
209
 
103
210
 
@@ -116,20 +223,17 @@ class PresetScalableGroupDet(GroupParameter):
116
223
  def __init__(self, **opts):
117
224
  opts['type'] = 'groupdet'
118
225
  opts['addText'] = "Add"
119
- options = []
120
- for name in [plugin['name'] for plugin in DAQ_0DViewer_Det_types]:
121
- options.append('DAQ0D/' + name)
122
- for name in [plugin['name'] for plugin in DAQ_1DViewer_Det_types]:
123
- options.append('DAQ1D/' + name)
124
- for name in [plugin['name'] for plugin in DAQ_2DViewer_Det_types]:
125
- options.append('DAQ2D/' + name)
126
- for name in [plugin['name'] for plugin in DAQ_NDViewer_Det_types]:
127
- options.append('DAQND/' + name)
128
- opts['addList'] = options
226
+ options = {
227
+ 'DAQ0D': [name for name in [plugin['name'] for plugin in DAQ_0DViewer_Det_types]],
228
+ 'DAQ1D': [name for name in [plugin['name'] for plugin in DAQ_1DViewer_Det_types]],
229
+ 'DAQ2D': [name for name in [plugin['name'] for plugin in DAQ_2DViewer_Det_types]],
230
+ 'DAQND': [name for name in [plugin['name'] for plugin in DAQ_NDViewer_Det_types]],
231
+ }
232
+ opts['addMenu'] = add_category_layers(options)
129
233
 
130
234
  super().__init__(**opts)
131
235
 
132
- def addNew(self, typ):
236
+ def addNew(self, typ:tuple):
133
237
  """
134
238
  Add a child.
135
239
 
@@ -140,63 +244,15 @@ class PresetScalableGroupDet(GroupParameter):
140
244
  """
141
245
  try:
142
246
  name_prefix = 'det'
143
- child_indexes = [int(par.name()[len(name_prefix) + 1:]) for par in self.children()]
144
-
145
- if child_indexes == []:
146
- newindex = 0
147
- else:
148
- newindex = max(child_indexes) + 1
149
-
150
- params = daq_viewer_params
151
- iterative_show_pb(params)
152
-
153
- for main_child in params:
154
- if main_child['name'] == 'main_settings':
155
- for child in main_child['children']:
156
- if child['name'] == 'DAQ_type':
157
- child['value'] = typ[0:5]
158
- if child['name'] == 'detector_type':
159
- child['value'] = typ[6:]
160
- if child['name'] == 'controller_status':
161
- child['visible'] = True
162
-
163
- if '0D' in typ:
164
- parent_module = utils.find_dict_in_list_from_key_val(DAQ_0DViewer_Det_types, 'name', typ[6:])
165
- class_ = getattr(getattr(parent_module['module'], 'daq_0Dviewer_' + typ[6:]), 'DAQ_0DViewer_' + typ[6:])
166
- elif '1D' in typ:
167
- parent_module = utils.find_dict_in_list_from_key_val(DAQ_1DViewer_Det_types, 'name', typ[6:])
168
- class_ = getattr(getattr(parent_module['module'], 'daq_1Dviewer_' + typ[6:]), 'DAQ_1DViewer_' + typ[6:])
169
- elif '2D' in typ:
170
- parent_module = utils.find_dict_in_list_from_key_val(DAQ_2DViewer_Det_types, 'name', typ[6:])
171
- class_ = getattr(getattr(parent_module['module'], 'daq_2Dviewer_' + typ[6:]), 'DAQ_2DViewer_' + typ[6:])
172
- elif 'ND' in typ:
173
- parent_module = utils.find_dict_in_list_from_key_val(DAQ_NDViewer_Det_types, 'name', typ[6:])
174
- class_ = getattr(getattr(parent_module['module'], 'daq_NDviewer_' + typ[6:]), 'DAQ_NDViewer_' + typ[6:])
175
- for main_child in params:
176
- if main_child['name'] == 'main_settings':
177
- for child in main_child['children']:
178
- if child['name'] == 'axes':
179
- child['visible'] = True
180
-
181
- params_hardware = getattr(class_, 'params')
182
- iterative_show_pb(params_hardware)
183
-
184
- for main_child in params:
185
- if main_child['name'] == 'detector_settings':
186
- while len(main_child['children']) > 0:
187
- for child in main_child['children']:
188
- main_child['children'].remove(child)
189
-
190
- main_child['children'].extend(params_hardware)
191
- controller_dict = get_param_dict_from_name(main_child['children'], 'controller_ID')
192
- controller_dict['value'] = random.randint(0, 9999)
193
-
194
- child = {'title': 'Det {:02.0f}'.format(newindex), 'name': f'{name_prefix}{newindex:02.0f}',
195
- 'type': 'group', 'children': [
196
- {'title': 'Name:', 'name': 'name', 'type': 'str', 'value': 'Det {:02.0f}'.format(newindex)},
247
+ typ = "/".join((typ[0],typ[-1])) #Only need first and last element to retrieve associated plugin
248
+ new_index = find_last_index(list_children=self.children(), name_prefix=name_prefix, format_string='02.0f')
249
+ params = make_viewer_params(typ)
250
+ child = {'title': f'Det {new_index}', 'name': f'{name_prefix}{new_index}',
251
+ 'type': 'group', 'children': [
252
+ {'title': 'Name:', 'name': 'name', 'type': 'str', 'value': f'Det {new_index}'},
197
253
  {'title': 'Init?:', 'name': 'init', 'type': 'bool', 'value': True},
198
254
  {'title': 'Settings:', 'name': 'params', 'type': 'group', 'children': params},
199
- ], 'removable': True, 'renamable': False}
255
+ ], 'removable': True, 'renamable': False}
200
256
 
201
257
  self.addChild(child)
202
258
  except Exception as e:
@@ -3,6 +3,7 @@ import sys
3
3
 
4
4
  from qtpy.QtCore import QObject, Signal
5
5
  from qtpy import QtGui, QtWidgets
6
+ from qtpy.QtWidgets import QMessageBox, QDialogButtonBox, QDialog
6
7
 
7
8
 
8
9
  from pymodaq.utils.config import get_set_remote_path
@@ -153,9 +154,9 @@ class ShortcutSelection(QtWidgets.QDialog):
153
154
  hor_layout.addWidget(label)
154
155
  hor_layout.addWidget(self.label)
155
156
 
156
- buttonBox = QtWidgets.QDialogButtonBox()
157
- buttonBox.addButton(QtWidgets.QDialogButtonBox.Ok)
158
- buttonBox.addButton(QtWidgets.QDialogButtonBox.Cancel)
157
+ buttonBox = QDialogButtonBox()
158
+ buttonBox.addButton(QDialogButtonBox.StandardButton.Ok)
159
+ buttonBox.addButton(QDialogButtonBox.StandardButton.Cancel)
159
160
  layout.addWidget(self.label)
160
161
  layout.addWidget(buttonBox)
161
162
 
@@ -244,9 +245,9 @@ class JoystickButtonsSelection(QtWidgets.QDialog):
244
245
 
245
246
  layout.addWidget(self.settings_tree)
246
247
 
247
- buttonBox = QtWidgets.QDialogButtonBox()
248
- buttonBox.addButton(QtWidgets.QDialogButtonBox.Ok)
249
- buttonBox.addButton(QtWidgets.QDialogButtonBox.Cancel)
248
+ buttonBox = QDialogButtonBox()
249
+ buttonBox.addButton(QDialogButtonBox.StandardButton.Ok)
250
+ buttonBox.addButton(QDialogButtonBox.StandardButton.Cancel)
250
251
  layout.addWidget(buttonBox)
251
252
 
252
253
  buttonBox.accepted.connect(self.accept)
@@ -261,13 +262,17 @@ class RemoteManager(QObject):
261
262
  self.actuators = actuators
262
263
  self.detectors = detectors
263
264
  if msgbox:
264
- msgBox = QtWidgets.QMessageBox()
265
+ msgBox = QMessageBox()
265
266
  msgBox.setText("Preset Manager?")
266
267
  msgBox.setInformativeText("What do you want to do?")
267
- cancel_button = msgBox.addButton(QtWidgets.QMessageBox.Cancel)
268
- new_button = msgBox.addButton("New", QtWidgets.QMessageBox.ActionRole)
269
- modify_button = msgBox.addButton('Modify', QtWidgets.QMessageBox.AcceptRole)
270
- msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel)
268
+ cancel_button = msgBox.addButton(QMessageBox.StandardButton.Cancel)
269
+ new_button = msgBox.addButton(
270
+ "New", QMessageBox.ButtonRole.ActionRole
271
+ )
272
+ modify_button = msgBox.addButton(
273
+ "Modify", QMessageBox.ButtonRole.AcceptRole
274
+ )
275
+ msgBox.setDefaultButton(QMessageBox.StandardButton.Cancel)
271
276
  ret = msgBox.exec()
272
277
 
273
278
  if msgBox.clickedButton() == new_button:
@@ -443,7 +448,7 @@ class RemoteManager(QObject):
443
448
  """
444
449
 
445
450
  """
446
- dialog = QtWidgets.QDialog()
451
+ dialog = QDialog()
447
452
  vlayout = QtWidgets.QVBoxLayout()
448
453
  tree = ParameterTree()
449
454
  # tree.setMinimumWidth(400)
@@ -452,18 +457,18 @@ class RemoteManager(QObject):
452
457
 
453
458
  vlayout.addWidget(tree)
454
459
  dialog.setLayout(vlayout)
455
- buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
460
+ buttonBox = QDialogButtonBox(parent=dialog)
456
461
 
457
- buttonBox.addButton('Save', buttonBox.AcceptRole)
462
+ buttonBox.addButton("Save", QDialogButtonBox.ButtonRole.AcceptRole)
458
463
  buttonBox.accepted.connect(dialog.accept)
459
- buttonBox.addButton('Cancel', buttonBox.RejectRole)
464
+ buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
460
465
  buttonBox.rejected.connect(dialog.reject)
461
466
 
462
467
  vlayout.addWidget(buttonBox)
463
468
  dialog.setWindowTitle('Fill in information about the actions and their shortcuts')
464
469
  res = dialog.exec()
465
470
 
466
- if res == dialog.Accepted:
471
+ if res == QDialog.DialogCode.Accepted:
467
472
  # save preset parameters in a xml file
468
473
  ioxml.parameter_to_xml_file(
469
474
  self.remote_params, os.path.join(remote_path, self.remote_params.child('filename').value()))
@@ -20,11 +20,16 @@ from pymodaq_utils import math_utils as mutils
20
20
  from pymodaq_utils import config as configmod
21
21
 
22
22
  from pymodaq_gui.managers.parameter_manager import ParameterManager, Parameter
23
-
24
23
  from pymodaq_data.data import Axis, DataDistribution
25
24
 
26
25
  from pymodaq.utils.scanner.scan_config import ScanConfig
27
- from pymodaq_gui.config import ConfigSaverLoader
26
+
27
+ try:
28
+ from pymodaq_gui.config_saver_loader import ConfigSaverLoader
29
+ except ModuleNotFoundError:
30
+ from pymodaq_gui.config import ConfigSaverLoader
31
+
32
+ from pymodaq.utils.config import Config as ControlModulesConfig
28
33
 
29
34
 
30
35
  if TYPE_CHECKING:
@@ -33,8 +38,9 @@ if TYPE_CHECKING:
33
38
 
34
39
 
35
40
  logger = set_logger(get_module_name(__file__))
36
- config = configmod.Config()
37
41
 
42
+ config_utils = configmod.Config()
43
+ config = ControlModulesConfig()
38
44
 
39
45
  class ScanParameterManager(ParameterManager):
40
46
  settings_name = 'scanner_settings'
@@ -81,11 +87,12 @@ class ScannerBase(ScanParameterManager, metaclass=ABCMeta):
81
87
  distribution: DataDistribution = abstract_attribute()
82
88
  save_settings = True
83
89
 
84
- def __init__(self, actuators: List[DAQ_Move] = None):
90
+ def __init__(self, actuators: List[DAQ_Move] = None, display_units=True):
85
91
  super().__init__()
86
92
  self.positions: np.ndarray = None
87
93
  self.n_steps = 1
88
94
  self.config = ScanConfig()
95
+ self.display_units = display_units
89
96
  base_path = [act.title for act in actuators] + [self.scan_type, self.scan_subtype]
90
97
 
91
98
  self.config_saver_loader = ConfigSaverLoader(self.settings,
@@ -96,10 +103,17 @@ class ScannerBase(ScanParameterManager, metaclass=ABCMeta):
96
103
 
97
104
  self.set_settings_titles()
98
105
  self.set_settings_values()
106
+ if len(actuators) > 0:
107
+ self.set_units()
99
108
 
100
109
  if self.check_steps():
101
110
  self.set_scan()
102
111
 
112
+ @abstractmethod
113
+ def set_units(self):
114
+ """ Update settings units depending on the scanner type and the display_units boolean"""
115
+ ...
116
+
103
117
  def set_settings_titles(self):
104
118
  """Update the settings accordingly with the selected actuators"""
105
119
  ...
@@ -311,9 +311,7 @@ class ScanSelector(ParameterManager, QObject):
311
311
  self.table_view.horizontalHeader().setStretchLastSection(True)
312
312
  self.table_view.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
313
313
  self.table_view.setSelectionMode(QtWidgets.QTableView.SingleSelection)
314
- styledItemDelegate = QtWidgets.QStyledItemDelegate()
315
- styledItemDelegate.setItemEditorFactory(gutils.SpinBoxDelegate())
316
- self.table_view.setItemDelegate(styledItemDelegate)
314
+ self.table_view.setItemDelegate(gutils.SpinBoxDelegate())
317
315
 
318
316
  self.table_view.setDragEnabled(False)
319
317
  self.table_view.setDropIndicatorShown(False)
@@ -5,6 +5,7 @@ from collections import OrderedDict
5
5
  from qtpy.QtCore import QObject, Signal
6
6
  from qtpy import QtWidgets
7
7
 
8
+ from pymodaq_gui.messenger import messagebox
8
9
  from pymodaq_utils.logger import set_logger, get_module_name
9
10
  from pymodaq_utils.config import Config
10
11
  import pymodaq_utils.utils as utils
@@ -15,13 +16,16 @@ from pymodaq.utils.scanner.scan_factory import ScannerFactory, ScannerBase
15
16
  from pymodaq.utils.scanner.utils import ScanInfo
16
17
  from pymodaq.utils.scanner.scan_selector import Selector
17
18
  from pymodaq.utils.data import DataToExport, DataActuator
19
+ from pymodaq.utils.config import Config as ControlModulesConfig
18
20
 
19
21
  if TYPE_CHECKING:
20
22
  from pymodaq.control_modules.daq_move import DAQ_Move
21
23
 
22
24
 
23
25
  logger = set_logger(get_module_name(__file__))
24
- config = Config()
26
+
27
+ config_utils = Config()
28
+ config = ControlModulesConfig()
25
29
  scanner_factory = ScannerFactory()
26
30
 
27
31
 
@@ -50,6 +54,12 @@ class Scanner(QObject, ParameterManager):
50
54
  'limits': scanner_factory.scan_types()},
51
55
  {'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'list',
52
56
  'limits': scanner_factory.scan_sub_types(scanner_factory.scan_types()[0])},
57
+ {'title': 'Units handling', 'name': 'units_handling', 'type': 'group', 'children': [
58
+ {'title': 'Display units', 'name': 'display_units', 'type': 'bool', 'value': True},
59
+ {'title': 'Default units', 'name': 'common_units', 'type': 'str', 'value': '', 'visible': False,
60
+ 'readonly': True},
61
+ ]},
62
+
53
63
  ]
54
64
 
55
65
  def __init__(self, parent_widget: QtWidgets.QWidget = None, scanner_items=OrderedDict([]),
@@ -82,9 +92,11 @@ class Scanner(QObject, ParameterManager):
82
92
 
83
93
  def set_scanner(self):
84
94
  try:
85
- self._scanner: ScannerBase = scanner_factory.get(self.settings['scan_type'],
86
- self.settings['scan_sub_type'],
87
- actuators=self.actuators)
95
+ self._scanner: ScannerBase = scanner_factory.get(
96
+ self.settings['scan_type'],
97
+ self.settings['scan_sub_type'],
98
+ actuators=self.actuators,
99
+ display_units=self.settings['units_handling', 'display_units'])
88
100
 
89
101
  while True:
90
102
  child = self._scanner_settings_widget.layout().takeAt(0)
@@ -111,10 +123,25 @@ class Scanner(QObject, ParameterManager):
111
123
  if param.name() == 'scan_type':
112
124
  self.settings.child('scan_sub_type').setOpts(
113
125
  limits=scanner_factory.scan_sub_types(param.value()))
114
- if param.name() in ['scan_type', 'scan_sub_type']:
126
+ if param.name() in ['scan_sub_type']:
115
127
  self.set_scanner()
116
128
  self.settings.child('scan_type').setOpts(tip=self._scanner.__doc__)
117
129
  self.settings.child('scan_sub_type').setOpts(tip=self._scanner.__doc__)
130
+ elif param.name() == 'display_units':
131
+ if not param.value() and len(self.actuators) > 0:
132
+
133
+ units = set([act.units for act in self.actuators])
134
+ if len(units) > 1:
135
+ messagebox(title='Info',
136
+ text='Could not use the same units for all settings as units are not compatible')
137
+ param.setValue(True)
138
+ self.settings.child('units_handling', 'common_units').show(False)
139
+ else:
140
+ self.settings.child('units_handling', 'common_units').setValue(list(units)[0])
141
+ self.settings.child('units_handling', 'common_units').show(True)
142
+ else:
143
+ self.settings.child('units_handling', 'common_units').show(False)
144
+ self.set_scanner()
118
145
 
119
146
  self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
120
147
 
@@ -245,10 +272,12 @@ def main():
245
272
  from pymodaq.utils.parameter import ParameterTree
246
273
  app = QtWidgets.QApplication(sys.argv)
247
274
 
275
+ units = ['nm', 'kW', 'ms']
276
+
248
277
  class MoveMock:
249
278
  def __init__(self, ind: int = 0):
250
279
  self.title = f'act_{ind}'
251
- self.units = f'units_{ind}'
280
+ self.units = units[ind]
252
281
 
253
282
  actuators = [MoveMock(ind) for ind in range(3)]
254
283
 
@@ -32,8 +32,14 @@ class Scan1DBase(ScannerBase):
32
32
  n_axes = 1
33
33
  distribution = DataDistribution['uniform']
34
34
 
35
- def __init__(self, actuators: List = None, **_ignored):
36
- super().__init__(actuators=actuators)
35
+ def __init__(self, actuators: List = None, display_units=True, **_ignored):
36
+ super().__init__(actuators=actuators, display_units=display_units)
37
+
38
+ def set_units(self):
39
+ """ Update settings units depending on the scanner type and the display_units boolean"""
40
+ for child in self.settings.children():
41
+ child.setOpts(
42
+ suffix='' if not self.display_units else self.actuators[0].units)
37
43
 
38
44
  def get_nav_axes(self) -> List[Axis]:
39
45
  return [Axis(label=f'{self.actuators[0].title}',
@@ -64,8 +70,9 @@ class Scan1DLinear(Scan1DBase):
64
70
  n_axes = 1
65
71
  distribution = DataDistribution['uniform']
66
72
 
67
- def __init__(self, actuators: List['DAQ_Move'] = None, **_ignored):
68
- super().__init__(actuators=actuators)
73
+ def __init__(self, actuators: List['DAQ_Move'] = None, display_units=True, **_ignored):
74
+ super().__init__(actuators=actuators, display_units=display_units)
75
+
69
76
 
70
77
  def set_scan(self):
71
78
  self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
@@ -96,8 +103,8 @@ class Scan1DRandom(Scan1DLinear):
96
103
 
97
104
  scan_subtype = 'Random'
98
105
 
99
- def __init__(self, actuators: List = None, **_ignored):
100
- super().__init__(actuators=actuators)
106
+ def __init__(self, actuators: List = None, display_units=True, **_ignored):
107
+ super().__init__(actuators=actuators, display_units=display_units)
101
108
 
102
109
  def set_scan(self):
103
110
  self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
@@ -128,8 +135,8 @@ class Scan1DSparse(Scan1DBase):
128
135
  distribution = DataDistribution['uniform'] # because in 1D it doesn't matter is spread or
129
136
  # uniform, one can easily plot both types on a regulat 1D plot
130
137
 
131
- def __init__(self, actuators: List['DAQ_Move'] = None, **_ignored):
132
- super().__init__(actuators=actuators)
138
+ def __init__(self, actuators: List['DAQ_Move'] = None, display_units=True, **_ignored):
139
+ super().__init__(actuators=actuators, display_units=display_units)
133
140
  self.settings.child('parsed_string').setOpts(tip=self.__doc__)
134
141
 
135
142
  def set_scan(self):
@@ -165,41 +172,3 @@ class Scan1DSparse(Scan1DBase):
165
172
  if len(self.actuators) == 1:
166
173
  self.settings.child('parsed_string').setOpts(title=f'{self.actuators[0].title} Parsed string:')
167
174
 
168
- try:
169
- import adaptive
170
-
171
-
172
- @ScannerFactory.register()
173
- class Scan1DAdaptive(Scan1DBase):
174
-
175
- scan_subtype = 'Adaptive'
176
- params = [
177
- {'title': 'Loss type', 'name': 'scan_loss', 'type': 'list',
178
- 'limits': ['default', 'curvature', 'uniform'], 'tip': 'Type of loss used by the algo. to determine next points'},
179
- {'title': 'Start:', 'name': 'start', 'type': 'float', 'value': 0.},
180
- {'title': 'Stop:', 'name': 'stop', 'type': 'float', 'value': 1.},
181
- ]
182
- distribution = DataDistribution['spread']
183
-
184
- def __init__(self, actuators: List['DAQ_Move'] = None, **_ignored):
185
- super().__init__(actuators=actuators)
186
-
187
- def set_scan(self):
188
- self.axes_unique = [np.array([])]
189
- self.axes_indexes = np.array([], dtype=int)
190
- self.positions = np.array([self.settings['start'], self.settings['stop']])
191
-
192
- def evaluate_steps(self) -> int:
193
- return 1
194
-
195
- def get_nav_axes(self) -> List[Axis]:
196
- return [Axis(label=f'{self.actuators[0].mod_name} axis',
197
- units=f'{self.actuators[0].units}',
198
- data=self.positions[0])]
199
-
200
- def get_scan_shape(self) -> Tuple[int]:
201
- return len(self.positions),
202
-
203
- except ModuleNotFoundError:
204
- logger.info('adaptive module is not present, no adaptive scan possible')
205
-