pymodaq 3.6.13__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.13.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.13.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 -686
  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.13.dist-info/METADATA +0 -39
  205. pymodaq-3.6.13.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.13.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.13.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,293 @@
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
+ logger = set_logger(get_module_name(__file__))
19
+ config = configmod.Config()
20
+
21
+
22
+ @ScannerFactory.register('Scan2D', 'Linear')
23
+ class Scan2DLinear(ScannerBase):
24
+ params = [{'title': 'Start Ax1:', 'name': 'start_axis1', 'type': 'float',
25
+ 'value': config('scan', 'scan2D', 'linear', 'start1')},
26
+ {'title': 'Start Ax2:', 'name': 'start_axis2', 'type': 'float',
27
+ 'value': config('scan', 'scan2D', 'linear', 'start2')},
28
+ {'title': 'Step Ax1:', 'name': 'step_axis1', 'type': 'float',
29
+ 'value': config('scan', 'scan2D', 'linear', 'step1')},
30
+ {'title': 'Step Ax2:', 'name': 'step_axis2', 'type': 'float',
31
+ 'value': config('scan', 'scan2D', 'linear', 'step2')},
32
+ {'title': 'Stop Ax1:', 'name': 'stop_axis1', 'type': 'float',
33
+ 'value': config('scan', 'scan2D', 'linear', 'stop1')},
34
+ {'title': 'Stop Ax2:', 'name': 'stop_axis2', 'type': 'float',
35
+ 'value': config('scan', 'scan2D', 'linear', 'stop2')},
36
+ ]
37
+ n_axes = 2
38
+ distribution = DataDistribution['uniform']
39
+
40
+ def __init__(self, actuators: List = None, **_ignored):
41
+ super().__init__(actuators=actuators)
42
+
43
+ def get_pos(self):
44
+ starts = np.array([self.settings['start_axis1'], self.settings['start_axis2']])
45
+ stops = np.array([self.settings['stop_axis1'], self.settings['stop_axis2']])
46
+ steps = np.array([self.settings['step_axis1'], self.settings['step_axis2']])
47
+ return starts, stops, steps
48
+
49
+ def evaluate_steps(self) -> int:
50
+ starts, stops, steps = self.get_pos()
51
+ n_steps = 1
52
+ for ind in range(starts.size):
53
+ n_steps *= np.abs((stops[ind] - starts[ind]) / steps[ind]) + 1
54
+ return int(n_steps)
55
+
56
+ def set_scan(self):
57
+ starts, stops, steps = self.get_pos()
58
+ if np.any(np.abs(steps) < 1e-12) or \
59
+ np.any(np.sign(stops - starts) != np.sign(steps)) or \
60
+ np.any(starts == stops):
61
+
62
+ return np.array([starts])
63
+
64
+ else:
65
+ axis_1_unique = mutils.linspace_step(starts[0], stops[0], steps[0])
66
+ axis_2_unique = mutils.linspace_step(starts[1], stops[1], steps[1])
67
+
68
+ positions = []
69
+ for ind_x, pos1 in enumerate(axis_1_unique):
70
+ for ind_y, pos2 in enumerate(axis_2_unique):
71
+ positions.append([pos1, pos2])
72
+
73
+ self.get_info_from_positions(np.array(positions))
74
+
75
+ def set_settings_titles(self):
76
+ if len(self.actuators) == 2:
77
+ self.settings.child('start_axis1').setOpts(title=f'{self.actuators[0].title} start:')
78
+ self.settings.child('stop_axis1').setOpts(title=f'{self.actuators[0].title} stop:')
79
+ self.settings.child('step_axis1').setOpts(title=f'{self.actuators[0].title} step:')
80
+ self.settings.child('start_axis2').setOpts(title=f'{self.actuators[1].title} start:')
81
+ self.settings.child('stop_axis2').setOpts(title=f'{self.actuators[1].title} stop:')
82
+ self.settings.child('step_axis2').setOpts(title=f'{self.actuators[1].title} step:')
83
+
84
+ def get_nav_axes(self) -> List[Axis]:
85
+ return [Axis(label=f'{act.title}',
86
+ units=f'{act.units}',
87
+ data=self.axes_unique[ind],
88
+ index=ind) for ind, act in enumerate(self.actuators)]
89
+
90
+ def get_scan_shape(self) -> Tuple[int]:
91
+ return tuple([len(axis) for axis in self.axes_unique])
92
+
93
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
94
+ """To be reimplemented. Calculations of indexes within the scan"""
95
+ return tuple(self.axes_indexes[scan_index])
96
+
97
+ def update_from_scan_selector(self, scan_selector: Selector):
98
+ coordinates = scan_selector.get_coordinates()
99
+ if coordinates.shape == (2, 2):
100
+ self.settings.child('start_axis1').setValue(coordinates[0, 0])
101
+ self.settings.child('start_axis2').setValue(coordinates[0, 1])
102
+ self.settings.child('stop_axis1').setValue(coordinates[1, 0])
103
+ self.settings.child('stop_axis2').setValue(coordinates[1, 1])
104
+
105
+
106
+ @ScannerFactory.register('Scan2D', 'LinearBack&Force')
107
+ class Scan2DLinearBF(Scan2DLinear):
108
+ def __init__(self, actuators: List = None, **_ignored):
109
+ super().__init__(actuators=actuators)
110
+
111
+ def set_scan(self):
112
+ starts, stops, steps = self.get_pos()
113
+ if np.any(np.abs(steps) < 1e-12) or \
114
+ np.any(np.sign(stops - starts) != np.sign(steps)) or \
115
+ np.any(starts == stops):
116
+
117
+ return np.array([starts])
118
+
119
+ else:
120
+ axis_1_unique = mutils.linspace_step(starts[0], stops[0], steps[0])
121
+ axis_2_unique = mutils.linspace_step(starts[1], stops[1], steps[1])
122
+
123
+ positions = []
124
+ for ind_x, pos1 in enumerate(axis_1_unique):
125
+ for ind_y, pos2 in enumerate(axis_2_unique):
126
+ if not mutils.odd_even(ind_x):
127
+ positions.append([pos1, pos2])
128
+ else:
129
+ positions.append([pos1, axis_2_unique[len(axis_2_unique) - ind_y - 1]])
130
+
131
+ self.get_info_from_positions(np.array(positions))
132
+
133
+
134
+ @ScannerFactory.register('Scan2D', 'Random')
135
+ class Scan2DRandom(Scan2DLinear):
136
+ def __init__(self, actuators: List = None, **_ignored):
137
+ super().__init__(actuators=actuators)
138
+
139
+ def set_scan(self):
140
+ super().set_scan()
141
+ np.random.shuffle(self.positions)
142
+ self.get_info_from_positions(self.positions)
143
+
144
+
145
+ @ScannerFactory.register('Scan2D', 'Spiral')
146
+ class Scan2DSpiral(Scan2DLinear):
147
+ params = [{'title': 'Center Ax1:', 'name': 'center_axis1', 'type': 'float',
148
+ 'value': config('scan', 'scan2D', 'spiral', 'center1')},
149
+ {'title': 'Center Ax2:', 'name': 'center_axis2', 'type': 'float',
150
+ 'value': config('scan', 'scan2D', 'spiral', 'center2')},
151
+ {'title': 'Rmax Ax1:', 'name': 'rmax_axis1', 'type': 'float',
152
+ 'value': config('scan', 'scan2D', 'spiral', 'rmax1')},
153
+ {'title': 'Rmax Ax2:', 'name': 'rmax_axis2', 'type': 'float',
154
+ 'value': config('scan', 'scan2D', 'spiral', 'rmax2')},
155
+ {'title': 'Npts/axis', 'name': 'npts_by_axis', 'type': 'int', 'min': 1,
156
+ 'value': config('scan', 'scan2D', 'spiral', 'npts')},
157
+ {'title': 'Step Ax1:', 'name': 'step_axis1', 'type': 'float', 'value': 0., 'readonly': True},
158
+ {'title': 'Step Ax2:', 'name': 'step_axis2', 'type': 'float', 'value': 0., 'readonly': True},
159
+ ]
160
+
161
+ def __init__(self, actuators: List = None, **_ignored):
162
+ super().__init__(actuators=actuators)
163
+
164
+ def set_settings_titles(self):
165
+ if len(self.actuators) == 2:
166
+ self.settings.child('center_axis1').setOpts(title=f'Center {self.actuators[0].title}:')
167
+ self.settings.child('rmax_axis1').setOpts(title=f'Rmax {self.actuators[0].title}:')
168
+ self.settings.child('step_axis1').setOpts(title=f'Step {self.actuators[0].title}:')
169
+ self.settings.child('center_axis2').setOpts(title=f'Center {self.actuators[1].title}:')
170
+ self.settings.child('rmax_axis2').setOpts(title=f'Rmax {self.actuators[1].title}:')
171
+ self.settings.child('step_axis2').setOpts(title=f'Step {self.actuators[1].title}:')
172
+
173
+ def value_changed(self, param):
174
+ starts, rmaxs, rsteps = self.get_pos()
175
+ self.settings.child('step_axis1').setValue(rsteps[0])
176
+ self.settings.child('step_axis2').setValue(rsteps[1])
177
+
178
+ def get_pos(self):
179
+ """Get centers, radius and n steps from settings
180
+
181
+ Returns
182
+ ----------
183
+ centers: np.ndarray
184
+ containing the center positions of the scan
185
+ rmaxs: np.ndarray
186
+ containing the maximum radius (ellipse axes) in each direction
187
+ r_steps: np.ndarray
188
+ steps size in both directions
189
+ """
190
+ centers = np.array([self.settings['center_axis1'], self.settings['center_axis2']])
191
+ rmaxs = np.array([self.settings['rmax_axis1'], self.settings['rmax_axis2']])
192
+ r_steps = 2 * rmaxs / self.settings['npts_by_axis']
193
+ return centers, rmaxs, r_steps
194
+
195
+ def evaluate_steps(self) -> int:
196
+ return int(self.settings['npts_by_axis'] + 1) ** 2
197
+
198
+ def set_scan(self):
199
+ starts, rmaxs, rsteps = self.get_pos()
200
+
201
+ if np.any(np.array(rmaxs) == 0) or np.any(np.abs(rmaxs) < 1e-12) or np.any(np.abs(rsteps) < 1e-12):
202
+ positions = np.array([starts])
203
+
204
+ else:
205
+ Nlin = self.settings['npts_by_axis'] / 2
206
+ axis_1_indexes = [0]
207
+ axis_2_indexes = [0]
208
+ ind = 0
209
+ flag = True
210
+
211
+ while flag:
212
+ if mutils.odd_even(ind):
213
+ step = 1
214
+ else:
215
+ step = -1
216
+ if flag:
217
+
218
+ for ind_step in range(ind):
219
+ axis_1_indexes.append(axis_1_indexes[-1] + step)
220
+ axis_2_indexes.append(axis_2_indexes[-1])
221
+ if len(axis_1_indexes) >= (2 * Nlin + 1) ** 2:
222
+ flag = False
223
+ break
224
+ if flag:
225
+ for ind_step in range(ind):
226
+ axis_1_indexes.append(axis_1_indexes[-1])
227
+ axis_2_indexes.append(axis_2_indexes[-1] + step)
228
+ if len(axis_1_indexes) >= (2 * Nlin + 1) ** 2:
229
+ flag = False
230
+ break
231
+ ind += 1
232
+
233
+ positions = []
234
+ for ind in range(len(axis_1_indexes)):
235
+ positions.append(np.array([axis_1_indexes[ind] * rsteps[0] + starts[0],
236
+ axis_2_indexes[ind] * rsteps[1] + starts[1]]))
237
+
238
+ self.get_info_from_positions(np.array(positions))
239
+
240
+ def update_from_scan_selector(self, scan_selector: Selector):
241
+ coordinates = scan_selector.get_coordinates()
242
+ if coordinates.shape == (2, 2):
243
+ self.settings.child('center_axis1').setValue((coordinates[0, 0] + coordinates[1, 0]) / 2)
244
+ self.settings.child('center_axis2').setValue((coordinates[0, 1] + coordinates[1, 1]) / 2)
245
+ self.settings.child('rmax_axis1').setValue(abs(coordinates[1, 0] - coordinates[0, 0]) / 2)
246
+ self.settings.child('rmax_axis2').setValue(abs(coordinates[1, 1] - coordinates[0, 1]) / 2)
247
+
248
+
249
+ try:
250
+ import adaptive
251
+
252
+ @ScannerFactory.register('Scan2D', 'Adaptive')
253
+ class Scan2DAdaptive(Scan2DLinear):
254
+ params = [
255
+ {'title': 'Loss type', 'name': 'scan_loss', 'type': 'list',
256
+ 'limits': ['default', 'curvature', 'uniform'],
257
+ 'tip': 'Type of loss used by the algo. to determine next points'},
258
+
259
+ {'title': 'Start Ax1:', 'name': 'start_axis1', 'type': 'float',
260
+ 'value': config('scan', 'scan2D', 'linear', 'start1')},
261
+ {'title': 'Start Ax2:', 'name': 'start_axis2', 'type': 'float',
262
+ 'value': config('scan', 'scan2D', 'linear', 'start2')},
263
+ {'title': 'Stop Ax1:', 'name': 'stop_axis1', 'type': 'float',
264
+ 'value': config('scan', 'scan2D', 'linear', 'stop1')},
265
+ {'title': 'Stop Ax2:', 'name': 'stop_axis2', 'type': 'float',
266
+ 'value': config('scan', 'scan2D', 'linear', 'stop2')},
267
+ ]
268
+ distribution = DataDistribution['spread']
269
+
270
+ def __init__(self, actuators: List = None, **_ignored):
271
+ super().__init__(actuators=actuators)
272
+
273
+ def set_scan(self):
274
+
275
+ self.axes_unique = [np.array([]), np.array([])]
276
+ self.axes_indexes = np.array([], dtype=int)
277
+ self.positions = np.zeros((0, 2))
278
+
279
+ def evaluate_steps(self) -> int:
280
+ return 1
281
+
282
+ def get_nav_axes(self) -> List[Axis]:
283
+ return [Axis(label=f'{act.mod_name} axis',
284
+ units=f'{act.units}',
285
+ data=self.positions[:, ind],
286
+ index=ind) for ind, act in enumerate(self.actuators)]
287
+
288
+ def get_scan_shape(self) -> Tuple[int]:
289
+ return () # unknown shape
290
+
291
+ except ModuleNotFoundError:
292
+ logger.info('adaptive module is not present, no adaptive scan possible')
293
+
@@ -0,0 +1,192 @@
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
+
11
+ from qtpy import QtCore, QtWidgets
12
+ from pymodaq.utils.data import Axis, DataDistribution
13
+ from pymodaq.utils.logger import set_logger, get_module_name
14
+ from pymodaq.utils import math_utils as mutils
15
+ from pymodaq.utils import config as configmod
16
+ from pymodaq.utils import gui_utils as gutils
17
+ from ..scan_factory import ScannerFactory, ScannerBase, ScanParameterManager
18
+ from pymodaq.utils.parameter import utils as putils
19
+ from pymodaq.utils.parameter.pymodaq_ptypes import TableViewCustom
20
+ from pymodaq.utils.plotting.scan_selector import Selector
21
+
22
+ logger = set_logger(get_module_name(__file__))
23
+ config = configmod.Config()
24
+
25
+
26
+ class TableModelSequential(gutils.TableModel):
27
+ """Table Model for the Model/View Qt framework dedicated to the Sequential scan mode"""
28
+ def __init__(self, data, **kwargs):
29
+ header = ['Actuator', 'Start', 'Stop', 'Step']
30
+ if 'header' in kwargs:
31
+ header = kwargs.pop('header')
32
+ editable = [False, True, True, True]
33
+ if 'editable' in kwargs:
34
+ editable = kwargs.pop('editable')
35
+ super().__init__(data, header, editable=editable, **kwargs)
36
+
37
+ def __repr__(self):
38
+ return f'{self.__class__.__name__} from module {self.__class__.__module__}'
39
+
40
+ def validate_data(self, row, col, value):
41
+ """
42
+ make sure the values and signs of the start, stop and step values are "correct"
43
+ Parameters
44
+ ----------
45
+ row: (int) row within the table that is to be changed
46
+ col: (int) col within the table that is to be changed
47
+ value: (float) new value for the value defined by row and col
48
+
49
+ Returns
50
+ -------
51
+ bool: True is the new value is fine (change some other values if needed) otherwise False
52
+ """
53
+ start = self.data(self.index(row, 1), QtCore.Qt.DisplayRole)
54
+ stop = self.data(self.index(row, 2), QtCore.Qt.DisplayRole)
55
+ step = self.data(self.index(row, 3), QtCore.Qt.DisplayRole)
56
+ isstep = False
57
+ if col == 1: # the start
58
+ start = value
59
+ elif col == 2: # the stop
60
+ stop = value
61
+ elif col == 3: # the step
62
+ isstep = True
63
+ step = value
64
+
65
+ if np.abs(step) < 1e-12 or start == stop:
66
+ return False
67
+ if np.sign(stop - start) != np.sign(step):
68
+ if isstep:
69
+ self._data[row][2] = -stop
70
+ else:
71
+ self._data[row][3] = -step
72
+ return True
73
+
74
+
75
+ @ScannerFactory.register('Sequential', 'Linear')
76
+ class SequentialScanner(ScannerBase):
77
+ params = [
78
+ {'title': 'Sequences', 'name': 'seq_table', 'type': 'table_view', 'delegate': gutils.SpinBoxDelegate},
79
+ ]
80
+ distribution = DataDistribution['uniform']
81
+ n_axes = 1
82
+
83
+ def __init__(self, actuators: List[str]):
84
+
85
+ self.table_model: TableModelSequential = None
86
+ self.table_view: TableViewCustom = None
87
+ super().__init__(actuators)
88
+ self.update_model()
89
+
90
+ @property
91
+ def actuators(self):
92
+ return self._actuators
93
+
94
+ @actuators.setter
95
+ def actuators(self, actuators_name):
96
+ self._actuators = actuators_name
97
+ self.update_model()
98
+
99
+ def update_model(self, init_data=None):
100
+ if init_data is None:
101
+ if self.table_model is not None:
102
+ init_data = []
103
+ names = [row[0] for row in self.table_model.get_data_all()]
104
+ for act in self._actuators:
105
+ if act.title in names:
106
+ ind_row = names.index(act.title)
107
+ init_data.append(self.table_model.get_data_all()[ind_row])
108
+ else:
109
+ init_data.append([act.title, 0., 1., 0.1])
110
+ else:
111
+ init_data = [[act.title, 0., 1., 0.1] for act in self._actuators]
112
+ self.table_model = TableModelSequential(init_data, )
113
+ self.table_view = putils.get_widget_from_tree(self.settings_tree, TableViewCustom)[0]
114
+ self.settings.child('seq_table').setValue(self.table_model)
115
+ self.n_axes = len(self._actuators)
116
+ self.update_table_view()
117
+
118
+ def get_pos(self):
119
+ starts = np.array([self.table_model.get_data(ind, 1) for ind in range(self.table_model.rowCount(None))])
120
+ stops = np.array([self.table_model.get_data(ind, 2) for ind in range(self.table_model.rowCount(None))])
121
+ steps = np.array([self.table_model.get_data(ind, 3) for ind in range(self.table_model.rowCount(None))])
122
+ return starts, stops, steps
123
+
124
+ def evaluate_steps(self) -> int:
125
+ starts, stops, steps = self.get_pos()
126
+ n_steps = 1
127
+ for ind in range(starts.size):
128
+ n_steps *= np.abs((stops[ind] - starts[ind]) / steps[ind]) + 1
129
+ return int(n_steps)
130
+
131
+ @staticmethod
132
+ def pos_above_stops(positions, steps, stops):
133
+ state = []
134
+ for pos, step, stop in zip(positions, steps, stops):
135
+ if step >= 0:
136
+ state.append(pos > stop)
137
+ else:
138
+ state.append(pos < stop)
139
+ return state
140
+
141
+ def update_table_view(self):
142
+ self.table_view.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
143
+ self.table_view.horizontalHeader().setStretchLastSection(True)
144
+ self.table_view.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
145
+ self.table_view.setSelectionMode(QtWidgets.QTableView.SingleSelection)
146
+ styledItemDelegate = QtWidgets.QStyledItemDelegate()
147
+ styledItemDelegate.setItemEditorFactory(gutils.SpinBoxDelegate())
148
+ self.table_view.setItemDelegate(styledItemDelegate)
149
+
150
+ self.table_view.setDragEnabled(True)
151
+ self.table_view.setDropIndicatorShown(True)
152
+ self.table_view.setAcceptDrops(True)
153
+ self.table_view.viewport().setAcceptDrops(True)
154
+ self.table_view.setDefaultDropAction(QtCore.Qt.MoveAction)
155
+ self.table_view.setDragDropMode(QtWidgets.QTableView.InternalMove)
156
+ self.table_view.setDragDropOverwriteMode(False)
157
+
158
+ def set_scan(self):
159
+ starts, stops, steps = self.get_pos()
160
+ all_positions = [starts.copy()]
161
+ positions = starts.copy()
162
+ state = self.pos_above_stops(positions, steps, stops)
163
+ if len(state) != 0:
164
+ while not state[0]:
165
+ if not np.any(np.array(state)):
166
+ positions[-1] += steps[-1]
167
+
168
+ else:
169
+ indexes_true = np.where(np.array(state))
170
+ positions[indexes_true[-1][0]] = starts[indexes_true[-1][0]]
171
+ positions[indexes_true[-1][0] - 1] += steps[indexes_true[-1][0] - 1]
172
+
173
+ state = self.pos_above_stops(positions, steps, stops)
174
+ if not np.any(np.array(state)):
175
+ all_positions.append(positions.copy())
176
+
177
+ self.get_info_from_positions(np.array(all_positions))
178
+
179
+ def get_nav_axes(self) -> List[Axis]:
180
+ return [Axis(label=f'{act.title}', units=act.units, data=self.axes_unique[ind], index=ind)
181
+ for ind, act in enumerate(self.actuators)]
182
+
183
+ def get_indexes_from_scan_index(self, scan_index: int) -> Tuple[int]:
184
+ """To be reimplemented. Calculations of indexes within the scan"""
185
+ return tuple(self.axes_indexes[scan_index])
186
+
187
+ def get_scan_shape(self) -> Tuple[int]:
188
+ return tuple([len(axis) for axis in self.axes_unique])
189
+
190
+ def update_from_scan_selector(self, scan_selector: Selector):
191
+ coordinates = scan_selector.get_coordinates()
192
+ pass