pymodaq 5.1.6__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.
Files changed (154) hide show
  1. pymodaq/__init__.py +98 -0
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +1238 -0
  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/ui_base.py +359 -0
  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 +1517 -0
  13. pymodaq/control_modules/daq_viewer_ui.py +407 -0
  14. pymodaq/control_modules/mocks.py +57 -0
  15. pymodaq/control_modules/move_utility_classes.py +1141 -0
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +591 -0
  19. pymodaq/control_modules/viewer_utility_classes.py +670 -0
  20. pymodaq/daq_utils/__init__.py +0 -0
  21. pymodaq/daq_utils/daq_utils.py +6 -0
  22. pymodaq/dashboard.py +2396 -0
  23. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
  24. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
  25. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
  26. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
  27. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
  28. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
  29. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
  30. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
  31. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
  32. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
  33. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
  34. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
  35. pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
  36. pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
  37. pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
  38. pymodaq/examples/__init__.py +0 -0
  39. pymodaq/examples/function_plotter.py +160 -0
  40. pymodaq/examples/nonlinearscanner.py +126 -0
  41. pymodaq/examples/qt_less_standalone_module.py +165 -0
  42. pymodaq/examples/tcp_client.py +97 -0
  43. pymodaq/extensions/__init__.py +25 -0
  44. pymodaq/extensions/adaptive/__init__.py +2 -0
  45. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  46. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  47. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  48. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  49. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  50. pymodaq/extensions/adaptive/utils.py +123 -0
  51. pymodaq/extensions/bayesian/__init__.py +2 -0
  52. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  53. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  54. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  55. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  56. pymodaq/extensions/bayesian/utils.py +180 -0
  57. pymodaq/extensions/console.py +73 -0
  58. pymodaq/extensions/daq_logger/__init__.py +1 -0
  59. pymodaq/extensions/daq_logger/abstract.py +52 -0
  60. pymodaq/extensions/daq_logger/daq_logger.py +519 -0
  61. pymodaq/extensions/daq_logger/db/__init__.py +0 -0
  62. pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
  63. pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
  64. pymodaq/extensions/daq_logger/h5logging.py +84 -0
  65. pymodaq/extensions/daq_scan.py +1218 -0
  66. pymodaq/extensions/daq_scan_ui.py +241 -0
  67. pymodaq/extensions/data_mixer/__init__.py +0 -0
  68. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  69. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  70. pymodaq/extensions/data_mixer/model.py +108 -0
  71. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  72. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  73. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  74. pymodaq/extensions/data_mixer/parser.py +53 -0
  75. pymodaq/extensions/data_mixer/utils.py +23 -0
  76. pymodaq/extensions/h5browser.py +9 -0
  77. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  78. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  79. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  80. pymodaq/extensions/optimizers_base/utils.py +427 -0
  81. pymodaq/extensions/pid/__init__.py +16 -0
  82. pymodaq/extensions/pid/actuator_controller.py +14 -0
  83. pymodaq/extensions/pid/daq_move_PID.py +154 -0
  84. pymodaq/extensions/pid/pid_controller.py +1016 -0
  85. pymodaq/extensions/pid/utils.py +189 -0
  86. pymodaq/extensions/utils.py +111 -0
  87. pymodaq/icon.ico +0 -0
  88. pymodaq/post_treatment/__init__.py +6 -0
  89. pymodaq/post_treatment/load_and_plot.py +352 -0
  90. pymodaq/resources/__init__.py +0 -0
  91. pymodaq/resources/config_template.toml +57 -0
  92. pymodaq/resources/preset_default.xml +1 -0
  93. pymodaq/resources/setup_plugin.py +73 -0
  94. pymodaq/splash.png +0 -0
  95. pymodaq/utils/__init__.py +0 -0
  96. pymodaq/utils/array_manipulation.py +6 -0
  97. pymodaq/utils/calibration_camera.py +180 -0
  98. pymodaq/utils/chrono_timer.py +203 -0
  99. pymodaq/utils/config.py +53 -0
  100. pymodaq/utils/conftests.py +5 -0
  101. pymodaq/utils/daq_utils.py +158 -0
  102. pymodaq/utils/data.py +128 -0
  103. pymodaq/utils/enums.py +6 -0
  104. pymodaq/utils/exceptions.py +38 -0
  105. pymodaq/utils/gui_utils/__init__.py +10 -0
  106. pymodaq/utils/gui_utils/loader_utils.py +75 -0
  107. pymodaq/utils/gui_utils/utils.py +18 -0
  108. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  109. pymodaq/utils/h5modules/__init__.py +2 -0
  110. pymodaq/utils/h5modules/module_saving.py +526 -0
  111. pymodaq/utils/leco/__init__.py +25 -0
  112. pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
  113. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
  114. pymodaq/utils/leco/director_utils.py +74 -0
  115. pymodaq/utils/leco/leco_director.py +166 -0
  116. pymodaq/utils/leco/pymodaq_listener.py +364 -0
  117. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  118. pymodaq/utils/leco/utils.py +74 -0
  119. pymodaq/utils/logger.py +6 -0
  120. pymodaq/utils/managers/__init__.py +0 -0
  121. pymodaq/utils/managers/batchscan_manager.py +346 -0
  122. pymodaq/utils/managers/modules_manager.py +589 -0
  123. pymodaq/utils/managers/overshoot_manager.py +242 -0
  124. pymodaq/utils/managers/preset_manager.py +229 -0
  125. pymodaq/utils/managers/preset_manager_utils.py +262 -0
  126. pymodaq/utils/managers/remote_manager.py +484 -0
  127. pymodaq/utils/math_utils.py +6 -0
  128. pymodaq/utils/messenger.py +6 -0
  129. pymodaq/utils/parameter/__init__.py +10 -0
  130. pymodaq/utils/parameter/utils.py +6 -0
  131. pymodaq/utils/scanner/__init__.py +5 -0
  132. pymodaq/utils/scanner/scan_config.py +16 -0
  133. pymodaq/utils/scanner/scan_factory.py +259 -0
  134. pymodaq/utils/scanner/scan_selector.py +477 -0
  135. pymodaq/utils/scanner/scanner.py +324 -0
  136. pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
  137. pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
  138. pymodaq/utils/scanner/scanners/__init__.py +1 -0
  139. pymodaq/utils/scanner/scanners/sequential.py +224 -0
  140. pymodaq/utils/scanner/scanners/tabular.py +319 -0
  141. pymodaq/utils/scanner/utils.py +110 -0
  142. pymodaq/utils/svg/__init__.py +6 -0
  143. pymodaq/utils/svg/svg_renderer.py +20 -0
  144. pymodaq/utils/svg/svg_view.py +35 -0
  145. pymodaq/utils/svg/svg_viewer2D.py +50 -0
  146. pymodaq/utils/tcp_ip/__init__.py +6 -0
  147. pymodaq/utils/tcp_ip/mysocket.py +12 -0
  148. pymodaq/utils/tcp_ip/serializer.py +13 -0
  149. pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
  150. pymodaq-5.1.6.dist-info/METADATA +238 -0
  151. pymodaq-5.1.6.dist-info/RECORD +154 -0
  152. pymodaq-5.1.6.dist-info/WHEEL +4 -0
  153. pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
  154. pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,324 @@
