pymodaq 3.6.12__py3-none-any.whl → 4.0.1__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 (233) hide show
  1. pymodaq/__init__.py +13 -6
  2. pymodaq/control_modules/__init__.py +0 -7
  3. pymodaq/control_modules/daq_move.py +965 -2
  4. pymodaq/control_modules/daq_move_ui.py +319 -0
  5. pymodaq/control_modules/daq_viewer.py +1573 -3
  6. pymodaq/control_modules/daq_viewer_ui.py +393 -0
  7. pymodaq/control_modules/mocks.py +51 -0
  8. pymodaq/control_modules/move_utility_classes.py +709 -8
  9. pymodaq/control_modules/utils.py +256 -0
  10. pymodaq/control_modules/viewer_utility_classes.py +663 -6
  11. pymodaq/daq_utils.py +89 -0
  12. pymodaq/dashboard.py +91 -72
  13. pymodaq/examples/custom_app.py +12 -11
  14. pymodaq/examples/custom_viewer.py +10 -10
  15. pymodaq/examples/function_plotter.py +16 -13
  16. pymodaq/examples/nonlinearscanner.py +8 -6
  17. pymodaq/examples/parameter_ex.py +7 -7
  18. pymodaq/examples/preset_MockCamera.xml +1 -0
  19. pymodaq/extensions/__init__.py +16 -0
  20. pymodaq/extensions/console.py +76 -0
  21. pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
  22. pymodaq/extensions/daq_scan.py +1339 -0
  23. pymodaq/extensions/daq_scan_ui.py +240 -0
  24. pymodaq/extensions/h5browser.py +23 -0
  25. pymodaq/{pid → extensions/pid}/__init__.py +4 -2
  26. pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
  27. pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
  28. pymodaq/{pid → extensions/pid}/utils.py +52 -6
  29. pymodaq/extensions/utils.py +40 -0
  30. pymodaq/post_treatment/__init__.py +6 -0
  31. pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
  32. pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
  33. pymodaq/post_treatment/load_and_plot.py +219 -0
  34. pymodaq/post_treatment/process_to_scalar.py +263 -0
  35. pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
  36. pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
  37. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
  38. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
  39. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
  40. pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
  41. pymodaq/resources/VERSION +1 -1
  42. pymodaq/resources/config_template.toml +32 -13
  43. pymodaq/resources/preset_default.xml +1 -1
  44. pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
  45. pymodaq/utils/__init__.py +0 -29
  46. pymodaq/utils/abstract/__init__.py +48 -0
  47. pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
  48. pymodaq/utils/array_manipulation.py +379 -8
  49. pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
  50. pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
  51. pymodaq/utils/config.py +448 -0
  52. pymodaq/utils/conftests.py +5 -0
  53. pymodaq/utils/daq_utils.py +828 -8
  54. pymodaq/utils/data.py +1873 -7
  55. pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
  56. pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
  57. pymodaq/{daq_utils → utils}/enums.py +12 -7
  58. pymodaq/utils/exceptions.py +37 -0
  59. pymodaq/utils/factory.py +82 -0
  60. pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
  61. pymodaq/utils/gui_utils/custom_app.py +129 -0
  62. pymodaq/utils/gui_utils/file_io.py +66 -0
  63. pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
  64. pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
  65. pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
  66. pymodaq/utils/gui_utils/widgets/label.py +24 -0
  67. pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
  68. pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
  69. pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
  70. pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
  71. pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
  72. pymodaq/utils/h5modules/__init__.py +1 -0
  73. pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
  74. pymodaq/utils/h5modules/browsing.py +683 -0
  75. pymodaq/utils/h5modules/data_saving.py +839 -0
  76. pymodaq/utils/h5modules/h5logging.py +110 -0
  77. pymodaq/utils/h5modules/module_saving.py +350 -0
  78. pymodaq/utils/h5modules/saving.py +914 -0
  79. pymodaq/utils/h5modules/utils.py +85 -0
  80. pymodaq/utils/logger.py +64 -6
  81. pymodaq/utils/managers/action_manager.py +460 -0
  82. pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
  83. pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
  84. pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
  85. pymodaq/utils/managers/parameter_manager.py +110 -0
  86. pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
  87. pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
  88. pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
  89. pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
  90. pymodaq/utils/math_utils.py +546 -10
  91. pymodaq/{daq_utils → utils}/messenger.py +5 -1
  92. pymodaq/utils/parameter/__init__.py +2 -15
  93. pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
  94. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
  95. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
  96. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
  97. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
  98. pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
  99. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
  100. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
  101. pymodaq/utils/parameter/utils.py +206 -11
  102. pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
  103. pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
  104. pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
  105. pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
  106. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
  107. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
  108. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
  109. pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
  110. pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
  111. pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
  112. pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
  113. pymodaq/utils/plotting/navigator.py +355 -0
  114. pymodaq/utils/plotting/scan_selector.py +480 -0
  115. pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
  116. pymodaq/utils/plotting/utils/filter.py +538 -0
  117. pymodaq/utils/plotting/utils/lineout.py +224 -0
  118. pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
  119. pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
  120. pymodaq/utils/plotting/widgets.py +76 -0
  121. pymodaq/utils/scanner/__init__.py +10 -0
  122. pymodaq/utils/scanner/scan_factory.py +204 -0
  123. pymodaq/utils/scanner/scanner.py +271 -0
  124. pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
  125. pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
  126. pymodaq/utils/scanner/scanners/sequential.py +192 -0
  127. pymodaq/utils/scanner/scanners/tabular.py +294 -0
  128. pymodaq/utils/scanner/utils.py +83 -0
  129. pymodaq/utils/slicing.py +47 -0
  130. pymodaq/utils/svg/__init__.py +6 -0
  131. pymodaq/utils/svg/svg_renderer.py +20 -0
  132. pymodaq/utils/svg/svg_view.py +35 -0
  133. pymodaq/utils/svg/svg_viewer2D.py +51 -0
  134. pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
  135. pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
  136. pymodaq/utils/units.py +216 -0
  137. pymodaq-4.0.1.dist-info/METADATA +159 -0
  138. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
  140. pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
  141. pymodaq/daq_move/daq_move_gui.py +0 -279
  142. pymodaq/daq_move/daq_move_gui.ui +0 -534
  143. pymodaq/daq_move/daq_move_main.py +0 -1042
  144. pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
  145. pymodaq/daq_move/utility_classes.py +0 -671
  146. pymodaq/daq_scan.py +0 -2160
  147. pymodaq/daq_utils/array_manipulation.py +0 -386
  148. pymodaq/daq_utils/config.py +0 -273
  149. pymodaq/daq_utils/conftests.py +0 -7
  150. pymodaq/daq_utils/custom_parameter_tree.py +0 -9
  151. pymodaq/daq_utils/daq_enums.py +0 -133
  152. pymodaq/daq_utils/daq_utils.py +0 -1402
  153. pymodaq/daq_utils/exceptions.py +0 -71
  154. pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
  155. pymodaq/daq_utils/gui_utils/file_io.py +0 -75
  156. pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
  157. pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
  158. pymodaq/daq_utils/h5exporters.py +0 -242
  159. pymodaq/daq_utils/h5modules.py +0 -1559
  160. pymodaq/daq_utils/h5utils.py +0 -241
  161. pymodaq/daq_utils/managers/action_manager.py +0 -236
  162. pymodaq/daq_utils/managers/parameter_manager.py +0 -57
  163. pymodaq/daq_utils/math_utils.py +0 -705
  164. pymodaq/daq_utils/parameter/__init__.py +0 -1
  165. pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
  166. pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
  167. pymodaq/daq_utils/parameter/utils.py +0 -136
  168. pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
  169. pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
  170. pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
  171. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
  172. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
  173. pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
  174. pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
  175. pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
  176. pymodaq/daq_utils/plotting/items/__init__.py +0 -0
  177. pymodaq/daq_utils/plotting/navigator.py +0 -500
  178. pymodaq/daq_utils/plotting/scan_selector.py +0 -289
  179. pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
  180. pymodaq/daq_utils/plotting/utils/filter.py +0 -236
  181. pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
  182. pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
  183. pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
  184. pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
  185. pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
  186. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
  187. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
  188. pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
  189. pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
  190. pymodaq/daq_utils/scanner.py +0 -1289
  191. pymodaq/daq_utils/tree_layout/__init__.py +0 -0
  192. pymodaq/daq_viewer/__init__.py +0 -0
  193. pymodaq/daq_viewer/daq_gui_settings.py +0 -237
  194. pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
  195. pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
  196. pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
  197. pymodaq/daq_viewer/utility_classes.py +0 -673
  198. pymodaq/examples/logger_image/__init__.py +0 -0
  199. pymodaq/examples/logger_image/logger_displayer.py +0 -121
  200. pymodaq/examples/logger_image/setup.svg +0 -3119
  201. pymodaq/examples/logger_image/setup_svg.py +0 -114
  202. pymodaq/h5browser.py +0 -39
  203. pymodaq/utils/scanner.py +0 -15
  204. pymodaq-3.6.12.dist-info/METADATA +0 -39
  205. pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.12.dist-info/top_level.txt +0 -1
  207. /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
  208. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
  209. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
  210. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
  211. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
  212. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
  213. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
  214. /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
  215. /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
  216. /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
  217. /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
  218. /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
  219. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
  220. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
  221. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
  222. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
  223. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
  224. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
  225. /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
  226. /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
  227. /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
  228. /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
  229. /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
  230. /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
  231. /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
  232. /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
  233. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,10 @@
