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.
- pymodaq/__init__.py +98 -0
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +1238 -0
- pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/factory.py +48 -0
- pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
- pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
- pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
- pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
- pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
- pymodaq/control_modules/daq_viewer.py +1517 -0
- pymodaq/control_modules/daq_viewer_ui.py +407 -0
- pymodaq/control_modules/mocks.py +57 -0
- pymodaq/control_modules/move_utility_classes.py +1141 -0
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +591 -0
- pymodaq/control_modules/viewer_utility_classes.py +670 -0
- pymodaq/daq_utils/__init__.py +0 -0
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +2396 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
- pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
- pymodaq/examples/__init__.py +0 -0
- pymodaq/examples/function_plotter.py +160 -0
- pymodaq/examples/nonlinearscanner.py +126 -0
- pymodaq/examples/qt_less_standalone_module.py +165 -0
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +25 -0
- pymodaq/extensions/adaptive/__init__.py +2 -0
- pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
- pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
- pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
- pymodaq/extensions/adaptive/utils.py +123 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
- pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
- pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
- pymodaq/extensions/bayesian/utils.py +180 -0
- pymodaq/extensions/console.py +73 -0
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/abstract.py +52 -0
- pymodaq/extensions/daq_logger/daq_logger.py +519 -0
- pymodaq/extensions/daq_logger/db/__init__.py +0 -0
- pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
- pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
- pymodaq/extensions/daq_logger/h5logging.py +84 -0
- pymodaq/extensions/daq_scan.py +1218 -0
- pymodaq/extensions/daq_scan_ui.py +241 -0
- pymodaq/extensions/data_mixer/__init__.py +0 -0
- pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
- pymodaq/extensions/data_mixer/data_mixer.py +262 -0
- pymodaq/extensions/data_mixer/model.py +108 -0
- pymodaq/extensions/data_mixer/models/__init__.py +0 -0
- pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
- pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
- pymodaq/extensions/data_mixer/parser.py +53 -0
- pymodaq/extensions/data_mixer/utils.py +23 -0
- pymodaq/extensions/h5browser.py +9 -0
- pymodaq/extensions/optimizers_base/__init__.py +0 -0
- pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
- pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
- pymodaq/extensions/optimizers_base/utils.py +427 -0
- pymodaq/extensions/pid/__init__.py +16 -0
- pymodaq/extensions/pid/actuator_controller.py +14 -0
- pymodaq/extensions/pid/daq_move_PID.py +154 -0
- pymodaq/extensions/pid/pid_controller.py +1016 -0
- pymodaq/extensions/pid/utils.py +189 -0
- pymodaq/extensions/utils.py +111 -0
- pymodaq/icon.ico +0 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/post_treatment/load_and_plot.py +352 -0
- pymodaq/resources/__init__.py +0 -0
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -0
- pymodaq/resources/setup_plugin.py +73 -0
- pymodaq/splash.png +0 -0
- pymodaq/utils/__init__.py +0 -0
- pymodaq/utils/array_manipulation.py +6 -0
- pymodaq/utils/calibration_camera.py +180 -0
- pymodaq/utils/chrono_timer.py +203 -0
- pymodaq/utils/config.py +53 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +158 -0
- pymodaq/utils/data.py +128 -0
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/exceptions.py +38 -0
- pymodaq/utils/gui_utils/__init__.py +10 -0
- pymodaq/utils/gui_utils/loader_utils.py +75 -0
- pymodaq/utils/gui_utils/utils.py +18 -0
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/__init__.py +2 -0
- pymodaq/utils/h5modules/module_saving.py +526 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
- pymodaq/utils/leco/director_utils.py +74 -0
- pymodaq/utils/leco/leco_director.py +166 -0
- pymodaq/utils/leco/pymodaq_listener.py +364 -0
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +74 -0
- pymodaq/utils/logger.py +6 -0
- pymodaq/utils/managers/__init__.py +0 -0
- pymodaq/utils/managers/batchscan_manager.py +346 -0
- pymodaq/utils/managers/modules_manager.py +589 -0
- pymodaq/utils/managers/overshoot_manager.py +242 -0
- pymodaq/utils/managers/preset_manager.py +229 -0
- pymodaq/utils/managers/preset_manager_utils.py +262 -0
- pymodaq/utils/managers/remote_manager.py +484 -0
- pymodaq/utils/math_utils.py +6 -0
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +10 -0
- pymodaq/utils/parameter/utils.py +6 -0
- pymodaq/utils/scanner/__init__.py +5 -0
- pymodaq/utils/scanner/scan_config.py +16 -0
- pymodaq/utils/scanner/scan_factory.py +259 -0
- pymodaq/utils/scanner/scan_selector.py +477 -0
- pymodaq/utils/scanner/scanner.py +324 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
- pymodaq/utils/scanner/scanners/__init__.py +1 -0
- pymodaq/utils/scanner/scanners/sequential.py +224 -0
- pymodaq/utils/scanner/scanners/tabular.py +319 -0
- pymodaq/utils/scanner/utils.py +110 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +50 -0
- pymodaq/utils/tcp_ip/__init__.py +6 -0
- pymodaq/utils/tcp_ip/mysocket.py +12 -0
- pymodaq/utils/tcp_ip/serializer.py +13 -0
- pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
- pymodaq-5.1.6.dist-info/METADATA +238 -0
- pymodaq-5.1.6.dist-info/RECORD +154 -0
- pymodaq-5.1.6.dist-info/WHEEL +4 -0
- pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
- 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
|
+
|