1
+ from __future__ import annotations
2
+ from typing import Tuple, List, TYPE_CHECKING
3
+ from collections import OrderedDict
4
+
5
+ from qtpy.QtCore import QObject, Signal
6
+ from qtpy import QtWidgets
7
+
8
+ from pymodaq_gui.messenger import messagebox
9
+ from pymodaq_utils.logger import set_logger, get_module_name
10
+ from pymodaq_utils.config import Config
11
+ import pymodaq_utils.utils as utils
12
+
13
+ from pymodaq_gui.managers.parameter_manager import ParameterManager, Parameter
14
+
15
+ from pymodaq.utils.scanner.scan_factory import ScannerFactory, ScannerBase
16
+ from pymodaq.utils.scanner.utils import ScanInfo
17
+ from pymodaq.utils.scanner.scan_selector import Selector
18
+ from pymodaq.utils.data import DataToExport, DataActuator
19
+ from pymodaq.utils.config import Config as ControlModulesConfig
20
+
21
+ if TYPE_CHECKING:
22
+ from pymodaq.control_modules.daq_move import DAQ_Move
23
+
24
+
25
+ logger = set_logger(get_module_name(__file__))
26
+
27
+ config_utils = Config()
28
+ config = ControlModulesConfig()
29
+ scanner_factory = ScannerFactory()
30
+
31
+
32
+ class Scanner(QObject, ParameterManager):
33
+ """Main Object to define a PyMoDAQ scan and create a UI to set it
34
+
35
+ Parameters
36
+ ----------
37
+ parent_widget: QtWidgets.QWidget
38
+ scanner_items: list of GraphicItems
39
+ used by ScanSelector for chosing scan area or linear traces
40
+ actuators: List[DAQ_Move]
41
+ list actuators names
42
+
43
+ See Also
44
+ --------
45
+ ScanSelector, ScannerBase, TableModelSequential, TableModelTabular, pymodaq_types.TableViewCustom
46
+ """
47
+ scanner_updated_signal = Signal()
48
+ settings_name = 'scanner'
49
+
50
+ params = [
51
+ {'title': 'Calculate positions:', 'name': 'calculate_positions', 'type': 'action'},
52
+ {'title': 'N steps:', 'name': 'n_steps', 'type': 'int', 'value': 0, 'readonly': True},
53
+ {'title': 'Scan type:', 'name': 'scan_type', 'type': 'list',
54
+ 'limits': scanner_factory.scan_types()},
55
+ {'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'list',
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
+
63
+ ]
64
+
65
+ def __init__(self, parent_widget: QtWidgets.QWidget = None, scanner_items=OrderedDict([]),
66
+ actuators: List[DAQ_Move] = []):
67
+ QObject.__init__(self)
68
+ ParameterManager.__init__(self)
69
+ if parent_widget is None:
70
+ parent_widget = QtWidgets.QWidget()
71
+ self.parent_widget = parent_widget
72
+ self._scanner_settings_widget = None
73
+
74
+ self.connect_things()
75
+ self._scanner: ScannerBase = None
76
+
77
+ self.setup_ui()
78
+ self.actuators = actuators
79
+ if self._scanner is not None:
80
+ self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
81
+
82
+ def setup_ui(self):
83
+ self.parent_widget.setLayout(QtWidgets.QVBoxLayout())
84
+ self.parent_widget.layout().setContentsMargins(0, 0, 0, 0)
85
+ self.parent_widget.layout().addWidget(self.settings_tree)
86
+ self._scanner_settings_widget = QtWidgets.QWidget()
87
+ self._scanner_settings_widget.setLayout(QtWidgets.QVBoxLayout())
88
+ self._scanner_settings_widget.layout().setContentsMargins(0, 0, 0, 0)
89
+ self.parent_widget.layout().addWidget(self._scanner_settings_widget)
90
+ self.settings_tree.setMinimumHeight(110)
91
+ self.settings_tree.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
92
+
93
+ def set_scanner(self):
94
+ try:
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'])
100
+
101
+ while True:
102
+ child = self._scanner_settings_widget.layout().takeAt(0)
103
+ if not child:
104
+ break
105
+ child.widget().deleteLater()
106
+ QtWidgets.QApplication.processEvents()
107
+
108
+ self._scanner_settings_widget.layout().addWidget(self._scanner.settings_tree)
109
+ self._scanner.settings.sigTreeStateChanged.connect(self._update_steps)
110
+
111
+ except ValueError as e:
112
+ pass
113
+
114
+ @property
115
+ def scanner(self) -> ScannerBase:
116
+ return self._scanner
117
+
118
+ def get_scanner_sub_settings(self):
119
+ """Get the current ScannerBase implementation's settings"""
120
+ return self._scanner.settings
121
+
122
+ def value_changed(self, param: Parameter):
123
+ if param.name() == 'scan_type':
124
+ self.settings.child('scan_sub_type').setOpts(
125
+ limits=scanner_factory.scan_sub_types(param.value()))
126
+ if param.name() in ['scan_sub_type']:
127
+ self.set_scanner()
128
+ self.settings.child('scan_type').setOpts(tip=self._scanner.__doc__)
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()
145
+
146
+ self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
147
+
148
+ @property
149
+ def actuators(self) -> list[DAQ_Move]:
150
+ """list of str: Returns as a list the name of the selected actuators to describe the actual scan"""
151
+ return self._actuators
152
+
153
+ @actuators.setter
154
+ def actuators(self, act_list):
155
+ self._actuators = act_list
156
+ self.set_scanner()
157
+
158
+ def set_scan_type_and_subtypes(self, scan_type: str, scan_subtype: str):
159
+ """Convenience function to set the main scan type
160
+
161
+ Parameters
162
+ ----------
163
+ scan_type: str
164
+ one of registered Scanner main identifier
165
+ scan_subtype: list of str or None
166
+ one of registered Scanner second identifier for a given main identifier
167
+
168
+ See Also
169
+ --------
170
+ ScannerFactory
171
+ """
172
+ if scan_type in scanner_factory.scan_types():
173
+ self.settings.child('scan_type').setValue(scan_type)
174
+
175
+ if scan_subtype is not None:
176
+ if scan_subtype in scanner_factory.scan_sub_types(scan_type):
177
+ self.settings.child('scan_sub_type').setValue(scan_subtype)
178
+
179
+ def set_scan_from_settings(self, settings: Parameter, scanner_settings: Parameter):
180
+
181
+ self.set_scan_type_and_subtypes(settings['scan_type'],
182
+ settings['scan_sub_type'])
183
+ self.settings.restoreState(settings.saveState())
184
+ self._scanner.settings.restoreState(scanner_settings.saveState())
185
+
186
+ @property
187
+ def scan_type(self) -> str:
188
+ return self.settings['scan_type']
189
+
190
+ @property
191
+ def scan_sub_type(self) -> str:
192
+ return self.settings['scan_sub_type']
193
+
194
+ def connect_things(self):
195
+ self.settings.child('calculate_positions').sigActivated.connect(self.set_scan)
196
+ self.scanner_updated_signal.connect(self.save_scanner_settings)
197
+
198
+ def save_scanner_settings(self):
199
+ self._scanner.save_scan_parameters()
200
+
201
+ def get_scan_info(self) -> ScanInfo:
202
+ """Get a summary of the configured scan as a ScanInfo object"""
203
+ return ScanInfo(self._scanner.n_steps, positions=self._scanner.positions,
204
+ axes_indexes=self._scanner.axes_indexes, axes_unique=self._scanner.axes_unique,
205
+ selected_actuators=[act.title for act in self.actuators])
206
+
207
+ def get_nav_axes(self):
208
+ return self._scanner.get_nav_axes()
209
+
210
+ def get_scan_shape(self):
211
+ return self._scanner.get_scan_shape()
212
+
213
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
214
+ """To be reimplemented. Calculations of indexes within the scan"""
215
+ return self._scanner.get_indexes_from_scan_index(scan_index)
216
+
217
+ def _update_steps(self):
218
+ self.settings.child('n_steps').setValue(self.n_steps)
219
+
220
+ @property
221
+ def n_steps(self):
222
+ return self._scanner.evaluate_steps()
223
+
224
+ @property
225
+ def n_axes(self):
226
+ return self._scanner.n_axes
227
+
228
+ @property
229
+ def positions(self):
230
+ return self._scanner.positions
231
+
232
+ def positions_at(self, index: int) -> DataToExport:
233
+ """ Extract the actuators positions at a given index in the scan as a DataToExport of DataActuators"""
234
+ dte = DataToExport('scanner')
235
+ for ind, pos in enumerate(self.positions[index]):
236
+ dte.append(DataActuator(self.actuators[ind].title, data=float(pos),
237
+ units=self.actuators[ind].units))
238
+ return dte
239
+
240
+ @property
241
+ def axes_indexes(self):
242
+ return self._scanner.axes_indexes
243
+
244
+ @property
245
+ def axes_unique(self):
246
+ return self._scanner.axes_unique
247
+
248
+ @property
249
+ def distribution(self):
250
+ return self._scanner.distribution
251
+
252
+ def set_scan(self):
253
+ """Process the settings options to calculate the scan positions
254
+
255
+ Returns
256
+ -------
257
+ bool: True if the processed number of steps if **higher** than the configured number of steps
258
+ """
259
+ oversteps = config('scan', 'steps_limit')
260
+ if self._scanner.evaluate_steps() > oversteps:
261
+ return True
262
+ self._scanner.set_scan()
263
+ self.settings.child('n_steps').setValue(self.n_steps)
264
+ self.scanner_updated_signal.emit()
265
+ return False
266
+
267
+ def update_from_scan_selector(self, scan_selector: Selector):
268
+ self._scanner.update_from_scan_selector(scan_selector)
269
+
270
+
271
+ def main():
272
+ from pymodaq.utils.parameter import ParameterTree
273
+ app = QtWidgets.QApplication(sys.argv)
274
+
275
+ units = ['nm', 'kW', 'ms']
276
+
277
+ class MoveMock:
278
+ def __init__(self, ind: int = 0):
279
+ self.title = f'act_{ind}'
280
+ self.units = units[ind]
281
+
282
+ actuators = [MoveMock(ind) for ind in range(3)]
283
+
284
+ params = [{'title': 'Actuators', 'name': 'actuators', 'type': 'itemselect',
285
+ 'value': dict(all_items=[act.title for act in actuators], selected=[]),'checkbox':True},
286
+ {'title': 'Set Scan', 'name': 'set_scan', 'type': 'action'},
287
+ ]
288
+ settings = Parameter.create(name='settings', type='group', children=params)
289
+ settings_tree = ParameterTree()
290
+ settings_tree.setParameters(settings)
291
+
292
+ widget_main = QtWidgets.QWidget()
293
+ widget_main.setLayout(QtWidgets.QVBoxLayout())
294
+ #widget_main.layout().setContentsMargins(0, 0, 0, 0)
295
+ widget_scanner = QtWidgets.QWidget()
296
+ widget_main.layout().addWidget(settings_tree)
297
+ widget_main.layout().addWidget(widget_scanner)
298
+ scanner = Scanner(widget_scanner, actuators=actuators)
299
+
300
+ def update_actuators(param):
301
+ scanner.actuators = [utils.find_objects_in_list_from_attr_name_val(actuators, 'title', act_str,
302
+ return_first=True)[0]
303
+ for act_str in param.value()['selected']]
304
+
305
+ def print_info():
306
+ print('info:')
307
+ print(scanner.get_scan_info())
308
+ print('positions:')
309
+ print(scanner.positions)
310
+ print('nav:')
311
+ print(scanner.get_nav_axes())
312
+
313
+ settings.child('actuators').sigValueChanged.connect(update_actuators)
314
+ settings.child('set_scan').sigActivated.connect(scanner.set_scan)
315
+ scanner.scanner_updated_signal.connect(print_info)
316
+ widget_main.show()
317
+ sys.exit(app.exec_())
318
+
319
+
320
+ if __name__ == '__main__':
321
+ import sys
322
+ from qtpy import QtWidgets
323
+ main()
324
+
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 05/12/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ from typing import List, Tuple, Any, TYPE_CHECKING
8
+ import re
9
+ import numpy as np
10
+
11
+ from pymodaq_utils.logger import set_logger, get_module_name
12
+ from pymodaq_utils import math_utils as mutils
13
+ from pymodaq_utils import config as configmod
14
+
15
+ from pymodaq_data.data import Axis, DataDistribution
16
+
17
+ from pymodaq.utils.scanner.scan_selector import Selector
18
+
19
+ from ..scan_factory import ScannerFactory, ScannerBase, ScanParameterManager
20
+
21
+ if TYPE_CHECKING:
22
+ from pymodaq.control_modules.daq_move import DAQ_Move
23
+
24
+ logger = set_logger(get_module_name(__file__))
25
+ config = configmod.Config()
26
+
27
+
28
+ class Scan1DBase(ScannerBase):
29
+ scan_type = 'Scan1D'
30
+
31
+ params = []
32
+ n_axes = 1
33
+ distribution = DataDistribution['uniform']
34
+
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)
43
+
44
+ def get_nav_axes(self) -> List[Axis]:
45
+ return [Axis(label=f'{self.actuators[0].title}',
46
+ units=f'{self.actuators[0].units}',
47
+ data=np.squeeze(self.positions))]
48
+
49
+ def get_scan_shape(self) -> Tuple[int]:
50
+ return len(self.positions),
51
+
52
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
53
+ """To be reimplemented. Calculations of indexes within the scan"""
54
+ return (scan_index,)
55
+
56
+ def update_from_scan_selector(self, scan_selector: Selector):
57
+ pass
58
+
59
+
60
+ @ScannerFactory.register()
61
+ class Scan1DLinear(Scan1DBase):
62
+ """ Defines a linear scan between start and stop values with steps of length defined in the step setting"""
63
+
64
+ scan_subtype = 'Linear'
65
+ params = [
66
+ {'title': 'Start:', 'name': 'start', 'type': 'float', 'value': 0.},
67
+ {'title': 'Stop:', 'name': 'stop', 'type': 'float', 'value': 1.},
68
+ {'title': 'Step:', 'name': 'step', 'type': 'float', 'value': 0.1}
69
+ ]
70
+ n_axes = 1
71
+ distribution = DataDistribution['uniform']
72
+
73
+ def __init__(self, actuators: List['DAQ_Move'] = None, display_units=True, **_ignored):
74
+ super().__init__(actuators=actuators, display_units=display_units)
75
+
76
+
77
+ def set_scan(self):
78
+ self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
79
+ self.settings['step'])
80
+ self.get_info_from_positions(self.positions)
81
+
82
+ def set_settings_titles(self):
83
+ if len(self.actuators) == 1:
84
+ self.settings.child('start').setOpts(title=f'{self.actuators[0].title} start:')
85
+ self.settings.child('stop').setOpts(title=f'{self.actuators[0].title} stop:')
86
+ self.settings.child('step').setOpts(title=f'{self.actuators[0].title} step:')
87
+
88
+ def evaluate_steps(self) -> int:
89
+ n_steps = int(np.abs((self.settings['stop'] - self.settings['start']) / self.settings['step']) + 1)
90
+ return n_steps
91
+
92
+ def update_from_scan_selector(self, scan_selector: Selector):
93
+ coordinates = scan_selector.get_coordinates()
94
+ if coordinates.shape == (2, 2) or coordinates.shape == (2, 1):
95
+ self.settings.child('start').setValue(coordinates[0, 0])
96
+ self.settings.child('stop').setValue(coordinates[1, 0])
97
+
98
+
99
+ @ScannerFactory.register()
100
+ class Scan1DRandom(Scan1DLinear):
101
+ """ Defines a random linear scan by first initializing a linear one between start and stop values with
102
+ steps of length defined in the step setting, then shuffling the values."""
103
+
104
+ scan_subtype = 'Random'
105
+
106
+ def __init__(self, actuators: List = None, display_units=True, **_ignored):
107
+ super().__init__(actuators=actuators, display_units=display_units)
108
+
109
+ def set_scan(self):
110
+ self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
111
+ self.settings['step'])
112
+ np.random.shuffle(self.positions)
113
+ self.get_info_from_positions(self.positions)
114
+ self.set_settings_titles()
115
+
116
+
117
+ @ScannerFactory.register()
118
+ class Scan1DSparse(Scan1DBase):
119
+ """ Syntax goes as start:step:stop or with single entry
120
+
121
+ * 0:0.2:1 will give [0 0.2 0.4 0.6 0.8 1]
122
+ * 0 will give [0]
123
+
124
+ Separate entries with comma or new line:
125
+
126
+ * 0:0.2:1,5 will give [0 0.2 0.4 0.6 0.8 1 5]
127
+ * 0:0.2:1,5:1:7 will give [0 0.2 0.4 0.6 0.8 1 5 6 7]
128
+ """
129
+
130
+ scan_subtype = 'Sparse'
131
+ params = [
132
+ {'title': 'Parsed string:', 'name': 'parsed_string', 'type': 'text', 'value': '0:0.1:1', }
133
+ ]
134
+ n_axes = 1
135
+ distribution = DataDistribution['uniform'] # because in 1D it doesn't matter is spread or
136
+ # uniform, one can easily plot both types on a regulat 1D plot
137
+
138
+ def __init__(self, actuators: List['DAQ_Move'] = None, display_units=True, **_ignored):
139
+ super().__init__(actuators=actuators, display_units=display_units)
140
+ self.settings.child('parsed_string').setOpts(tip=self.__doc__)
141
+
142
+ def set_scan(self):
143
+ try:
144
+ range_strings = re.findall("[^,\s]+", self.settings['parsed_string'])
145
+ series = np.asarray([])
146
+ for range_string in range_strings:
147
+ number_strings = re.findall("[^:]+", range_string) # Extract the numbers by splitting on :.
148
+ this_range = np.asarray([])
149
+ if len(number_strings) == 3: # 3 Numbers specify a range
150
+ start, step, stop = [float(number) for number in number_strings]
151
+ this_range = mutils.linspace_step(start, stop, step)
152
+ elif len(number_strings) == 1: # 1 number just specifies a single number
153
+ this_range = np.asarray([float(number_strings[0])])
154
+ series = np.concatenate((series, this_range))
155
+
156
+ self.positions = np.atleast_1d(np.squeeze(series))
157
+ self.get_info_from_positions(self.positions)
158
+ except Exception as e:
159
+ pass # many things could happen when parsing strings
160
+
161
+ def set_settings_titles(self):
162
+ if len(self.actuators) == 1:
163
+ self.settings.child('start').setOpts(title=f'{self.actuators[0].title} start:')
164
+
165
+ def evaluate_steps(self) -> int:
166
+ """Quick evaluation of the number of steps to stop the calculation if the evaluation os above the
167
+ configured limit"""
168
+ self.set_scan() # no possible quick evaluation, easiest to process it
169
+ return self.n_steps
170
+
171
+ def set_settings_titles(self):
172
+ if len(self.actuators) == 1:
173
+ self.settings.child('parsed_string').setOpts(title=f'{self.actuators[0].title} Parsed string:')
174
+