1
+ from importlib import import_module
2
+ from pathlib import Path
3
+
4
+ here = Path(__file__).parent.joinpath('scanners')
5
+ for path in here.iterdir():
6
+ if path.is_file():
7
+ import_module(f'.{path.stem}', 'pymodaq.utils.scanner.scanners')
8
+
9
+
10
+ from .scanner import Scanner # import this one after the scanners because they have to first be registered
@@ -0,0 +1,204 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 05/12/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from abc import ABCMeta, abstractmethod
10
+ from typing import Callable, Union, List, Tuple, TYPE_CHECKING
11
+
12
+
13
+ import numpy as np
14
+
15
+ from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter
16
+ from pymodaq.utils.factory import ObjectFactory
17
+ from pymodaq.utils.logger import set_logger, get_module_name
18
+ from pymodaq.utils.data import Axis, DataDistribution
19
+ from pymodaq.utils.abstract import abstract_attribute
20
+ from pymodaq.utils import math_utils as mutils
21
+ from pymodaq.utils import config as configmod
22
+
23
+ if TYPE_CHECKING:
24
+ from pymodaq.control_modules.daq_move import DAQ_Move
25
+ from pymodaq.utils.plotting.scan_selector import Selector
26
+
27
+
28
+ logger = set_logger(get_module_name(__file__))
29
+ config = configmod.Config()
30
+
31
+
32
+ class ScanParameterManager(ParameterManager):
33
+ settings_name = 'scanner_settings'
34
+
35
+ def __init__(self):
36
+ super().__init__()
37
+ self.settings_tree.header().setVisible(False)
38
+ self.settings_tree.setMinimumHeight(150)
39
+
40
+
41
+ class ScannerBase(ScanParameterManager, metaclass=ABCMeta):
42
+ """Abstract class for all Scanners
43
+
44
+ Attributes
45
+ ----------
46
+ params: List[dict]
47
+ list specifying the scanner set of parameters to properly configure all the scan steps
48
+ positions: np.ndarray
49
+ ndarray of all positions. First dimension is number of actuators, second is positions of a given actuator at
50
+ each step
51
+ axes_unique: List[np.ndarray]
52
+ list of ndarrays representing unique values of steps for each actuator in the scan
53
+ axes_indexes: np.ndarray
54
+ ndarray of indexes from axes_unique for each value in positions
55
+ n_steps: int
56
+ Number of scan steps. Equal to the second dimension of positions
57
+ n_axes: int
58
+ Number of actuators/scan axes. Equal to the first dimension of positions
59
+ """
60
+ params: List[dict] = abstract_attribute()
61
+ axes_unique: List[np.ndarray] = abstract_attribute()
62
+ axes_indexes: np.ndarray = abstract_attribute()
63
+ n_steps: int = abstract_attribute()
64
+ n_axes: int = abstract_attribute()
65
+ distribution: DataDistribution = abstract_attribute()
66
+
67
+ def __init__(self, actuators: List[DAQ_Move] = None):
68
+ super().__init__()
69
+ self.positions: np.ndarray = None
70
+ self.n_steps = 1
71
+ self._actuators: List[DAQ_Move] = None
72
+
73
+ self.actuators = actuators
74
+
75
+ self.set_settings_titles()
76
+
77
+ if self.check_steps():
78
+ self.set_scan()
79
+
80
+ def set_settings_titles(self):
81
+ """Update the settings accordingly with the selected actuators"""
82
+ ...
83
+
84
+ @property
85
+ def actuators(self):
86
+ return self._actuators
87
+
88
+ @actuators.setter
89
+ def actuators(self, actuators_name: List[DAQ_Move]):
90
+ self._actuators = actuators_name
91
+
92
+ def check_steps(self):
93
+ steps_limit = config('scan', 'steps_limit')
94
+ n_steps = self.evaluate_steps()
95
+ return n_steps <= steps_limit
96
+
97
+ def __call__(self, **kwargs):
98
+ return self(**kwargs)
99
+
100
+ @abstractmethod
101
+ def set_scan(self):
102
+ """To be reimplemented. Calculations of all mandatory attributes from the settings"""
103
+ ...
104
+
105
+ @abstractmethod
106
+ def get_nav_axes(self) -> List[Axis]:
107
+ """To be reimplemented. Calculations of all navigation axes from attributes"""
108
+ ...
109
+
110
+ @abstractmethod
111
+ def get_scan_shape(self) -> Tuple[int]:
112
+ """To be reimplemented. Calculations of all the final shape of the scan"""
113
+ ...
114
+
115
+ @abstractmethod
116
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
117
+ """To be reimplemented. Calculations of indexes within the scan"""
118
+ ...
119
+
120
+ def get_info_from_positions(self, positions: np.ndarray):
121
+ """Set mandatory attributes from a ndarray of positions"""
122
+ if positions is not None:
123
+ if len(positions.shape) == 1:
124
+ positions = np.expand_dims(positions, 1)
125
+ axes_unique = []
126
+ for ax in positions.T:
127
+ axes_unique.append(np.unique(ax))
128
+ axes_indexes = np.zeros_like(positions, dtype=int)
129
+ for ind in range(positions.shape[0]):
130
+ for ind_pos, pos in enumerate(positions[ind]):
131
+ axes_indexes[ind, ind_pos] = mutils.find_index(axes_unique[ind_pos], pos)[0][0]
132
+
133
+ self.n_axes = len(axes_unique)
134
+ self.axes_unique = axes_unique
135
+ self.axes_indexes = axes_indexes
136
+ self.positions = positions
137
+ self.n_steps = positions.shape[0]
138
+
139
+ @abstractmethod
140
+ def evaluate_steps(self):
141
+ """To be reimplemented. Quick evaluation of the current numbers of scan steps from the settings
142
+ """
143
+ ...
144
+
145
+ def update_model(self):
146
+ """Method to reimplement and use for scanners using table_view types Parameters to set and apply the underlying
147
+ model
148
+
149
+ See Also
150
+ --------
151
+ SequentialScanner, TabularScanner, pymodaq.utils.parameter.pymodaq_ptypes.tableview
152
+ """
153
+ ...
154
+
155
+ def value_changed(self, param):
156
+ self.evaluate_steps()
157
+
158
+ @abstractmethod
159
+ def update_from_scan_selector(self, scan_selector: Selector):
160
+ """To be reimplemented. Process the Selector object to set the Scanner settings
161
+
162
+ See Also
163
+ --------
164
+ Selector, ScanSelector
165
+ """
166
+ ...
167
+
168
+
169
+ class ScannerFactory(ObjectFactory):
170
+ """Factory class registering and storing Scanners"""
171
+
172
+ @classmethod
173
+ def register(cls, key: str, sub_key: str = '') -> Callable:
174
+ def inner_wrapper(wrapped_class: Union[Callable]) -> Callable:
175
+ if cls.__name__ not in cls._builders:
176
+ cls._builders[cls.__name__] = {}
177
+ if key not in cls._builders[cls.__name__]:
178
+ cls._builders[cls.__name__][key] = {}
179
+ if sub_key not in cls._builders[cls.__name__][key]:
180
+ cls._builders[cls.__name__][key][sub_key] = wrapped_class
181
+ else:
182
+ logger.warning(f'The {cls.__name__}/{key}/{sub_key} builder is already registered. Replacing it')
183
+ return wrapped_class
184
+
185
+ return inner_wrapper
186
+
187
+ @classmethod
188
+ def create(cls, key, sub_key, **kwargs) -> ScannerBase:
189
+ builder = cls._builders[cls.__name__].get(key).get(sub_key)
190
+ if not builder:
191
+ raise ValueError(key)
192
+ return builder(**kwargs)
193
+
194
+ def get(self, scan_type, scan_sub_type, **kwargs):
195
+ return self.create(scan_type, scan_sub_type, **kwargs)
196
+
197
+ def scan_types(self) -> List[str]:
198
+ """Returns the list of scan types, main identifier of a given scanner"""
199
+ return sorted(list(self.builders[self.__class__.__name__].keys()))
200
+
201
+ def scan_sub_types(self, scan_type: str) -> List[str]:
202
+ """Returns the list of scan subtypes, second identifier of a given scanner of type scan_type"""
203
+ return list(self.builders[self.__class__.__name__][scan_type].keys())
204
+
@@ -0,0 +1,271 @@
1
+ from __future__ import annotations
2
+ from typing import Tuple, List, TYPE_CHECKING
3
+ from collections import OrderedDict
4
+
5
+
6
+ from qtpy import QtWidgets, QtCore
7
+ from qtpy.QtCore import QObject, Signal, Slot
8
+
9
+ from pymodaq.utils.logger import set_logger, get_module_name
10
+ from pymodaq.utils.config import Config
11
+ from pymodaq.utils.scanner.scan_factory import ScannerFactory, ScannerBase
12
+ from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter
13
+ import pymodaq.utils.daq_utils as utils
14
+ from pymodaq.utils.scanner.utils import ScanInfo
15
+ from pymodaq.utils.plotting.scan_selector import Selector
16
+
17
+ if TYPE_CHECKING:
18
+ from pymodaq.control_modules.daq_move import DAQ_Move
19
+
20
+
21
+ logger = set_logger(get_module_name(__file__))
22
+ config = Config()
23
+ scanner_factory = ScannerFactory()
24
+
25
+
26
+ class Scanner(QObject, ParameterManager):
27
+ """Main Object to define a PyMoDAQ scan and create a UI to set it
28
+
29
+ Parameters
30
+ ----------
31
+ parent_widget: QtWidgets.QWidget
32
+ scanner_items: list of GraphicItems
33
+ used by ScanSelector for chosing scan area or linear traces
34
+ actuators: List[DAQ_Move]
35
+ list actuators names
36
+
37
+ See Also
38
+ --------
39
+ ScanSelector, ScannerBase, TableModelSequential, TableModelTabular, pymodaq_types.TableViewCustom
40
+ """
41
+ scanner_updated_signal = Signal()
42
+ settings_name = 'scanner'
43
+
44
+ params = [
45
+ {'title': 'Calculate positions:', 'name': 'calculate_positions', 'type': 'action'},
46
+ {'title': 'N steps:', 'name': 'n_steps', 'type': 'int', 'value': 0, 'readonly': True},
47
+ {'title': 'Scan type:', 'name': 'scan_type', 'type': 'list', 'limits': scanner_factory.scan_types()},
48
+ {'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'list',
49
+ 'limits': scanner_factory.scan_sub_types(scanner_factory.scan_types()[0])},
50
+ ]
51
+
52
+ def __init__(self, parent_widget: QtWidgets.QWidget = None, scanner_items=OrderedDict([]),
53
+ actuators: List[DAQ_Move] = []):
54
+ QObject.__init__(self)
55
+ ParameterManager.__init__(self)
56
+ if parent_widget is None:
57
+ parent_widget = QtWidgets.QWidget()
58
+ self.parent_widget = parent_widget
59
+ self._scanner_settings_widget = None
60
+
61
+ self.connect_things()
62
+ self._scanner: ScannerBase = None
63
+
64
+ self.setup_ui()
65
+ self.actuators = actuators
66
+ self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
67
+
68
+ def setup_ui(self):
69
+ self.parent_widget.setLayout(QtWidgets.QVBoxLayout())
70
+ self.parent_widget.layout().setContentsMargins(0, 0, 0, 0)
71
+ self.parent_widget.layout().addWidget(self.settings_tree)
72
+ self._scanner_settings_widget = QtWidgets.QWidget()
73
+ self._scanner_settings_widget.setLayout(QtWidgets.QVBoxLayout())
74
+ self._scanner_settings_widget.layout().setContentsMargins(0, 0, 0, 0)
75
+ self.parent_widget.layout().addWidget(self._scanner_settings_widget)
76
+ self.settings_tree.setMinimumHeight(110)
77
+ self.settings_tree.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
78
+
79
+ def set_scanner(self):
80
+ try:
81
+ self._scanner: ScannerBase = scanner_factory.get(self.settings['scan_type'],
82
+ self.settings['scan_sub_type'], actuators=self.actuators)
83
+
84
+ while True:
85
+ child = self._scanner_settings_widget.layout().takeAt(0)
86
+ if not child:
87
+ break
88
+ child.widget().deleteLater()
89
+ QtWidgets.QApplication.processEvents()
90
+
91
+ self._scanner_settings_widget.layout().addWidget(self._scanner.settings_tree)
92
+ self._scanner.settings.sigTreeStateChanged.connect(self._update_steps)
93
+
94
+ except ValueError:
95
+ pass
96
+
97
+ def get_scanner_sub_settings(self):
98
+ """Get the current ScannerBase implementation's settings"""
99
+ return self._scanner.settings
100
+
101
+ def value_changed(self, param: Parameter):
102
+ if param.name() == 'scan_type':
103
+ self.settings.child('scan_sub_type').setOpts(
104
+ limits=scanner_factory.scan_sub_types(param.value()))
105
+ if param.name() in ['scan_type', 'scan_sub_type']:
106
+ self.set_scanner()
107
+
108
+ self.settings.child('n_steps').setValue(self._scanner.evaluate_steps())
109
+
110
+ @property
111
+ def actuators(self):
112
+ """list of str: Returns as a list the name of the selected actuators to describe the actual scan"""
113
+ return self._actuators
114
+
115
+ @actuators.setter
116
+ def actuators(self, act_list):
117
+ self._actuators = act_list
118
+ self.set_scanner()
119
+
120
+ def set_scan_type_and_subtypes(self, scan_type: str, scan_subtype: str):
121
+ """Convenience function to set the main scan type
122
+
123
+ Parameters
124
+ ----------
125
+ scan_type: str
126
+ one of registered Scanner main identifier
127
+ scan_subtype: list of str or None
128
+ one of registered Scanner second identifier for a given main identifier
129
+
130
+ See Also
131
+ --------
132
+ ScannerFactory
133
+ """
134
+ if scan_type in scanner_factory.scan_types():
135
+ self.settings.child('scan_type').setValue(scan_type)
136
+
137
+ if scan_subtype is not None:
138
+ if scan_subtype in scanner_factory.scan_sub_types(scan_type):
139
+ self.settings.child('scan_sub_type').setValue(scan_subtype)
140
+
141
+ def set_scan_from_settings(self, settings: Parameter, scanner_settings: Parameter):
142
+
143
+ self.set_scan_type_and_subtypes(settings['scan_type'],
144
+ settings['scan_sub_type'])
145
+ self.settings.restoreState(settings.saveState())
146
+ self._scanner.settings.restoreState(scanner_settings.saveState())
147
+
148
+ @property
149
+ def scan_type(self) -> str:
150
+ return self.settings['scan_type']
151
+
152
+ @property
153
+ def scan_sub_type(self) -> str:
154
+ return self.settings['scan_sub_type']
155
+
156
+ def connect_things(self):
157
+ self.settings.child('calculate_positions').sigActivated.connect(self.set_scan)
158
+
159
+ def get_scan_info(self) -> ScanInfo:
160
+ """Get a summary of the configured scan as a ScanInfo object"""
161
+ return ScanInfo(self._scanner.n_steps, positions=self._scanner.positions,
162
+ axes_indexes=self._scanner.axes_indexes, axes_unique=self._scanner.axes_unique,
163
+ selected_actuators=[act.title for act in self.actuators])
164
+
165
+ def get_nav_axes(self):
166
+ return self._scanner.get_nav_axes()
167
+
168
+ def get_scan_shape(self):
169
+ return self._scanner.get_scan_shape()
170
+
171
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
172
+ """To be reimplemented. Calculations of indexes within the scan"""
173
+ return self._scanner.get_indexes_from_scan_index(scan_index)
174
+
175
+ def _update_steps(self):
176
+ self.settings.child('n_steps').setValue(self.n_steps)
177
+
178
+ @property
179
+ def n_steps(self):
180
+ return self._scanner.evaluate_steps()
181
+
182
+ @property
183
+ def n_axes(self):
184
+ return self._scanner.n_axes
185
+
186
+ @property
187
+ def positions(self):
188
+ return self._scanner.positions
189
+
190
+ @property
191
+ def axes_indexes(self):
192
+ return self._scanner.axes_indexes
193
+
194
+ @property
195
+ def axes_unique(self):
196
+ return self._scanner.axes_unique
197
+
198
+ @property
199
+ def distribution(self):
200
+ return self._scanner.distribution
201
+
202
+ def set_scan(self):
203
+ """Process the settings options to calculate the scan positions
204
+
205
+ Returns
206
+ -------
207
+ bool: True if the processed number of steps if **higher** than the configured number of steps
208
+ """
209
+ oversteps = config('scan', 'steps_limit')
210
+ if self._scanner.evaluate_steps() > oversteps:
211
+ return True
212
+ self._scanner.set_scan()
213
+ self.settings.child('n_steps').setValue(self.n_steps)
214
+ self.scanner_updated_signal.emit()
215
+ return False
216
+
217
+ def update_from_scan_selector(self, scan_selector: Selector):
218
+ self._scanner.update_from_scan_selector(scan_selector)
219
+
220
+
221
+ def main():
222
+ from pymodaq.utils.parameter import ParameterTree
223
+ app = QtWidgets.QApplication(sys.argv)
224
+
225
+ class MoveMock:
226
+ def __init__(self, ind: int = 0):
227
+ self.title = f'act_{ind}'
228
+ self.units = f'units_{ind}'
229
+
230
+ actuators = [MoveMock(ind) for ind in range(3)]
231
+
232
+ params = [{'title': 'Actuators', 'name': 'actuators', 'type': 'itemselect',
233
+ 'value': dict(all_items=[act.title for act in actuators], selected=[])},
234
+ {'title': 'Set Scan', 'name': 'set_scan', 'type': 'action'},
235
+ ]
236
+ settings = Parameter.create(name='settings', type='group', children=params)
237
+ settings_tree = ParameterTree()
238
+ settings_tree.setParameters(settings)
239
+
240
+ widget_main = QtWidgets.QWidget()
241
+ widget_main.setLayout(QtWidgets.QVBoxLayout())
242
+ widget_scanner = QtWidgets.QWidget()
243
+ widget_main.layout().addWidget(settings_tree)
244
+ widget_main.layout().addWidget(widget_scanner)
245
+ scanner = Scanner(widget_scanner, actuators=actuators)
246
+
247
+ def update_actuators(param):
248
+ scanner.actuators = [utils.find_objects_in_list_from_attr_name_val(actuators, 'title', act_str,
249
+ return_first=True)[0]
250
+ for act_str in param.value()['selected']]
251
+
252
+ def print_info():
253
+ print('info:')
254
+ print(scanner.get_scan_info())
255
+ print('positions:')
256
+ print(scanner.positions)
257
+ print('nav:')
258
+ print(scanner.get_nav_axes())
259
+
260
+ settings.child('actuators').sigValueChanged.connect(update_actuators)
261
+ settings.child('set_scan').sigActivated.connect(scanner.set_scan)
262
+ scanner.scanner_updated_signal.connect(print_info)
263
+ widget_main.show()
264
+ sys.exit(app.exec_())
265
+
266
+
267
+ if __name__ == '__main__':
268
+ import sys
269
+ from qtpy import QtWidgets
270
+ main()
271
+
@@ -0,0 +1,117 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 05/12/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ from typing import List, Tuple
8
+
9
+ import numpy as np
10
+ from pymodaq.utils.data import Axis, DataDistribution
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
+ from pymodaq.utils.plotting.scan_selector import Selector
15
+
16
+ from ..scan_factory import ScannerFactory, ScannerBase, ScanParameterManager
17
+
18
+
19
+ logger = set_logger(get_module_name(__file__))
20
+ config = configmod.Config()
21
+
22
+
23
+ @ScannerFactory.register('Scan1D', 'Linear')
24
+ class Scan1DLinear(ScannerBase):
25
+ params = [
26
+ {'title': 'Start:', 'name': 'start', 'type': 'float', 'value': config('scan', 'scan1D', 'start')},
27
+ {'title': 'Stop:', 'name': 'stop', 'type': 'float', 'value': config('scan', 'scan1D', 'stop')},
28
+ {'title': 'Step:', 'name': 'step', 'type': 'float', 'value': config('scan', 'scan1D', 'step')}
29
+ ]
30
+ n_axes = 1
31
+ distribution = DataDistribution['uniform']
32
+
33
+ def __init__(self, actuators: List = None, **_ignored):
34
+ ScannerBase.__init__(self, actuators=actuators)
35
+
36
+ def set_scan(self):
37
+ self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
38
+ self.settings['step'])
39
+ self.get_info_from_positions(self.positions)
40
+
41
+ def set_settings_titles(self):
42
+ if len(self.actuators) == 1:
43
+ self.settings.child('start').setOpts(title=f'{self.actuators[0].title} start:')
44
+ self.settings.child('stop').setOpts(title=f'{self.actuators[0].title} stop:')
45
+ self.settings.child('step').setOpts(title=f'{self.actuators[0].title} step:')
46
+
47
+ def evaluate_steps(self) -> int:
48
+ n_steps = int(np.abs((self.settings['stop'] - self.settings['start']) / self.settings['step']) + 1)
49
+ return n_steps
50
+
51
+ def get_nav_axes(self) -> List[Axis]:
52
+ return [Axis(label=f'{self.actuators[0].title}',
53
+ units=f'{self.actuators[0].units}',
54
+ data=np.squeeze(self.positions))]
55
+
56
+ def get_scan_shape(self) -> Tuple[int]:
57
+ return len(self.positions),
58
+
59
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
60
+ """To be reimplemented. Calculations of indexes within the scan"""
61
+ return tuple(self.axes_indexes[scan_index])
62
+
63
+ def update_from_scan_selector(self, scan_selector: Selector):
64
+ coordinates = scan_selector.get_coordinates()
65
+ if coordinates.shape == (2, 2) or coordinates.shape == (2, 1):
66
+ self.settings.child('start').setValue(coordinates[0, 0])
67
+ self.settings.child('stop').setValue(coordinates[1, 0])
68
+
69
+
70
+ @ScannerFactory.register('Scan1D', 'Random')
71
+ class Scan1DRandom(Scan1DLinear):
72
+ def __init__(self, actuators: List = None, **_ignored):
73
+ super().__init__(actuators=actuators)
74
+
75
+ def set_scan(self):
76
+ self.positions = mutils.linspace_step(self.settings['start'], self.settings['stop'],
77
+ self.settings['step'])
78
+ np.random.shuffle(self.positions)
79
+ self.get_info_from_positions(self.positions)
80
+ self.set_settings_titles()
81
+
82
+ try:
83
+ import adaptive
84
+
85
+
86
+ @ScannerFactory.register('Scan1D', 'Adaptive')
87
+ class Scan1DAdaptive(Scan1DLinear):
88
+ params = [
89
+ {'title': 'Loss type', 'name': 'scan_loss', 'type': 'list',
90
+ 'limits': ['default', 'curvature', 'uniform'], 'tip': 'Type of loss used by the algo. to determine next points'},
91
+ {'title': 'Start:', 'name': 'start', 'type': 'float', 'value': config('scan', 'scan1D', 'start')},
92
+ {'title': 'Stop:', 'name': 'stop', 'type': 'float', 'value': config('scan', 'scan1D', 'stop')},
93
+ ]
94
+ distribution = DataDistribution['spread']
95
+
96
+ def __init__(self, actuators: List = None, **_ignored):
97
+ super().__init__(actuators=actuators)
98
+
99
+ def set_scan(self):
100
+ self.axes_unique = [np.array([])]
101
+ self.axes_indexes = np.array([], dtype=int)
102
+ self.positions = np.array([self.settings['start'], self.settings['stop']])
103
+
104
+ def evaluate_steps(self) -> int:
105
+ return 1
106
+
107
+ def get_nav_axes(self) -> List[Axis]:
108
+ return [Axis(label=f'{self.actuators[0].mod_name} axis',
109
+ units=f'{self.actuators[0].units}',
110
+ data=self.positions[0])]
111
+
112
+ def get_scan_shape(self) -> Tuple[int]:
113
+ return len(self.positions),
114
+
115
+ except ModuleNotFoundError:
116
+ logger.info('adaptive module is not present, no adaptive scan possible')
117
+