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
@@ -1,1289 +0,0 @@
1
- import sys
2
- from collections import OrderedDict
3
- import numpy as np
4
- from pymodaq.daq_utils.config import Config
5
- from qtpy import QtWidgets, QtCore
6
- from qtpy.QtCore import QObject, Signal, Slot
7
-
8
- from pymodaq.daq_utils.parameter import ioxml
9
-
10
- from pymodaq.daq_utils.daq_utils import linspace_step, odd_even, greater2n
11
- from pymodaq.daq_utils.plotting.scan_selector import ScanSelector
12
- import pymodaq.daq_utils.daq_utils as utils
13
- import pymodaq.daq_utils.gui_utils as gutils
14
- from pymodaq.daq_utils.parameter import utils as putils
15
- from pymodaq.daq_utils import math_utils as mutils
16
- from pymodaq.daq_utils.plotting.utils.plot_utils import QVector
17
- from pyqtgraph.parametertree import Parameter, ParameterTree
18
- import pymodaq.daq_utils.parameter.pymodaq_ptypes as pymodaq_types # to be placed after importing Parameter
19
-
20
- from pymodaq.daq_utils.exceptions import ScannerException
21
-
22
- logger = utils.set_logger(utils.get_module_name(__file__))
23
- config = Config()
24
-
25
- SCAN_TYPES = ['Scan1D', 'Scan2D', 'Sequential', 'Tabular']
26
- SCAN_SUBTYPES = dict(Scan1D=dict(subpath=('scan1D_settings', 'scan1D_type'),
27
- limits=['Linear', 'Adaptive', 'Linear back to start', 'Random']),
28
- Scan2D=dict(subpath=('scan2D_settings', 'scan2D_type'),
29
- limits=['Spiral', 'Linear', 'Adaptive', 'Back&Forth', 'Random']),
30
- Sequential=dict(subpath=('seq_settings', 'scanseq_type'),
31
- limits=['Linear']),
32
- Tabular=dict(subpath=('tabular_settings', 'tabular_subtype'),
33
- limits=['Linear', 'Adaptive']))
34
-
35
- try:
36
- import adaptive
37
- from adaptive.learner import learner1D
38
- from adaptive.learner import learner2D
39
- adaptive_losses = dict(
40
- loss1D=['default', 'curvature', 'uniform'],
41
- loss2D=['default', 'resolution', 'uniform', 'triangle'])
42
-
43
- except Exception:
44
- SCAN_SUBTYPES['Scan1D']['limits'].pop(SCAN_SUBTYPES['Scan1D']['limits'].index('Adaptive'))
45
- SCAN_SUBTYPES['Scan2D']['limits'].pop(SCAN_SUBTYPES['Scan2D']['limits'].index('Adaptive'))
46
- SCAN_SUBTYPES['Tabular']['limits'].pop(SCAN_SUBTYPES['Tabular']['limits'].index('Adaptive'))
47
- adaptive_losses = None
48
- adaptive = None
49
- logger.info('adaptive module is not present, no adaptive scan possible')
50
-
51
-
52
- class ScanInfo:
53
- def __init__(self, Nsteps=0, positions=None, axes_indexes=None, axes_unique=None, **kwargs):
54
- """
55
-
56
- Parameters
57
- ----------
58
- Nsteps: (int) Number of steps of the scan
59
- positions: (ndarray) multidimensional array of Nsteps 0th dimension length where each element is the position
60
- positions_indexes: (ndarray) multidimensional array of Nsteps 0th dimension length where each element is the index
61
- of the corresponding positions within the axis_unique
62
- axes_unique: (list of ndarray) list of sorted (and with unique values) 1D arrays of unique positions of each defined axes
63
- """
64
- self.Nsteps = Nsteps
65
- self.positions = positions
66
- self.axes_indexes = axes_indexes
67
- self.axes_unique = axes_unique
68
- for k in kwargs:
69
- setattr(self, k, kwargs[k])
70
-
71
- def __repr__(self):
72
- if self.positions is not None:
73
- return f'[ScanInfo with {self.Nsteps} positions of shape {self.positions.shape})'
74
- else:
75
- return '[ScanInfo with position is None)'
76
-
77
-
78
- class ScanParameters:
79
- """
80
- Utility class to define and store information about scans to be done
81
- """
82
-
83
- def __init__(self, Naxes=1, scan_type='Scan1D', scan_subtype='Linear', starts=None, stops=None, steps=None,
84
- positions=None, adaptive_loss=None):
85
- """
86
-
87
- Parameters
88
- ----------
89
- Naxes: (int) number of axes used to do the scan
90
- scan_type: (str) one value of the SCAN_TYPES list items
91
- scan_subtype: (str) ne value of the SCAN_SUBTYPES dict items for the scan_type key
92
- starts: (list of floats) list of starts position of each axis
93
- stops: (list of floats) list of stops position of each axis
94
- steps: (list of floats) list of steps position of each axis
95
- positions: (ndarray) containing the positions already calculated from some method. If not None, this is used to
96
- define the scan_info (otherwise one use the starts, stops and steps)
97
-
98
- See Also
99
- --------
100
- daq_utils.plotting.scan_selector
101
- """
102
- self.Naxes = Naxes
103
- if scan_type not in SCAN_TYPES:
104
- raise ValueError(
105
- f'Chosen scan_type value ({scan_type}) is not possible. Should be among : {str(SCAN_TYPES)}')
106
- if scan_subtype not in SCAN_SUBTYPES[scan_type]['limits']:
107
- raise ValueError(
108
- f'Chosen scan_subtype value ({scan_subtype}) is not possible. Should be among'
109
- f' : {str(SCAN_SUBTYPES[scan_type]["limits"])}')
110
- self.scan_type = scan_type
111
- self.scan_subtype = scan_subtype
112
- self.adaptive_loss = adaptive_loss
113
- self.vectors = None
114
-
115
- # if positions is not None:
116
- # self.starts = np.min(positions, axis=0)
117
- # self.stops = np.max(positions, axis=0)
118
- # else:
119
- self.starts = starts
120
- self.stops = stops
121
-
122
- self.steps = steps
123
-
124
- self.scan_info = ScanInfo(Nsteps=0, positions=positions, adaptive_loss=adaptive_loss)
125
-
126
- self.set_scan()
127
-
128
- def __getattr__(self, item):
129
- if item == 'Nsteps':
130
- return self.scan_info.Nsteps
131
- elif item == 'positions':
132
- return self.scan_info.positions
133
- elif item == 'axes_indexes':
134
- return self.scan_info.axes_indexes
135
- elif item == 'axes_unique':
136
- return self.scan_info.axes_unique
137
- else:
138
- if hasattr(self.scan_info, item):
139
- return getattr(self.scan_info, item)
140
- else:
141
- raise ValueError(f'no attribute named {item}')
142
-
143
- def get_info_from_positions(self, positions):
144
- if positions is not None:
145
- if len(positions.shape) == 1:
146
- positions = np.expand_dims(positions, 1)
147
- axes_unique = []
148
- for ax in positions.T:
149
- axes_unique.append(np.unique(ax))
150
- axes_indexes = np.zeros_like(positions, dtype=int)
151
- for ind in range(positions.shape[0]):
152
- for ind_pos, pos in enumerate(positions[ind]):
153
- axes_indexes[ind, ind_pos] = mutils.find_index(axes_unique[ind_pos], pos)[0][0]
154
-
155
- return ScanInfo(Nsteps=positions.shape[0], axes_unique=axes_unique,
156
- axes_indexes=axes_indexes, positions=positions, adaptive_loss=self.adaptive_loss)
157
- else:
158
- return ScanInfo()
159
-
160
- def set_scan(self):
161
- steps_limit = config('scan', 'steps_limit')
162
- Nsteps = self.evaluate_Nsteps()
163
- if Nsteps > steps_limit:
164
- self.scan_info = ScanInfo(Nsteps=Nsteps)
165
- return self.scan_info
166
-
167
- if self.scan_type == "Scan1D":
168
- if self.positions is not None:
169
- positions = self.positions
170
- else:
171
- positions = mutils.linspace_step(self.starts[0], self.stops[0], self.steps[0])
172
-
173
- if self.scan_subtype == "Linear":
174
- self.scan_info = self.get_info_from_positions(positions)
175
-
176
- elif self.scan_subtype == 'Linear back to start':
177
- positions = np.insert(positions, range(1, len(positions) + 1), positions[0], axis=0)
178
- self.scan_info = self.get_info_from_positions(positions)
179
-
180
- elif self.scan_subtype == 'Random':
181
- np.random.shuffle(positions)
182
- self.scan_info = self.get_info_from_positions(positions)
183
-
184
- elif self.scan_subtype == 'Adaptive':
185
- # return an "empty" ScanInfo as positions will be "set" during the scan
186
- self.scan_info = ScanInfo(Nsteps=0, positions=np.array([0, 1]), axes_unique=[np.array([])],
187
- axes_indexes=np.array([]), adaptive_loss=self.adaptive_loss)
188
-
189
- else: # pragma: no cover
190
- raise ScannerException(f'The chosen scan_subtype: {str(self.scan_subtype)} is not known')
191
-
192
- elif self.scan_type == "Scan2D":
193
- if self.scan_subtype != 'Adaptive':
194
- if np.abs((self.stops[0]-self.starts[0]) / self.steps[0]) > steps_limit:
195
- return ScanInfo()
196
-
197
- if self.scan_subtype == 'Spiral':
198
- positions = set_scan_spiral(self.starts, self.stops, self.steps)
199
- self.scan_info = self.get_info_from_positions(positions)
200
-
201
- elif self.scan_subtype == 'Back&Forth':
202
- positions = set_scan_linear(self.starts, self.stops, self.steps, back_and_force=True)
203
- self.scan_info = self.get_info_from_positions(positions)
204
-
205
- elif self.scan_subtype == 'Linear':
206
- positions = set_scan_linear(self.starts, self.stops, self.steps, back_and_force=False)
207
- self.scan_info = self.get_info_from_positions(positions)
208
-
209
- elif self.scan_subtype == 'Random':
210
- positions = set_scan_random(self.starts, self.stops, self.steps)
211
- self.scan_info = self.get_info_from_positions(positions)
212
-
213
- elif self.scan_subtype == 'Adaptive':
214
- # return an "empty" ScanInfo as positions will be "set" during the scan
215
- self.scan_info = ScanInfo(Nsteps=0, positions=np.zeros([0, 2]), axes_unique=[np.array([])],
216
- axes_indexes=np.array([]), adaptive_loss=self.adaptive_loss)
217
- else:
218
- raise ScannerException(f'The chosen scan_subtype: {str(self.scan_subtype)} is not known')
219
-
220
- elif self.scan_type == "Sequential":
221
- if self.scan_subtype == 'Linear':
222
- positions = set_scan_sequential(self.starts, self.stops, self.steps)
223
- self.scan_info = self.get_info_from_positions(positions)
224
- else:
225
- raise ScannerException(f'The chosen scan_subtype: {str(self.scan_subtype)} is not known')
226
-
227
- elif self.scan_type == 'Tabular':
228
- if self.scan_subtype == 'Linear':
229
- if self.positions is not None:
230
- self.starts = np.min(self.positions, axis=0)
231
- self.stops = np.max(self.positions, axis=0)
232
- self.scan_info = self.get_info_from_positions(self.positions)
233
- elif self.scan_subtype == 'Adaptive':
234
- # return an "empty" ScanInfo as positions will be "set" during the scan
235
- # but adds some usefull info such as total length and list of vectors
236
- self.vectors = []
237
- length = 0.
238
-
239
- for ind in range(len(self.starts)):
240
- self.vectors.append(QVector(self.starts[ind][0], self.starts[ind][1],
241
- self.stops[ind][0], self.stops[ind][1]))
242
- length += self.vectors[-1].norm()
243
-
244
- self.scan_info = ScanInfo(Nsteps=0, positions=np.zeros([0, self.Naxes]), axes_unique=[np.array([])],
245
- axes_indexes=np.array([]), vectors=self.vectors, length=length,
246
- adaptive_loss=self.adaptive_loss)
247
- else:
248
- raise ScannerException(f'The chosen scan_subtype: {str(self.scan_subtype)} is not known')
249
- return self.scan_info
250
-
251
- def evaluate_Nsteps(self):
252
- Nsteps = 1
253
- if self.starts is not None:
254
- for ind in range(len(self.starts)):
255
- if self.scan_subtype == 'Adaptive':
256
- Nsteps = 1
257
- elif self.scan_subtype != 'Spiral':
258
- Nsteps *= np.abs((self.stops[ind] - self.starts[ind]) / self.steps[ind])+1
259
- else:
260
- Nsteps *= np.abs(2 * (self.stops[ind] / self.steps[ind]) + 1)
261
- return Nsteps
262
-
263
- def __repr__(self):
264
- if self.vectors is not None:
265
- bounds = f'bounds as vectors: {self.vectors} and curvilinear step: {self.steps}'
266
- else:
267
- bounds = f' bounds (starts/stops/steps): {self.starts}/{self.stops}/{self.steps})'
268
-
269
- if self.scan_subtype != 'Adaptive':
270
- return f'[{self.scan_type}/{self.scan_subtype}] scanner with {self.scan_info.Nsteps} positions and ' + bounds
271
- else:
272
- return f'[{self.scan_type}/{self.scan_subtype}] scanner with unknown (yet) positions to reach and ' + bounds
273
-
274
-
275
- class Scanner(QObject):
276
- scan_params_signal = Signal(ScanParameters)
277
-
278
- params = [#{'title': 'Scanner settings', 'name': 'scan_options', 'type': 'group', 'children': [
279
- {'title': 'Calculate positions:', 'name': 'calculate_positions', 'type': 'action'},
280
- {'title': 'N steps:', 'name': 'Nsteps', 'type': 'int', 'value': 0, 'readonly': True},
281
-
282
- {'title': 'Scan type:', 'name': 'scan_type', 'type': 'list', 'limits': SCAN_TYPES,
283
- 'value': config('scan', 'default')},
284
- {'title': 'Scan1D settings', 'name': 'scan1D_settings', 'type': 'group', 'children': [
285
- {'title': 'Scan subtype:', 'name': 'scan1D_type', 'type': 'list',
286
- 'limits': SCAN_SUBTYPES['Scan1D']['limits'], 'value': config('scan', 'scan1D', 'type'),
287
- 'tip': 'For adaptive, an algo will '
288
- 'determine the positions to check within the scan bounds. The defined step will be set as the'
289
- 'biggest feature size the algo should reach.'},
290
- {'title': 'Loss type', 'name': 'scan1D_loss', 'type': 'list',
291
- 'limits': [], 'tip': 'Type of loss used by the algo. to determine next points'},
292
- {'title': 'Start:', 'name': 'start_1D', 'type': 'float', 'value': config('scan', 'scan1D', 'start')},
293
- {'title': 'stop:', 'name': 'stop_1D', 'type': 'float', 'value': config('scan', 'scan1D', 'stop')},
294
- {'title': 'Step:', 'name': 'step_1D', 'type': 'float', 'value': config('scan', 'scan1D', 'step')}
295
- ]},
296
- {'title': 'Scan2D settings', 'name': 'scan2D_settings', 'type': 'group', 'visible': False, 'children': [
297
- {'title': 'Scan subtype:', 'name': 'scan2D_type', 'type': 'list',
298
- 'limits': SCAN_SUBTYPES['Scan2D']['limits'], 'value': config('scan', 'scan2D', 'type'),
299
- 'tip': 'For adaptive, an algo will '
300
- 'determine the positions to check within the scan bounds. The defined step will be set as the'
301
- 'biggest feature size the algo should reach.'},
302
- {'title': 'Loss type', 'name': 'scan2D_loss', 'type': 'list',
303
- 'limits': [], 'tip': 'Type of loss used by the algo. to determine next points'},
304
- {'title': 'Selection:', 'name': 'scan2D_selection', 'type': 'list', 'limits': ['Manual', 'FromROI']},
305
- {'title': 'From module:', 'name': 'scan2D_roi_module', 'type': 'list', 'limits': [], 'visible': False},
306
- {'title': 'Start Ax1:', 'name': 'start_2d_axis1', 'type': 'float',
307
- 'value': config('scan', 'scan2D', 'start1'), 'visible': True},
308
- {'title': 'Start Ax2:', 'name': 'start_2d_axis2', 'type': 'float',
309
- 'value': config('scan', 'scan2D', 'start2'), 'visible': True},
310
- {'title': 'Step Ax1:', 'name': 'step_2d_axis1', 'type': 'float',
311
- 'value': config('scan', 'scan2D', 'step1'), 'visible': True},
312
- {'title': 'Step Ax2:', 'name': 'step_2d_axis2', 'type': 'float',
313
- 'value': config('scan', 'scan2D', 'step2'), 'visible': True},
314
- {'title': 'Npts/axis', 'name': 'npts_by_axis', 'type': 'int', 'min': 1,
315
- 'value': config('scan', 'scan2D', 'npts'),
316
- 'visible': True},
317
- {'title': 'Stop Ax1:', 'name': 'stop_2d_axis1', 'type': 'float',
318
- 'value': config('scan', 'scan2D', 'stop1'), 'visible': True,
319
- 'readonly': True, },
320
- {'title': 'Stop Ax2:', 'name': 'stop_2d_axis2', 'type': 'float',
321
- 'value': config('scan', 'scan2D', 'stop2'), 'visible': True,
322
- 'readonly': True, },
323
-
324
- ]},
325
- {'title': 'Sequential settings', 'name': 'seq_settings', 'type': 'group', 'visible': False, 'children': [
326
- {'title': 'Scan subtype:', 'name': 'scanseq_type', 'type': 'list',
327
- 'limits': SCAN_SUBTYPES['Sequential']['limits'], 'value': SCAN_SUBTYPES['Sequential']['limits'][0], },
328
- {'title': 'Sequences', 'name': 'seq_table', 'type': 'table_view',
329
- 'delegate': gutils.SpinBoxDelegate},
330
- ]},
331
- {'title': 'Tabular settings', 'name': 'tabular_settings', 'type': 'group', 'visible': False, 'children': [
332
- {'title': 'Scan subtype:', 'name': 'tabular_subtype', 'type': 'list',
333
- 'limits': SCAN_SUBTYPES['Tabular']['limits'], 'value': config('scan', 'tabular', 'type'),
334
- 'tip': 'For adaptive, an algo will '
335
- 'determine the positions to check within the scan bounds. The defined step will be set as the'
336
- 'biggest feature size the algo should reach.'},
337
- {'title': 'Loss type', 'name': 'tabular_loss', 'type': 'list',
338
- 'limits': [], 'tip': 'Type of loss used by the algo. to determine next points'},
339
- {'title': 'Selection:', 'name': 'tabular_selection', 'type': 'list',
340
- 'limits': ['Manual', 'Polylines']},
341
- {'title': 'From module:', 'name': 'tabular_roi_module', 'type': 'list', 'limits': [],
342
- 'visible': False},
343
- {'title': 'Curvilinear Step:', 'name': 'tabular_step', 'type': 'float',
344
- 'value': config('scan', 'tabular', 'curvilinear')},
345
- {'title': 'Positions', 'name': 'tabular_table', 'type': 'table_view',
346
- 'delegate': gutils.SpinBoxDelegate, 'menu': True},
347
- ]},
348
- {'title': 'Load settings', 'name': 'load_xml', 'type': 'action'},
349
- {'title': 'Save settings', 'name': 'save_xml', 'type': 'action'},
350
- ]#}]
351
-
352
- def __init__(self, scanner_items=OrderedDict([]), scan_type='Scan1D', actuators=[], adaptive_losses=None):
353
- """
354
-
355
- Parameters
356
- ----------
357
- scanner_items: (items used by ScanSelector for chosing scan area or linear traces)
358
- scan_type: type of scan selector
359
- actuators: list of actuators names
360
- """
361
- super().__init__()
362
-
363
- self.settings_tree = None
364
- self.setupUI()
365
-
366
- self.scan_selector = ScanSelector(scanner_items, scan_type)
367
- self.settings.child('scan_type').setValue(scan_type)
368
- # self.scan_selector.settings.child('scan_options', 'scan_type').hide()
369
- self.scan_selector.scan_select_signal.connect(self.update_scan_2D_positions)
370
- self.scan_selector.scan_select_signal.connect(lambda: self.update_tabular_positions())
371
-
372
- self.settings.child('tabular_settings', 'tabular_roi_module').setOpts(
373
- limits=self.scan_selector.sources_names)
374
- self.settings.child('scan2D_settings', 'scan2D_roi_module').setOpts(
375
- limits=self.scan_selector.sources_names)
376
- self.table_model = None
377
-
378
- if adaptive_losses is not None:
379
- if 'loss1D' in adaptive_losses:
380
- self.settings.child('scan1D_settings', 'scan1D_loss').setOpts(
381
- limits=adaptive_losses['loss1D'], visible=False)
382
- if 'loss1D' in adaptive_losses:
383
- self.settings.child('tabular_settings', 'tabular_loss').setOpts(
384
- limits=adaptive_losses['loss1D'], visible=False)
385
- if 'loss2D' in adaptive_losses:
386
- self.settings.child('scan2D_settings', 'scan2D_loss').setOpts(
387
- limits=adaptive_losses['loss2D'], visible=False)
388
-
389
- self.actuators = actuators
390
- # if actuators != []:
391
- # self.actuators = actuators
392
- # else:
393
- # stypes = SCAN_TYPES[:]
394
- # stypes.pop(stypes.index('Sequential'))
395
- # self.settings.child('scan_type').setLimits(stypes)
396
- # self.settings.child('scan_type').setValue(stypes[0])
397
-
398
- self.scan_selector.widget.setVisible(False)
399
- self.scan_selector.show_scan_selector(visible=False)
400
- self.settings.child('load_xml').sigActivated.connect(self.load_xml)
401
- self.settings.child('save_xml').sigActivated.connect(self.save_xml)
402
-
403
- self.set_scan()
404
- self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed)
405
-
406
- def load_xml(self):
407
- fname = gutils.select_file(start_path=None, save=False, ext='xml')
408
- if fname is not None and fname != '':
409
- par = ioxml.XML_file_to_parameter(fname)
410
- self.settings.restoreState(Parameter.create(name='settings', type='group', children=par).saveState())
411
- self.update_model()
412
- scan_type = self.settings.child('scan_type').value()
413
- if scan_type == 'Sequential':
414
- self.table_model = self.settings.child('seq_settings', 'seq_table').value()
415
- elif scan_type == 'Tabular':
416
- self.table_model = self.settings.child('tabular_settings', 'tabular_table').value()
417
- self.set_scan()
418
-
419
- def save_xml(self):
420
- """
421
- """
422
- fname = gutils.select_file(start_path=None, save=True, ext='xml')
423
- if fname is not None and fname != '':
424
- ioxml.parameter_to_xml_file(self.settings, fname)
425
-
426
- def set_config(self):
427
- scan_type = config['scan']['default']
428
- self.settings.child('scan_type').setValue(scan_type)
429
-
430
- self.settings.child('scan1D_settings', 'scan1D_type').setValue(config('scan', 'scan1D', 'type'))
431
- self.settings.child('scan1D_settings', 'start_1D').setValue(config('scan', 'scan1D', 'start'))
432
- self.settings.child('scan1D_settings', 'stop_1D').setValue(config('scan', 'scan1D', 'stop'))
433
- self.settings.child('scan1D_settings', 'step_1D').setValue(config('scan', 'scan1D', 'step'))
434
-
435
- self.settings.child('scan2D_settings', 'scan2D_type').setValue(config('scan', 'scan2D', 'type'))
436
- self.settings.child('scan2D_settings', 'start_2d_axis1').setValue(
437
- config('scan', 'scan2D', 'start1'))
438
- self.settings.child('scan2D_settings', 'start_2d_axis2').setValue(
439
- config('scan', 'scan2D', 'start2'))
440
- self.settings.child('scan2D_settings', 'step_2d_axis2').setValue(
441
- config('scan', 'scan2D', 'step1'))
442
- self.settings.child('scan2D_settings', 'step_2d_axis2').setValue(
443
- config('scan', 'scan2D', 'step2'))
444
- self.settings.child('scan2D_settings', 'npts_by_axis').setValue(
445
- config('scan', 'scan2D', 'npts'))
446
- self.settings.child('scan2D_settings', 'stop_2d_axis1').setValue(
447
- config('scan', 'scan2D', 'stop1'))
448
- self.settings.child('scan2D_settings', 'stop_2d_axis2').setValue(
449
- config('scan', 'scan2D', 'stop2'))
450
-
451
- self.settings.child('tabular_settings', 'tabular_subtype').setValue(
452
- config('scan', 'tabular', 'type'))
453
- self.settings.child('tabular_settings', 'tabular_step').setValue(
454
- config('scan', 'tabular', 'curvilinear'))
455
-
456
- @property
457
- def actuators(self):
458
- """
459
- Returns as a list the name of the actuators selected to describe the actual scan
460
- """
461
- return self._actuators
462
-
463
- @actuators.setter
464
- def actuators(self, act_list):
465
- self._actuators = act_list
466
- if len(act_list) >= 1:
467
- tip = f'Ax1 corresponds to the {act_list[0]} actuator'
468
- self.settings.child('scan2D_settings', 'start_2d_axis1').setOpts(tip=tip)
469
- self.settings.child('scan2D_settings', 'stop_2d_axis1').setOpts(tip=tip)
470
- self.settings.child('scan2D_settings', 'step_2d_axis1').setOpts(tip=tip)
471
- if len(act_list) >= 2:
472
- tip = f'Ax2 corresponds to the {act_list[1]} actuator'
473
- self.settings.child('scan2D_settings', 'start_2d_axis2').setOpts(tip=tip)
474
- self.settings.child('scan2D_settings', 'stop_2d_axis2').setOpts(tip=tip)
475
- self.settings.child('scan2D_settings', 'step_2d_axis2').setOpts(tip=tip)
476
-
477
- self.update_model()
478
-
479
- def update_model(self, init_data=None):
480
- try:
481
- scan_type = self.settings.child('scan_type').value()
482
- if scan_type == 'Sequential':
483
- if init_data is None:
484
- if self.table_model is not None:
485
- init_data = []
486
- names = [row[0] for row in self.table_model.get_data_all()]
487
- for name in self._actuators:
488
- if name in names:
489
- ind_row = names.index(name)
490
- init_data.append(self.table_model.get_data_all()[ind_row])
491
- else:
492
- init_data.append([name, 0., 1., 0.1])
493
- else:
494
- init_data = [[name, 0., 1., 0.1] for name in self._actuators]
495
- self.table_model = TableModelSequential(init_data, )
496
- self.table_view = putils.get_widget_from_tree(self.settings_tree, pymodaq_types.TableViewCustom)[0]
497
- self.settings.child('seq_settings', 'seq_table').setValue(self.table_model)
498
- elif scan_type == 'Tabular':
499
- if init_data is None:
500
- init_data = [[0. for name in self._actuators]]
501
-
502
- self.table_model = TableModelTabular(init_data, [name for name in self._actuators])
503
- self.table_view = putils.get_widget_from_tree(self.settings_tree, pymodaq_types.TableViewCustom)[1]
504
- self.settings.child('tabular_settings', 'tabular_table').setValue(self.table_model)
505
- except Exception as e:
506
- logger.exception(str(e))
507
-
508
- if scan_type == 'Sequential' or scan_type == 'Tabular':
509
- self.table_view.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
510
- self.table_view.horizontalHeader().setStretchLastSection(True)
511
- self.table_view.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
512
- self.table_view.setSelectionMode(QtWidgets.QTableView.SingleSelection)
513
- styledItemDelegate = QtWidgets.QStyledItemDelegate()
514
- styledItemDelegate.setItemEditorFactory(gutils.SpinBoxDelegate())
515
- self.table_view.setItemDelegate(styledItemDelegate)
516
-
517
- self.table_view.setDragEnabled(True)
518
- self.table_view.setDropIndicatorShown(True)
519
- self.table_view.setAcceptDrops(True)
520
- self.table_view.viewport().setAcceptDrops(True)
521
- self.table_view.setDefaultDropAction(QtCore.Qt.MoveAction)
522
- self.table_view.setDragDropMode(QtWidgets.QTableView.InternalMove)
523
- self.table_view.setDragDropOverwriteMode(False)
524
-
525
- if scan_type == 'Tabular':
526
- self.table_view.add_data_signal[int].connect(self.table_model.add_data)
527
- self.table_view.remove_row_signal[int].connect(self.table_model.remove_data)
528
- self.table_view.load_data_signal.connect(self.table_model.load_txt)
529
- self.table_view.save_data_signal.connect(self.table_model.save_txt)
530
-
531
-
532
- @property
533
- def viewers_items(self):
534
- return self.scan_selector.viewers_items
535
-
536
- @viewers_items.setter
537
- def viewers_items(self, items):
538
- self.scan_selector.remove_scan_selector()
539
- self.scan_selector.viewers_items = items
540
- self.settings.child('tabular_settings', 'tabular_roi_module').setOpts(
541
- limits=self.scan_selector.sources_names)
542
- self.settings.child('scan2D_settings', 'scan2D_roi_module').setOpts(
543
- limits=self.scan_selector.sources_names)
544
-
545
- def set_scan_type_and_subtypes(self, scan_type: str, scan_subtype=None):
546
- """Convenience function to qzt the main scan type"""
547
- if scan_type in SCAN_TYPES:
548
- self.settings.child('scan_type').setValue(scan_type)
549
-
550
- if scan_subtype is not None:
551
- if scan_subtype in SCAN_SUBTYPES[scan_type]['limits']:
552
- self.settings.child(*SCAN_SUBTYPES[scan_type]['subpath']).setValue(scan_subtype)
553
-
554
- def parameter_tree_changed(self, param, changes):
555
- """
556
-
557
- """
558
- for param, change, data in changes:
559
- path = self.settings.childPath(param)
560
- if path is not None:
561
- childName = '.'.join(path)
562
- else:
563
- childName = param.name()
564
- if change == 'childAdded':
565
- pass
566
-
567
- elif change == 'value':
568
- if param.name() == 'scan_type':
569
-
570
- if data == 'Scan1D':
571
- self.settings.child('scan1D_settings').show()
572
- self.settings.child('scan2D_settings').hide()
573
- self.settings.child('seq_settings').hide()
574
- self.settings.child('tabular_settings').hide()
575
- self.settings_tree.setMaximumHeight(500)
576
-
577
- elif data == 'Scan2D':
578
- self.settings.child('scan1D_settings').hide()
579
- self.settings.child('scan2D_settings').show()
580
- self.settings.child('seq_settings').hide()
581
- self.settings.child('tabular_settings').hide()
582
- self.settings_tree.setMaximumHeight(500)
583
- self.scan_selector.settings.child('scan_options', 'scan_type').setValue(data)
584
- if self.settings.child('scan2D_settings',
585
- 'scan2D_selection').value() == 'Manual':
586
- self.scan_selector.show_scan_selector(visible=False)
587
- else:
588
- self.scan_selector.show_scan_selector(visible=True)
589
- self.update_scan_2D_positions()
590
- self.update_scan2D_type(param)
591
-
592
- elif data == 'Sequential':
593
- self.settings.child('scan1D_settings').hide()
594
- self.settings.child('scan2D_settings').hide()
595
- self.settings.child('seq_settings').show()
596
- self.settings.child('tabular_settings').hide()
597
- self.update_model()
598
- self.settings_tree.setMaximumHeight(600)
599
-
600
- elif data == 'Tabular':
601
- self.settings.child('scan1D_settings').hide()
602
- self.settings.child('scan2D_settings').hide()
603
- self.settings.child('seq_settings').hide()
604
- self.settings.child('tabular_settings').show()
605
- self.settings.child('tabular_settings', 'tabular_step').hide()
606
-
607
- self.update_tabular_positions()
608
- self.settings_tree.setMaximumHeight(600)
609
- self.scan_selector.settings.child('scan_options', 'scan_type').setValue(data)
610
- if self.settings.child('tabular_settings',
611
- 'tabular_selection').value() == 'Manual':
612
- self.scan_selector.show_scan_selector(visible=False)
613
- else:
614
- self.scan_selector.show_scan_selector(visible=True)
615
-
616
- elif param.name() == 'scan1D_type':
617
- status = 'adaptive' in param.value().lower()
618
- self.settings.child('scan1D_settings', 'scan1D_loss').show(status)
619
-
620
- elif param.name() == 'tabular_subtype':
621
- isadaptive = 'adaptive' in self.settings.child('tabular_settings',
622
- 'tabular_subtype').value().lower()
623
- ismanual = self.settings.child('tabular_settings',
624
- 'tabular_selection').value() == 'Manual'
625
- self.settings.child('tabular_settings', 'tabular_loss').show(isadaptive)
626
- self.settings.child('tabular_settings',
627
- 'tabular_step').show(not isadaptive and not ismanual)
628
- self.update_tabular_positions()
629
-
630
- elif param.name() == 'tabular_roi_module' or param.name() == 'scan2D_roi_module':
631
- self.scan_selector.settings.child('scan_options', 'sources').setValue(param.value())
632
-
633
- elif param.name() == 'tabular_selection':
634
- isadaptive = 'adaptive' in self.settings.child('tabular_settings',
635
- 'tabular_subtype').value().lower()
636
- ismanual = self.settings.child('tabular_settings',
637
- 'tabular_selection').value() == 'Manual'
638
- self.settings.child('tabular_settings',
639
- 'tabular_step').show(not isadaptive and not ismanual)
640
- if data == 'Polylines':
641
- self.settings.child('tabular_settings', 'tabular_roi_module').show()
642
- self.scan_selector.show_scan_selector(visible=True)
643
- else:
644
- self.settings.child('tabular_settings', 'tabular_roi_module').hide()
645
- self.scan_selector.show_scan_selector(visible=False)
646
- self.update_tabular_positions()
647
-
648
- elif param.name() == 'tabular_step':
649
- self.update_tabular_positions()
650
- self.set_scan()
651
-
652
- elif param.name() == 'scan2D_selection':
653
- if param.value() == 'Manual':
654
- self.scan_selector.show_scan_selector(visible=False)
655
- self.settings.child('scan2D_settings', 'scan2D_roi_module').hide()
656
- else:
657
- self.scan_selector.show_scan_selector(visible=True)
658
- self.settings.child('scan2D_settings', 'scan2D_roi_module').show()
659
-
660
- self.update_scan2D_type(param)
661
-
662
- elif param.name() in putils.iter_children(self.settings.child('scan2D_settings'), []):
663
- self.update_scan2D_type(param)
664
- self.set_scan()
665
-
666
- elif param.name() == 'Nsteps':
667
- pass # just do nothing (otherwise set_scan will be fired, see below)
668
-
669
- else:
670
- try:
671
- self.set_scan()
672
- except Exception as e:
673
- logger.error(f'Invalid call to setScan ({str(e)})')
674
-
675
- elif change == 'parent':
676
- pass
677
-
678
- def setupUI(self):
679
- # layout = QtWidgets.QHBoxLayout()
680
- # layout.setSpacing(0)
681
- # self.parent.setLayout(layout)
682
- self.settings_tree = ParameterTree()
683
- self.settings = Parameter.create(name='Scanner_Settings', title='Scanner Settings', type='group',
684
- children=self.params)
685
- self.settings_tree.setParameters(self.settings, showTop=False)
686
- self.settings_tree.setMaximumHeight(500)
687
-
688
- self.settings.child('calculate_positions').sigActivated.connect(self.set_scan)
689
- # layout.addWidget(self.settings_tree)
690
-
691
- def set_scan(self):
692
- scan_type = self.settings.child('scan_type').value()
693
-
694
- if scan_type == "Scan1D":
695
- start = self.settings.child('scan1D_settings', 'start_1D').value()
696
- stop = self.settings.child('scan1D_settings', 'stop_1D').value()
697
- step = self.settings.child('scan1D_settings', 'step_1D').value()
698
- self.scan_parameters = ScanParameters(Naxes=1, scan_type="Scan1D",
699
- scan_subtype=self.settings.child('scan1D_settings',
700
- 'scan1D_type').value(),
701
- starts=[start], stops=[stop], steps=[step],
702
- adaptive_loss=self.settings.child('scan1D_settings',
703
- 'scan1D_loss').value())
704
-
705
- elif scan_type == "Scan2D":
706
- starts = [self.settings.child('scan2D_settings', 'start_2d_axis1').value(),
707
- self.settings.child('scan2D_settings', 'start_2d_axis2').value()]
708
- stops = [self.settings.child('scan2D_settings', 'stop_2d_axis1').value(),
709
- self.settings.child('scan2D_settings', 'stop_2d_axis2').value()]
710
- steps = [self.settings.child('scan2D_settings', 'step_2d_axis1').value(),
711
- self.settings.child('scan2D_settings', 'step_2d_axis2').value()]
712
- self.scan_parameters = ScanParameters(Naxes=2, scan_type="Scan2D",
713
- scan_subtype=self.settings.child('scan2D_settings',
714
- 'scan2D_type').value(),
715
- starts=starts, stops=stops, steps=steps,
716
- adaptive_loss=self.settings.child('scan2D_settings',
717
- 'scan2D_loss').value())
718
-
719
- elif scan_type == "Sequential":
720
- starts = [self.table_model.get_data(ind, 1) for ind in range(self.table_model.rowCount(None))]
721
- stops = [self.table_model.get_data(ind, 2) for ind in range(self.table_model.rowCount(None))]
722
- steps = [self.table_model.get_data(ind, 3) for ind in range(self.table_model.rowCount(None))]
723
- self.scan_parameters = ScanParameters(Naxes=len(starts), scan_type="Sequential",
724
- scan_subtype=self.settings.child('seq_settings',
725
- 'scanseq_type').value(),
726
- starts=starts, stops=stops, steps=steps)
727
-
728
- elif scan_type == 'Tabular':
729
- positions = np.array(self.table_model.get_data_all())
730
- Naxes = positions.shape[1]
731
- if self.settings.child('tabular_settings', 'tabular_subtype').value() == 'Adaptive':
732
- starts = positions[:-1]
733
- stops = positions[1:]
734
- steps = [self.settings.child('tabular_settings', 'tabular_step').value()]
735
- positions = None
736
- else:
737
- starts = None
738
- stops = None
739
- steps = None
740
-
741
- self.scan_parameters = ScanParameters(Naxes=Naxes, scan_type="Tabular",
742
- scan_subtype=self.settings.child('tabular_settings',
743
- 'tabular_subtype').value(),
744
- starts=starts, stops=stops, steps=steps, positions=positions,
745
- adaptive_loss=self.settings.child('tabular_settings',
746
- 'tabular_loss').value())
747
-
748
- self.settings.child('Nsteps').setValue(self.scan_parameters.Nsteps)
749
- self.scan_params_signal.emit(self.scan_parameters)
750
- return self.scan_parameters
751
-
752
- def update_tabular_positions(self, positions=None):
753
- try:
754
- if self.settings.child('scan_type').value() == 'Tabular':
755
- if positions is None:
756
- if self.settings.child('tabular_settings',
757
- 'tabular_selection').value() == 'Polylines': # from ROI
758
- viewer = self.scan_selector.scan_selector_source
759
-
760
- if self.settings.child('tabular_settings', 'tabular_subtype').value() == 'Linear':
761
- positions = self.scan_selector.scan_selector.getArrayIndexes(
762
- spacing=self.settings.child('tabular_settings', 'tabular_step').value())
763
- elif self.settings.child('tabular_settings',
764
- 'tabular_subtype').value() == 'Adaptive':
765
- positions = self.scan_selector.scan_selector.get_vertex()
766
-
767
- steps_x, steps_y = zip(*positions)
768
- steps_x, steps_y = viewer.scale_axis(np.array(steps_x), np.array(steps_y))
769
- positions = np.transpose(np.array([steps_x, steps_y]))
770
- self.update_model(init_data=positions)
771
- else:
772
- self.update_model()
773
- elif isinstance(positions, np.ndarray):
774
- self.update_model(init_data=positions)
775
- else:
776
- pass
777
- else:
778
- self.update_model()
779
- except Exception as e:
780
- logger.exception(str(e))
781
-
782
- def update_scan_2D_positions(self):
783
- try:
784
- viewer = self.scan_selector.scan_selector_source
785
- pos_dl = self.scan_selector.scan_selector.pos()
786
- pos_ur = self.scan_selector.scan_selector.pos() + self.scan_selector.scan_selector.size()
787
- pos_dl_scaled = viewer.scale_axis(pos_dl[0], pos_dl[1])
788
- pos_ur_scaled = viewer.scale_axis(pos_ur[0], pos_ur[1])
789
-
790
- if self.settings.child('scan2D_settings', 'scan2D_type').value() == 'Spiral':
791
- self.settings.child('scan2D_settings', 'start_2d_axis1').setValue(
792
- np.mean((pos_dl_scaled[0], pos_ur_scaled[0])))
793
- self.settings.child('scan2D_settings', 'start_2d_axis2').setValue(
794
- np.mean((pos_dl_scaled[1], pos_ur_scaled[1])))
795
-
796
- nsteps = 2 * np.min((np.abs((pos_ur_scaled[0] - pos_dl_scaled[0]) / 2) / self.settings.child(
797
- 'scan2D_settings', 'step_2d_axis1').value(), np.abs(
798
- (pos_ur_scaled[1] - pos_dl_scaled[1]) / 2) / self.settings.child(
799
- 'scan2D_settings', 'step_2d_axis2').value()))
800
-
801
- self.settings.child('scan2D_settings', 'npts_by_axis').setValue(nsteps)
802
-
803
- else:
804
- self.settings.child('scan2D_settings', 'start_2d_axis1').setValue(pos_dl_scaled[0])
805
- self.settings.child('scan2D_settings', 'start_2d_axis2').setValue(pos_dl_scaled[1])
806
- self.settings.child('scan2D_settings', 'stop_2d_axis1').setValue(pos_ur_scaled[0])
807
- self.settings.child('scan2D_settings', 'stop_2d_axis2').setValue(pos_ur_scaled[1])
808
-
809
- except Exception as e:
810
- raise ScannerException(str(e))
811
-
812
- def update_scan2D_type(self, param):
813
- """
814
- Update the scan type from the given parameter.
815
-
816
- =============== ================================= ========================
817
- **Parameters** **Type** **Description**
818
- *param* instance of pyqtgraph parameter the parameter to treat
819
- =============== ================================= ========================
820
-
821
- See Also
822
- --------
823
- update_status
824
- """
825
- try:
826
- self.settings.child('scan2D_settings', 'step_2d_axis1').show()
827
- self.settings.child('scan2D_settings', 'step_2d_axis2').show()
828
- scan_subtype = self.settings.child('scan2D_settings', 'scan2D_type').value()
829
- self.settings.child('scan2D_settings', 'scan2D_loss').show(scan_subtype == 'Adaptive')
830
- if scan_subtype == 'Adaptive':
831
- if self.settings.child('scan2D_settings', 'scan2D_loss').value() == 'resolution':
832
- title = 'Minimal feature (%):'
833
- if self.settings.child('scan2D_settings', 'step_2d_axis1').opts['title'] != title:
834
- self.settings.child('scan2D_settings', 'step_2d_axis1').setValue(1)
835
- self.settings.child('scan2D_settings', 'step_2d_axis2').setValue(100)
836
-
837
- self.settings.child('scan2D_settings', 'step_2d_axis1').setOpts(
838
- limits=[0, 100], title=title, visible=True,
839
- tip='Features smaller than this will not be probed first. In percent of maximal scanned area'
840
- ' length',
841
- )
842
- self.settings.child('scan2D_settings', 'step_2d_axis2').setOpts(
843
- limits=[0, 100], title='Maximal feature (%):', visible=True,
844
- tip='Features bigger than this will be probed first. In percent of maximal scanned area length',
845
- )
846
-
847
- else:
848
- self.settings.child('scan2D_settings', 'step_2d_axis1').hide()
849
- self.settings.child('scan2D_settings', 'step_2d_axis2').hide()
850
- else:
851
- self.settings.child('scan2D_settings',
852
- 'step_2d_axis1').setOpts(title='Step Ax1:',
853
- tip='Step size for ax1 in actuator units')
854
- self.settings.child('scan2D_settings',
855
- 'step_2d_axis2').setOpts(title='Step Ax2:',
856
- tip='Step size for ax2 in actuator units')
857
-
858
- if scan_subtype == 'Spiral':
859
- self.settings.child('scan2D_settings',
860
- 'start_2d_axis1').setOpts(title='Center Ax1')
861
- self.settings.child('scan2D_settings',
862
- 'start_2d_axis2').setOpts(title='Center Ax2')
863
-
864
- self.settings.child('scan2D_settings',
865
- 'stop_2d_axis1').setOpts(title='Rmax Ax1', readonly=True,
866
- tip='Read only for Spiral scan type, set the step and Npts/axis')
867
- self.settings.child('scan2D_settings',
868
- 'stop_2d_axis2').setOpts(title='Rmax Ax2', readonly=True,
869
- tip='Read only for Spiral scan type, set the step and Npts/axis')
870
- self.settings.child('scan2D_settings',
871
- 'npts_by_axis').show()
872
-
873
- # do some checks and set stops values
874
- self.settings.sigTreeStateChanged.disconnect()
875
- if param.name() == 'step_2d_axis1':
876
- if param.value() < 0:
877
- param.setValue(-param.value())
878
-
879
- if param.name() == 'step_2d_axis2':
880
- if param.value() < 0:
881
- param.setValue(-param.value())
882
-
883
- self.settings.child('scan2D_settings', 'stop_2d_axis1').setValue(
884
- np.rint(self.settings.child(
885
- 'scan2D_settings', 'npts_by_axis').value() / 2) * np.abs(
886
- self.settings.child('scan2D_settings', 'step_2d_axis1').value()))
887
-
888
- self.settings.child('scan2D_settings', 'stop_2d_axis2').setValue(
889
- np.rint(self.settings.child(
890
- 'scan2D_settings', 'npts_by_axis').value() / 2) * np.abs(
891
- self.settings.child('scan2D_settings', 'step_2d_axis2').value()))
892
- QtWidgets.QApplication.processEvents()
893
- self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed)
894
- else:
895
- self.settings.child('scan2D_settings',
896
- 'start_2d_axis1').setOpts(title='Start Ax1')
897
- self.settings.child('scan2D_settings',
898
- 'start_2d_axis2').setOpts(title='Start Ax2')
899
-
900
- self.settings.child('scan2D_settings',
901
- 'stop_2d_axis1').setOpts(title='Stop Ax1', readonly=False,
902
- tip='Set the stop positions')
903
- self.settings.child('scan2D_settings',
904
- 'stop_2d_axis2').setOpts(title='StopAx2', readonly=False,
905
- tip='Set the stop positions')
906
- self.settings.child('scan2D_settings', 'npts_by_axis').hide()
907
- except Exception as e:
908
- raise ScannerException(str(e))
909
-
910
-
911
- class TableModelTabular(gutils.TableModel):
912
- def __init__(self, data, axes_name=None, **kwargs):
913
- if axes_name is None:
914
- if 'header' in kwargs: # when saved as XML the header will be saved and restored here
915
- axes_name = [h for h in kwargs['header']]
916
- kwargs.pop('header')
917
- else:
918
- raise Exception('Invalid header')
919
-
920
- header = [name for name in axes_name]
921
- editable = [True for name in axes_name]
922
- super().__init__(data, header, editable=editable, **kwargs)
923
-
924
- @Slot(int)
925
- def add_data(self, row, data=None):
926
- if data is not None:
927
- self.insert_data(row, [float(d) for d in data])
928
- else:
929
- self.insert_data(row, [0. for name in self.header])
930
-
931
- @Slot(int)
932
- def remove_data(self, row):
933
- self.remove_row(row)
934
-
935
- def load_txt(self):
936
- fname = gutils.select_file(start_path=None, save=False, ext='*')
937
- if fname is not None and fname != '':
938
- while self.rowCount(self.index(-1, -1)) > 0:
939
- self.remove_row(0)
940
-
941
- data = np.loadtxt(fname)
942
- if len(data.shape) == 1:
943
- data = data.reshape((data.size, 1))
944
- self.set_data_all(data)
945
-
946
- def save_txt(self):
947
- fname = gutils.select_file(start_path=None, save=True, ext='dat')
948
- if fname is not None and fname != '':
949
- np.savetxt(fname, self.get_data_all(), delimiter='\t')
950
-
951
- def __repr__(self):
952
- return f'{self.__class__.__name__} from module {self.__class__.__module__}'
953
-
954
- def validate_data(self, row, col, value):
955
- """
956
- make sure the values and signs of the start, stop and step values are "correct"
957
- Parameters
958
- ----------
959
- row: (int) row within the table that is to be changed
960
- col: (int) col within the table that is to be changed
961
- value: (float) new value for the value defined by row and col
962
-
963
- Returns
964
- -------
965
- bool: True is the new value is fine (change some other values if needed) otherwise False
966
- """
967
-
968
- return True
969
-
970
-
971
- class TableModelSequential(gutils.TableModel):
972
- def __init__(self, data, **kwargs):
973
- header = ['Actuator', 'Start', 'Stop', 'Step']
974
- if 'header' in kwargs:
975
- header = kwargs.pop('header')
976
- editable = [False, True, True, True]
977
- if 'editable' in kwargs:
978
- editable = kwargs.pop('editable')
979
- super().__init__(data, header, editable=editable, **kwargs)
980
-
981
- def __repr__(self):
982
- return f'{self.__class__.__name__} from module {self.__class__.__module__}'
983
-
984
- def validate_data(self, row, col, value):
985
- """
986
- make sure the values and signs of the start, stop and step values are "correct"
987
- Parameters
988
- ----------
989
- row: (int) row within the table that is to be changed
990
- col: (int) col within the table that is to be changed
991
- value: (float) new value for the value defined by row and col
992
-
993
- Returns
994
- -------
995
- bool: True is the new value is fine (change some other values if needed) otherwise False
996
- """
997
- start = self.data(self.index(row, 1), QtCore.Qt.DisplayRole)
998
- stop = self.data(self.index(row, 2), QtCore.Qt.DisplayRole)
999
- step = self.data(self.index(row, 3), QtCore.Qt.DisplayRole)
1000
- isstep = False
1001
- if col == 1: # the start
1002
- start = value
1003
- elif col == 2: # the stop
1004
- stop = value
1005
- elif col == 3: # the step
1006
- isstep = True
1007
- step = value
1008
-
1009
- if np.abs(step) < 1e-12 or start == stop:
1010
- return False
1011
- if np.sign(stop - start) != np.sign(step):
1012
- if isstep:
1013
- self._data[row][2] = -stop
1014
- else:
1015
- self._data[row][3] = -step
1016
- return True
1017
-
1018
-
1019
- def set_scan_linear(starts, stops, steps, back_and_force=False, oversteps=10000):
1020
- """
1021
- Set a linear scan
1022
- Parameters
1023
- ----------
1024
- starts
1025
- stops
1026
- steps
1027
- back_and_force: (bool) if True insert between two steps a position back to start (to be used as a reference in the scan analysis)
1028
- oversteps: (int) maximum number of calculated steps (stops the steps calculation if over the first power of 2 greater than oversteps)
1029
-
1030
- Returns
1031
- -------
1032
- positions (ndarray)
1033
-
1034
- See Also
1035
- --------
1036
- ScanParameters
1037
- """
1038
- starts = np.array(starts)
1039
- stops = np.array(stops)
1040
- steps = np.array(steps)
1041
-
1042
- if np.any(np.abs(steps) < 1e-12) or \
1043
- np.any(np.sign(stops - starts) != np.sign(steps)) or \
1044
- np.any(starts == stops):
1045
- return np.array([starts])
1046
-
1047
- else:
1048
- axis_1_unique = linspace_step(starts[0], stops[0], steps[0])
1049
- len1 = len(axis_1_unique)
1050
-
1051
- axis_2_unique = linspace_step(starts[1], stops[1], steps[1])
1052
- len2 = len(axis_2_unique)
1053
- # if number of steps is over oversteps, reduce both axis in the same ratio
1054
- if len1 * len2 > oversteps:
1055
- axis_1_unique = axis_1_unique[:int(np.ceil(np.sqrt(oversteps * len1 / len2)))]
1056
- axis_2_unique = axis_2_unique[:int(np.ceil(np.sqrt(oversteps * len2 / len1)))]
1057
-
1058
- positions = []
1059
- for ind_x, pos1 in enumerate(axis_1_unique):
1060
- if back_and_force:
1061
- for ind_y, pos2 in enumerate(axis_2_unique):
1062
- if not odd_even(ind_x):
1063
- positions.append([pos1, pos2])
1064
- else:
1065
- positions.append([pos1, axis_2_unique[len(axis_2_unique) - ind_y - 1]])
1066
- else:
1067
- for ind_y, pos2 in enumerate(axis_2_unique):
1068
- positions.append([pos1, pos2])
1069
-
1070
- return np.array(positions)
1071
-
1072
-
1073
- def set_scan_random(starts, stops, steps, oversteps=10000):
1074
- """
1075
-
1076
- Parameters
1077
- ----------
1078
- starts
1079
- stops
1080
- steps
1081
- oversteps
1082
-
1083
- Returns
1084
- -------
1085
-
1086
- """
1087
-
1088
- positions = set_scan_linear(starts, stops, steps, back_and_force=False, oversteps=oversteps)
1089
- np.random.shuffle(positions)
1090
- return positions
1091
-
1092
-
1093
- def set_scan_spiral(starts, rmaxs, rsteps, nsteps=None, oversteps=10000):
1094
- """Calculate the positions to describe a spiral type scan, starting from a center position and spiraling out from it
1095
-
1096
- Parameters
1097
- ----------
1098
- starts: (sequence like) containing the center positions of the scan
1099
- rmaxs: (sequence like) containing the maximum radius (ellipse axes) in each direction
1100
- rsteps: (sequence like) containing the step size for each axis
1101
- nsteps: (int) If not None, this is used together with rsteps to calculate rmaxs
1102
- oversteps: (int) maximum number of calculated steps (stops the steps calculation if over the first power of 2 greater than oversteps)
1103
-
1104
- Returns
1105
- -------
1106
- ndarray of all positions for each axis
1107
-
1108
- See Also
1109
- --------
1110
- ScanParameters
1111
- """
1112
- if np.isscalar(rmaxs):
1113
- rmaxs = np.ones(starts.shape) * rmaxs
1114
- else:
1115
- rmaxs = np.array(rmaxs)
1116
- if np.isscalar(rsteps):
1117
- rsteps = np.ones(starts.shape) * rsteps
1118
- else:
1119
- rsteps = np.array(rsteps)
1120
-
1121
- starts = np.array(starts)
1122
-
1123
- if nsteps is not None:
1124
- rmaxs = np.rint(nsteps / 2) * rsteps
1125
-
1126
- if np.any(np.array(rmaxs) == 0) or np.any(np.abs(rmaxs) < 1e-12) or np.any(np.abs(rsteps) < 1e-12):
1127
- positions = np.array([starts])
1128
- return positions
1129
-
1130
- ind = 0
1131
- flag = True
1132
- oversteps = greater2n(oversteps) # make sure the position matrix is still a square
1133
-
1134
- Nlin = np.trunc(rmaxs / rsteps)
1135
- if not np.all(Nlin == Nlin[0]):
1136
- raise ScannerException(f'For Spiral 2D scans both axis should have same length, here: {Nlin.shape}')
1137
- else:
1138
- Nlin = Nlin[0]
1139
-
1140
- axis_1_indexes = [0]
1141
- axis_2_indexes = [0]
1142
- while flag:
1143
- if odd_even(ind):
1144
- step = 1
1145
- else:
1146
- step = -1
1147
- if flag:
1148
-
1149
- for ind_step in range(ind):
1150
- axis_1_indexes.append(axis_1_indexes[-1] + step)
1151
- axis_2_indexes.append(axis_2_indexes[-1])
1152
- if len(axis_1_indexes) >= (2 * Nlin + 1) ** 2 or len(axis_1_indexes) >= oversteps:
1153
- flag = False
1154
- break
1155
- if flag:
1156
- for ind_step in range(ind):
1157
-
1158
- axis_1_indexes.append(axis_1_indexes[-1])
1159
- axis_2_indexes.append(axis_2_indexes[-1] + step)
1160
- if len(axis_1_indexes) >= (2 * Nlin + 1) ** 2 or len(axis_1_indexes) >= oversteps:
1161
- flag = False
1162
- break
1163
- ind += 1
1164
-
1165
- positions = []
1166
- for ind in range(len(axis_1_indexes)):
1167
- positions.append(np.array([axis_1_indexes[ind] * rsteps[0] + starts[0],
1168
- axis_2_indexes[ind] * rsteps[1] + starts[1]]))
1169
-
1170
- return np.array(positions)
1171
-
1172
-
1173
- def pos_above_stops(positions, steps, stops):
1174
- state = []
1175
- for pos, step, stop in zip(positions, steps, stops):
1176
- if step >= 0:
1177
- state.append(pos > stop)
1178
- else:
1179
- state.append(pos < stop)
1180
- return state
1181
-
1182
-
1183
- def set_scan_sequential(starts, stops, steps):
1184
- """
1185
- Create a list of positions (one for each actuator == one for each element in starts list) that are sequential
1186
- Parameters
1187
- ----------
1188
- starts: (sequence like)
1189
- list of starts of all selected actuators
1190
- stops: (sequence like)
1191
- list of stops of all selected actuators
1192
- steps: (sequence like)
1193
-
1194
- Returns
1195
- -------
1196
- positions: (ndarray)
1197
- """
1198
-
1199
- all_positions = [starts[:]]
1200
- positions = starts[:]
1201
- state = pos_above_stops(positions, steps, stops)
1202
- while not state[0]:
1203
- if not np.any(np.array(state)):
1204
- positions[-1] += steps[-1]
1205
-
1206
- else:
1207
- indexes_true = np.where(np.array(state))
1208
- positions[indexes_true[-1][0]] = starts[indexes_true[-1][0]]
1209
- positions[indexes_true[-1][0] - 1] += steps[indexes_true[-1][0] - 1]
1210
-
1211
- state = pos_above_stops(positions, steps, stops)
1212
- if not np.any(np.array(state)):
1213
- all_positions.append(positions[:])
1214
-
1215
- return np.array(all_positions)
1216
-
1217
-
1218
- if __name__ == '__main__':
1219
- app = QtWidgets.QApplication(sys.argv)
1220
- from qtpy.QtCore import QThread
1221
- from pymodaq.daq_utils.gui_utils import DockArea
1222
- from pyqtgraph.dockarea import Dock
1223
- from pymodaq.daq_utils.plotting.data_viewers.viewer2D import Viewer2D
1224
- from pymodaq.daq_utils.plotting.navigator import Navigator
1225
- from pymodaq.daq_viewer.daq_viewer_main import DAQ_Viewer
1226
-
1227
- class UI:
1228
- def __init__(self):
1229
- pass
1230
-
1231
- class FakeDaqScan:
1232
- def __init__(self, area):
1233
- self.area = area
1234
- self.detector_modules = None
1235
- self.ui = UI()
1236
- self.dock = Dock('2D scan', size=(500, 300), closable=False)
1237
-
1238
- form = QtWidgets.QWidget()
1239
- self.ui.scan2D_graph = Viewer2D(form)
1240
- self.dock.addWidget(form)
1241
- self.area.addDock(self.dock)
1242
-
1243
- def get_scan_params(param):
1244
- print(param)
1245
- print(param.scan_info.positions)
1246
-
1247
- #
1248
- # ####simple sequential scan test
1249
- # prog = Scanner(actuators=['Xaxis', 'Yaxis', 'Theta Axis'])
1250
- # prog.settings_tree.show()
1251
- # #prog.actuators = ['xxx', 'yyy']
1252
- # prog.scan_params_signal.connect(get_scan_params)
1253
-
1254
- win = QtWidgets.QMainWindow()
1255
- area = DockArea()
1256
-
1257
- win.setCentralWidget(area)
1258
- win.resize(1000, 500)
1259
- win.setWindowTitle('pymodaq main')
1260
- fake = FakeDaqScan(area)
1261
-
1262
- prog = DAQ_Viewer(area, title="Testing", DAQ_type='DAQ2D', parent_scan=fake)
1263
- prog.ui.IniDet_pb.click()
1264
- QThread.msleep(1000)
1265
- QtWidgets.QApplication.processEvents()
1266
- prog2 = Navigator()
1267
- widgnav = QtWidgets.QWidget()
1268
- prog2 = Navigator(widgnav)
1269
- nav_dock = Dock('Navigator')
1270
- nav_dock.addWidget(widgnav)
1271
- area.addDock(nav_dock)
1272
- QThread.msleep(1000)
1273
- QtWidgets.QApplication.processEvents()
1274
-
1275
- fake.detector_modules = [prog, prog2]
1276
- items = OrderedDict()
1277
- items[prog.title] = dict(viewers=[view for view in prog.ui.viewers],
1278
- names=[view.title for view in prog.ui.viewers],
1279
- )
1280
- items['Navigator'] = dict(viewers=[prog2.viewer],
1281
- names=['Navigator'])
1282
- items["DaqScan"] = dict(viewers=[fake.ui.scan2D_graph],
1283
- names=["DaqScan"])
1284
-
1285
- prog = Scanner(items, actuators=['Xaxis', 'Yaxis'])
1286
- prog.settings_tree.show()
1287
- prog.scan_params_signal.connect(get_scan_params)
1288
- win.show()
1289
- sys.exit(app.exec_())