pymodaq 3.6.12__py3-none-any.whl → 4.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pymodaq might be problematic. Click here for more details.

Files changed (233) hide show
  1. pymodaq/__init__.py +13 -6
  2. pymodaq/control_modules/__init__.py +0 -7
  3. pymodaq/control_modules/daq_move.py +965 -2
  4. pymodaq/control_modules/daq_move_ui.py +319 -0
  5. pymodaq/control_modules/daq_viewer.py +1573 -3
  6. pymodaq/control_modules/daq_viewer_ui.py +393 -0
  7. pymodaq/control_modules/mocks.py +51 -0
  8. pymodaq/control_modules/move_utility_classes.py +709 -8
  9. pymodaq/control_modules/utils.py +256 -0
  10. pymodaq/control_modules/viewer_utility_classes.py +663 -6
  11. pymodaq/daq_utils.py +89 -0
  12. pymodaq/dashboard.py +91 -72
  13. pymodaq/examples/custom_app.py +12 -11
  14. pymodaq/examples/custom_viewer.py +10 -10
  15. pymodaq/examples/function_plotter.py +16 -13
  16. pymodaq/examples/nonlinearscanner.py +8 -6
  17. pymodaq/examples/parameter_ex.py +7 -7
  18. pymodaq/examples/preset_MockCamera.xml +1 -0
  19. pymodaq/extensions/__init__.py +16 -0
  20. pymodaq/extensions/console.py +76 -0
  21. pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
  22. pymodaq/extensions/daq_scan.py +1339 -0
  23. pymodaq/extensions/daq_scan_ui.py +240 -0
  24. pymodaq/extensions/h5browser.py +23 -0
  25. pymodaq/{pid → extensions/pid}/__init__.py +4 -2
  26. pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
  27. pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
  28. pymodaq/{pid → extensions/pid}/utils.py +52 -6
  29. pymodaq/extensions/utils.py +40 -0
  30. pymodaq/post_treatment/__init__.py +6 -0
  31. pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
  32. pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
  33. pymodaq/post_treatment/load_and_plot.py +219 -0
  34. pymodaq/post_treatment/process_to_scalar.py +263 -0
  35. pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
  36. pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
  37. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
  38. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
  39. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
  40. pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
  41. pymodaq/resources/VERSION +1 -1
  42. pymodaq/resources/config_template.toml +32 -13
  43. pymodaq/resources/preset_default.xml +1 -1
  44. pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
  45. pymodaq/utils/__init__.py +0 -29
  46. pymodaq/utils/abstract/__init__.py +48 -0
  47. pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
  48. pymodaq/utils/array_manipulation.py +379 -8
  49. pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
  50. pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
  51. pymodaq/utils/config.py +448 -0
  52. pymodaq/utils/conftests.py +5 -0
  53. pymodaq/utils/daq_utils.py +828 -8
  54. pymodaq/utils/data.py +1873 -7
  55. pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
  56. pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
  57. pymodaq/{daq_utils → utils}/enums.py +12 -7
  58. pymodaq/utils/exceptions.py +37 -0
  59. pymodaq/utils/factory.py +82 -0
  60. pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
  61. pymodaq/utils/gui_utils/custom_app.py +129 -0
  62. pymodaq/utils/gui_utils/file_io.py +66 -0
  63. pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
  64. pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
  65. pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
  66. pymodaq/utils/gui_utils/widgets/label.py +24 -0
  67. pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
  68. pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
  69. pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
  70. pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
  71. pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
  72. pymodaq/utils/h5modules/__init__.py +1 -0
  73. pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
  74. pymodaq/utils/h5modules/browsing.py +683 -0
  75. pymodaq/utils/h5modules/data_saving.py +839 -0
  76. pymodaq/utils/h5modules/h5logging.py +110 -0
  77. pymodaq/utils/h5modules/module_saving.py +350 -0
  78. pymodaq/utils/h5modules/saving.py +914 -0
  79. pymodaq/utils/h5modules/utils.py +85 -0
  80. pymodaq/utils/logger.py +64 -6
  81. pymodaq/utils/managers/action_manager.py +460 -0
  82. pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
  83. pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
  84. pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
  85. pymodaq/utils/managers/parameter_manager.py +110 -0
  86. pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
  87. pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
  88. pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
  89. pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
  90. pymodaq/utils/math_utils.py +546 -10
  91. pymodaq/{daq_utils → utils}/messenger.py +5 -1
  92. pymodaq/utils/parameter/__init__.py +2 -15
  93. pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
  94. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
  95. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
  96. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
  97. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
  98. pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
  99. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
  100. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
  101. pymodaq/utils/parameter/utils.py +206 -11
  102. pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
  103. pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
  104. pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
  105. pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
  106. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
  107. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
  108. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
  109. pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
  110. pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
  111. pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
  112. pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
  113. pymodaq/utils/plotting/navigator.py +355 -0
  114. pymodaq/utils/plotting/scan_selector.py +480 -0
  115. pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
  116. pymodaq/utils/plotting/utils/filter.py +538 -0
  117. pymodaq/utils/plotting/utils/lineout.py +224 -0
  118. pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
  119. pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
  120. pymodaq/utils/plotting/widgets.py +76 -0
  121. pymodaq/utils/scanner/__init__.py +10 -0
  122. pymodaq/utils/scanner/scan_factory.py +204 -0
  123. pymodaq/utils/scanner/scanner.py +271 -0
  124. pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
  125. pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
  126. pymodaq/utils/scanner/scanners/sequential.py +192 -0
  127. pymodaq/utils/scanner/scanners/tabular.py +294 -0
  128. pymodaq/utils/scanner/utils.py +83 -0
  129. pymodaq/utils/slicing.py +47 -0
  130. pymodaq/utils/svg/__init__.py +6 -0
  131. pymodaq/utils/svg/svg_renderer.py +20 -0
  132. pymodaq/utils/svg/svg_view.py +35 -0
  133. pymodaq/utils/svg/svg_viewer2D.py +51 -0
  134. pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
  135. pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
  136. pymodaq/utils/units.py +216 -0
  137. pymodaq-4.0.1.dist-info/METADATA +159 -0
  138. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
  140. pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
  141. pymodaq/daq_move/daq_move_gui.py +0 -279
  142. pymodaq/daq_move/daq_move_gui.ui +0 -534
  143. pymodaq/daq_move/daq_move_main.py +0 -1042
  144. pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
  145. pymodaq/daq_move/utility_classes.py +0 -671
  146. pymodaq/daq_scan.py +0 -2160
  147. pymodaq/daq_utils/array_manipulation.py +0 -386
  148. pymodaq/daq_utils/config.py +0 -273
  149. pymodaq/daq_utils/conftests.py +0 -7
  150. pymodaq/daq_utils/custom_parameter_tree.py +0 -9
  151. pymodaq/daq_utils/daq_enums.py +0 -133
  152. pymodaq/daq_utils/daq_utils.py +0 -1402
  153. pymodaq/daq_utils/exceptions.py +0 -71
  154. pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
  155. pymodaq/daq_utils/gui_utils/file_io.py +0 -75
  156. pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
  157. pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
  158. pymodaq/daq_utils/h5exporters.py +0 -242
  159. pymodaq/daq_utils/h5modules.py +0 -1559
  160. pymodaq/daq_utils/h5utils.py +0 -241
  161. pymodaq/daq_utils/managers/action_manager.py +0 -236
  162. pymodaq/daq_utils/managers/parameter_manager.py +0 -57
  163. pymodaq/daq_utils/math_utils.py +0 -705
  164. pymodaq/daq_utils/parameter/__init__.py +0 -1
  165. pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
  166. pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
  167. pymodaq/daq_utils/parameter/utils.py +0 -136
  168. pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
  169. pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
  170. pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
  171. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
  172. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
  173. pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
  174. pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
  175. pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
  176. pymodaq/daq_utils/plotting/items/__init__.py +0 -0
  177. pymodaq/daq_utils/plotting/navigator.py +0 -500
  178. pymodaq/daq_utils/plotting/scan_selector.py +0 -289
  179. pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
  180. pymodaq/daq_utils/plotting/utils/filter.py +0 -236
  181. pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
  182. pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
  183. pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
  184. pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
  185. pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
  186. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
  187. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
  188. pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
  189. pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
  190. pymodaq/daq_utils/scanner.py +0 -1289
  191. pymodaq/daq_utils/tree_layout/__init__.py +0 -0
  192. pymodaq/daq_viewer/__init__.py +0 -0
  193. pymodaq/daq_viewer/daq_gui_settings.py +0 -237
  194. pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
  195. pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
  196. pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
  197. pymodaq/daq_viewer/utility_classes.py +0 -673
  198. pymodaq/examples/logger_image/__init__.py +0 -0
  199. pymodaq/examples/logger_image/logger_displayer.py +0 -121
  200. pymodaq/examples/logger_image/setup.svg +0 -3119
  201. pymodaq/examples/logger_image/setup_svg.py +0 -114
  202. pymodaq/h5browser.py +0 -39
  203. pymodaq/utils/scanner.py +0 -15
  204. pymodaq-3.6.12.dist-info/METADATA +0 -39
  205. pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.12.dist-info/top_level.txt +0 -1
  207. /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
  208. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
  209. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
  210. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
  211. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
  212. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
  213. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
  214. /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
  215. /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
  216. /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
  217. /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
  218. /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
  219. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
  220. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
  221. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
  222. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
  223. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
  224. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
  225. /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
  226. /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
  227. /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
  228. /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
  229. /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
  230. /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
  231. /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
  232. /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
  233. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,1339 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """Automated scanning module functionalities for PyMoDAQ
5
+
6
+ Contains all objects related to the DAQScan module, to do automated scans, saving data...
7
+ """
8
+ from __future__ import annotations
9
+ from collections import OrderedDict
10
+ import logging
11
+ import os
12
+ from pathlib import Path
13
+ import sys
14
+ import tempfile
15
+ from typing import List, Tuple, TYPE_CHECKING
16
+
17
+ import numpy as np
18
+ from qtpy import QtWidgets, QtCore, QtGui
19
+ from qtpy.QtCore import QObject, Slot, QThread, Signal, QDateTime, QDate, QTime
20
+
21
+ from pymodaq.utils import data as data_mod
22
+ from pymodaq.utils.logger import set_logger, get_module_name
23
+ from pymodaq.utils.config import Config, get_set_preset_path
24
+ from pymodaq.utils.parameter import ioxml
25
+ from pymodaq.utils.plotting.data_viewers.viewer import ViewersEnum
26
+ from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter, ParameterTree
27
+
28
+ from pymodaq.utils import exceptions
29
+ from pymodaq.utils.plotting.data_viewers.viewer2D import Viewer2D
30
+ from pymodaq.utils.plotting.data_viewers.viewer1D import Viewer1D
31
+ from pymodaq.utils.plotting.navigator import Navigator
32
+ from pymodaq.utils.plotting.scan_selector import ScanSelector, SelectorItem
33
+ from pymodaq.utils.scanner.scanner import Scanner, scanner_factory #, adaptive, adaptive_losses
34
+ from pymodaq.utils.managers.batchscan_manager import BatchScanner
35
+ from pymodaq.utils.managers.modules_manager import ModulesManager
36
+ from pymodaq.post_treatment.load_and_plot import LoaderPlotter
37
+ from pymodaq.utils.messenger import messagebox
38
+ from pymodaq.extensions.daq_scan_ui import DAQScanUI
39
+
40
+ from pymodaq.utils import daq_utils as utils
41
+ from pymodaq.utils import gui_utils as gutils
42
+ from pymodaq.utils.h5modules.saving import H5Saver
43
+ from pymodaq.utils.h5modules import module_saving, data_saving
44
+
45
+ if TYPE_CHECKING:
46
+ from pymodaq.dashboard import DashBoard
47
+
48
+ config = Config()
49
+ logger = set_logger(get_module_name(__file__))
50
+
51
+ SHOW_POPUPS = config('scan', 'show_popups')
52
+
53
+
54
+ class ScanDataTemp:
55
+ """Convenience class to hold temporary data to be plotted in the live plots"""
56
+ def __init__(self, scan_index: int, indexes: Tuple[int], data: data_mod.DataToExport):
57
+ self.scan_index = scan_index
58
+ self.indexes = indexes
59
+ self.data = data
60
+
61
+
62
+ class DAQScan(QObject, ParameterManager):
63
+ """
64
+ Main class initializing a DAQScan module with its dashboard and scanning control panel
65
+ """
66
+ settings_name = 'daq_scan_settings'
67
+ command_daq_signal = Signal(utils.ThreadCommand)
68
+ status_signal = Signal(str)
69
+ live_data_1D_signal = Signal(list)
70
+
71
+ params = [
72
+ {'title': 'Time Flow:', 'name': 'time_flow', 'type': 'group', 'expanded': False, 'children': [
73
+ {'title': 'Wait time step (ms)', 'name': 'wait_time', 'type': 'int', 'value': 0,
74
+ 'tip': 'Wait time in ms after each step of acquisition (move and grab)'},
75
+ {'title': 'Wait time between (ms)', 'name': 'wait_time_between', 'type': 'int',
76
+ 'value': 0,
77
+ 'tip': 'Wait time in ms between move and grab processes'},
78
+ {'title': 'Timeout (ms)', 'name': 'timeout', 'type': 'int', 'value': 10000},
79
+ ]},
80
+ {'title': 'Scan options', 'name': 'scan_options', 'type': 'group', 'children': [
81
+ {'title': 'Naverage:', 'name': 'scan_average', 'type': 'int', 'value': 1, 'min': 1},
82
+ {'title': 'Group 0D data:', 'name': 'group0D', 'type': 'bool', 'value': True},
83
+ {'title': 'Sort 1D scan data:', 'name': 'sort_scan1D', 'type': 'bool', 'value': False},]},
84
+
85
+ {'title': 'Plotting options', 'name': 'plot_options', 'type': 'group', 'children': [
86
+ {'title': 'Get Probe signals', 'name': 'plot_probe', 'type': 'bool_push'},
87
+ {'title': 'Plot 0Ds:', 'name': 'plot_0d', 'type': 'itemselect'},
88
+ {'title': 'Plot 1Ds:', 'name': 'plot_1d', 'type': 'itemselect'},
89
+ {'title': 'Prepare Viewers', 'name': 'prepare_viewers', 'type': 'bool_push'},
90
+ {'title': 'Plot at each step?', 'name': 'plot_at_each_step', 'type': 'bool', 'value': True},
91
+ {'title': 'Refresh Plots (ms)', 'name': 'refresh_live', 'type': 'int', 'value': 1000, 'visible': False},
92
+ ]},
93
+ ]
94
+
95
+ def __init__(self, dockarea: gutils.DockArea = None, dashboard: DashBoard = None):
96
+ """
97
+
98
+ Parameters
99
+ ----------
100
+ dockarea: DockArea
101
+ instance of the modified pyqtgraph Dockarea
102
+ dashboard: DashBoard
103
+ instance of the pymodaq dashboard
104
+
105
+ """
106
+
107
+ logger.info('Initializing DAQScan')
108
+ QObject.__init__(self)
109
+ ParameterManager.__init__(self)
110
+
111
+ self.title = __class__.__name__
112
+
113
+ self.dockarea: gutils.DockArea = dockarea
114
+ self.dashboard: DashBoard = dashboard
115
+ if dashboard is None:
116
+ raise Exception('No valid dashboard initialized')
117
+
118
+ self.mainwindow = self.dockarea.parent()
119
+ self.ui: DAQScanUI = DAQScanUI(self.dockarea)
120
+
121
+ self.wait_time = 1000
122
+
123
+ self.navigator: Navigator = None
124
+ self.scan_selector: ScanSelector = None
125
+
126
+ self.ind_scan = 0
127
+ self.ind_average = 0
128
+
129
+ self._metada_dataset_set = False
130
+
131
+ self.curvilinear_values = []
132
+ self.plot_colors = utils.plot_colors
133
+
134
+ self.scan_thread: QThread = None
135
+
136
+ self.modules_manager = ModulesManager(self.dashboard.detector_modules, self.dashboard.actuators_modules)
137
+ self.modules_manager.settings.child('data_dimensions').setOpts(expanded=False)
138
+ self.modules_manager.settings.child('actuators_positions').setOpts(expanded=False)
139
+ self.modules_manager.detectors_changed.connect(self.clear_plot_from)
140
+
141
+ self.h5saver = H5Saver()
142
+ self.module_and_data_saver = module_saving.ScanSaver(self)
143
+ self.module_and_data_saver.h5saver = self.h5saver
144
+
145
+ self.extended_saver: data_saving.DataToExportExtendedSaver = None
146
+ self.h5temp: H5Saver = None
147
+ self.temp_path: tempfile.TemporaryDirectory = None
148
+
149
+ self.h5saver.settings.child('do_save').hide()
150
+ self.h5saver.settings.child('custom_name').hide()
151
+ self.h5saver.new_file_sig.connect(self.create_new_file)
152
+
153
+ self.scanner = Scanner(actuators=self.modules_manager.actuators) # , adaptive_losses=adaptive_losses)
154
+ self.scan_parameters = None
155
+
156
+ self.batcher: BatchScanner = None
157
+ self.batch_started = False
158
+ self.ind_batch = 0
159
+
160
+ self.modules_manager.actuators_changed[list].connect(self.update_actuators)
161
+
162
+ self.setup_ui()
163
+ self.ui.command_sig.connect(self.process_ui_cmds)
164
+
165
+ self.create_dataset_settings()
166
+ self.setup_modules(self.dashboard.title)
167
+ self.set_config()
168
+
169
+ self.live_plotter = LoaderPlotter(self.dockarea)
170
+ self.live_timer = QtCore.QTimer()
171
+ self.live_timer.timeout.connect(self.update_live_plots)
172
+
173
+ self.ui.enable_start_stop(True)
174
+ logger.info('DAQScan Initialized')
175
+
176
+ def plot_from(self):
177
+ self.modules_manager.get_det_data_list()
178
+ data0D = self.modules_manager.settings['data_dimensions', 'det_data_list0D']
179
+ data1D = self.modules_manager.settings['data_dimensions', 'det_data_list1D']
180
+ data0D['selected'] = data0D['all_items']
181
+ data1D['selected'] = data1D['all_items']
182
+ self.settings.child('plot_options', 'plot_0d').setValue(data0D)
183
+ self.settings.child('plot_options', 'plot_1d').setValue(data1D)
184
+
185
+ def setup_ui(self):
186
+ self.ui.populate_toolbox_widget([self.settings_tree, self.h5saver.settings_tree],
187
+ ['General Settings', 'Save Settings'])
188
+
189
+ self.ui.set_scanner_settings(self.scanner.parent_widget)
190
+ self.ui.set_modules_settings(self.modules_manager.settings_tree)
191
+
192
+ self.plotting_settings_tree = ParameterTree()
193
+ self.plotting_settings_tree.setParameters(self.settings.child('plot_options'))
194
+ self.ui.set_plotting_settings(self.plotting_settings_tree)
195
+
196
+ ################
197
+ # CONFIG/SETUP UI / EXIT
198
+
199
+ def set_config(self):
200
+ self.settings.child('time_flow', 'wait_time').setValue(config['scan']['timeflow']['wait_time'])
201
+ self.settings.child('time_flow', 'wait_time_between').setValue(config['scan']['timeflow']['wait_time'])
202
+ self.settings.child('time_flow', 'timeout').setValue(config['scan']['timeflow']['timeout'])
203
+
204
+ self.settings.child('scan_options', 'scan_average').setValue(config['scan']['Naverage'])
205
+ self.settings.child('scan_options', 'sort_scan1D').setValue(config['scan']['sort1D'])
206
+
207
+ def setup_modules(self, filename):
208
+ """
209
+
210
+ """
211
+ try:
212
+ # todo update with v4 layout
213
+ pass
214
+ ######################################################################
215
+ # set scan selector
216
+ # items = OrderedDict()
217
+ # if self.navigator is not None:
218
+ # items["Navigator"] = dict(viewers=[self.navigator.viewer], names=["Navigator"])
219
+ # for det in self.modules_manager.detectors_all:
220
+ # if len([view for view in det.ui.viewers if view.viewer_type == 'Data2D']) != 0:
221
+ # items[det.title] = dict(viewers=[view for view in det.ui.viewers if view.viewer_type == 'Data2D'],
222
+ # names=[view.title for view in det.ui.viewers if
223
+ # view.viewer_type == 'Data2D'], )
224
+ # items["DAQScan"] = dict(viewers=[self.ui.scan2D_graph], names=["DAQScan"])
225
+ #
226
+ # if self.navigator is not None:
227
+ # items = OrderedDict(Navigator=dict(viewers=[self.navigator.viewer], names=["Navigator"]))
228
+ # items.update(self.scanner.scan_selector.viewers_items)
229
+ #
230
+ # self.scanner.viewers_items = items
231
+ #
232
+ # self.scanner.scan_selector.widget.setVisible(False)
233
+ # self.scanner.scan_selector.settings.child('scan_options', 'scan_type').hide()
234
+ #
235
+ # self.scanner.scan_selector.widget.setVisible(False)
236
+ # self.scanner.scan_selector.show_scan_selector(visible=False)
237
+ #
238
+ # self.show_average_dock(False)
239
+ #
240
+ # self.ui.scan_dock.setEnabled(True)
241
+ # self.file_menu.setEnabled(True)
242
+ # self.settings_menu.setEnabled(True)
243
+ # self.create_new_file(True)
244
+
245
+ except Exception as e:
246
+ logger.exception(str(e))
247
+ # self.update_status(getLineInfo()+str(e), self.wait_time, log_type='log')
248
+
249
+ def create_average_dock(self):
250
+ #todo update with v4 layout
251
+ self.ui.average_dock = gutils.dock.Dock("Averaging")
252
+ average_tab = QtWidgets.QTabWidget()
253
+ average1D_widget = QtWidgets.QWidget()
254
+ average2D_widget = QtWidgets.QWidget()
255
+
256
+ # %% init the 1D viewer
257
+ self.ui.average1D_graph = Viewer1D(average1D_widget)
258
+
259
+ # %% init the 2D viewer
260
+ self.ui.average2D_graph = Viewer2D(average2D_widget)
261
+
262
+ average_tab.addTab(average1D_widget, '1D plot Average')
263
+ average_tab.addTab(average2D_widget, '2D plot Average')
264
+
265
+ self.ui.average_dock.addWidget(average_tab)
266
+ self.dockarea.addDock(self.ui.average_dock, 'right', self.ui.scan_dock)
267
+
268
+ self.ui.average_dock.setVisible(False)
269
+
270
+ def process_ui_cmds(self, cmd: utils.ThreadCommand):
271
+ """Process commands sent by actions done in the ui
272
+
273
+ Parameters
274
+ ----------
275
+ cmd: ThreadCommand
276
+ Possible values are:
277
+ * quit
278
+ * ini_positions
279
+ * start
280
+ * start_batch
281
+ * stop
282
+ * move_at
283
+ * show_log
284
+ * load
285
+ * save
286
+ * show_file
287
+ * navigator
288
+ * batch
289
+ * viewers_changed
290
+ """
291
+ if cmd.command == 'quit':
292
+ self.quit_fun()
293
+ elif cmd.command == 'ini_positions':
294
+ self.set_ini_positions()
295
+ elif cmd.command == 'start':
296
+ self.start_scan()
297
+ elif cmd.command == 'start_batch':
298
+ self.start_scan_batch()
299
+ elif cmd.command == 'stop':
300
+ self.stop_scan()
301
+ elif cmd.command == 'move_at':
302
+ self.move_to_crosshair()
303
+ elif cmd.command == 'show_log':
304
+ self.show_log()
305
+ elif cmd.command == 'load':
306
+ self.load_file()
307
+ elif cmd.command == 'save':
308
+ self.save_file()
309
+ elif cmd.command == 'show_file':
310
+ self.show_file_content()
311
+ elif cmd.command == 'navigator':
312
+ self.show_navigator()
313
+ elif cmd.command == 'batch':
314
+ self.show_batcher(self.ui.menubar)
315
+ elif cmd.command == 'viewers_changed':
316
+ ...
317
+
318
+ def show_log(self):
319
+ """Open the log file in the default text editor"""
320
+ import webbrowser
321
+ webbrowser.open(logger.parent.handlers[0].baseFilename)
322
+
323
+ def quit_fun(self):
324
+ """
325
+ Quit the current instance of DAQ_scan
326
+
327
+ See Also
328
+ --------
329
+ quit_fun
330
+ """
331
+ try:
332
+ if self.temp_path is not None:
333
+ try:
334
+ self.h5temp.close()
335
+ self.temp_path.cleanup()
336
+ except Exception as e:
337
+ logger.exception(str(e))
338
+
339
+ self.h5saver.close_file()
340
+ self.ui.average_dock.close()
341
+ self.ui.scan_dock.close()
342
+ self.dockarea.close()
343
+
344
+ except Exception as e:
345
+ logger.exception(str(e))
346
+
347
+ def create_dataset_settings(self):
348
+ # params about dataset attributes and scan attibutes
349
+ date = QDateTime(QDate.currentDate(), QTime.currentTime())
350
+ params_dataset = [{'title': 'Dataset information', 'name': 'dataset_info', 'type': 'group', 'children': [
351
+ {'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config['user']['name']},
352
+ {'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
353
+ {'title': 'Sample:', 'name': 'sample', 'type': 'str', 'value': ''},
354
+ {'title': 'Experiment type:', 'name': 'experiment_type', 'type': 'str', 'value': ''},
355
+ {'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''}]}]
356
+
357
+ params_scan = [{'title': 'Scan information', 'name': 'scan_info', 'type': 'group', 'children': [
358
+ {'title': 'Author:', 'name': 'author', 'type': 'str', 'value': config['user']['name']},
359
+ {'title': 'Date/time:', 'name': 'date_time', 'type': 'date_time', 'value': date},
360
+ {'title': 'Scan type:', 'name': 'scan_type', 'type': 'str', 'value': ''},
361
+ {'title': 'Scan subtype:', 'name': 'scan_sub_type', 'type': 'str', 'value': ''},
362
+ {'title': 'Scan name:', 'name': 'scan_name', 'type': 'str', 'value': '', 'readonly': True},
363
+ {'title': 'Description:', 'name': 'description', 'type': 'text', 'value': ''},
364
+ ]}]
365
+
366
+ self.dataset_attributes = Parameter.create(name='Attributes', type='group', children=params_dataset)
367
+ self.scan_attributes = Parameter.create(name='Attributes', type='group', children=params_scan)
368
+ ###################
369
+ # external modules
370
+
371
+ def show_batcher(self, menubar):
372
+ self.batcher = BatchScanner(self.dockarea, self.modules_manager.actuators_all,
373
+ self.modules_manager.detectors_all)
374
+ self.batcher.create_menu(menubar)
375
+ self.batcher.setupUI()
376
+ self.ui.set_action_visible('start_batch', True)
377
+
378
+ def start_scan_batch(self):
379
+ self.batch_started = True
380
+ self.ind_batch = 0
381
+ self.loop_scan_batch()
382
+
383
+ def loop_scan_batch(self):
384
+ if self.ind_batch >= len(self.batcher.scans_names):
385
+ self.stop_scan()
386
+ return
387
+ self.scanner = self.batcher.get_scan(self.batcher.scans_names[self.ind_batch])
388
+ actuators, detectors = self.batcher.get_act_dets()
389
+ self.set_scan_batch(actuators[self.batcher.scans_names[self.ind_batch]],
390
+ detectors[self.batcher.scans_names[self.ind_batch]])
391
+ self.start_scan()
392
+
393
+ def set_scan_batch(self, actuators, detectors):
394
+ self.modules_manager.selected_detectors_name = detectors
395
+ self.modules_manager.selected_actuators_name = actuators
396
+ QtWidgets.QApplication.processEvents()
397
+
398
+ def show_file_attributes(self, type_info='dataset'):
399
+ """
400
+ Switch the type_info value.
401
+
402
+ In case of :
403
+ * *scan* : Set parameters showing top false
404
+ * *dataset* : Set parameters showing top false
405
+ * *managers* : Set parameters showing top false. Add the save/cancel buttons to the accept/reject dialog (to save managers parameters in a xml file).
406
+
407
+ Finally, in case of accepted managers type info, save the managers parameters in a xml file.
408
+
409
+ =============== =========== ====================================
410
+ **Parameters** **Type** **Description**
411
+ *type_info* string The file type information between
412
+ * scan
413
+ * dataset
414
+ * managers
415
+ =============== =========== ====================================
416
+
417
+ See Also
418
+ --------
419
+ custom_tree.parameter_to_xml_file, create_menu
420
+ """
421
+ if SHOW_POPUPS:
422
+ dialog = QtWidgets.QDialog()
423
+ vlayout = QtWidgets.QVBoxLayout()
424
+ tree = ParameterTree()
425
+ tree.setMinimumWidth(400)
426
+ tree.setMinimumHeight(500)
427
+ if type_info == 'scan':
428
+ tree.setParameters(self.scan_attributes, showTop=False)
429
+ elif type_info == 'dataset':
430
+ tree.setParameters(self.dataset_attributes, showTop=False)
431
+
432
+ vlayout.addWidget(tree)
433
+ dialog.setLayout(vlayout)
434
+ buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
435
+ buttonBox.addButton('Cancel', buttonBox.RejectRole)
436
+ buttonBox.addButton('Apply', buttonBox.AcceptRole)
437
+ buttonBox.rejected.connect(dialog.reject)
438
+ buttonBox.accepted.connect(dialog.accept)
439
+
440
+ vlayout.addWidget(buttonBox)
441
+ dialog.setWindowTitle('Fill in information about this {}'.format(type_info))
442
+ res = dialog.exec()
443
+ else:
444
+ res = True
445
+ return res
446
+
447
+ def show_file_content(self):
448
+ try:
449
+ self.h5saver.init_file(addhoc_file_path=self.h5saver.settings.child(('current_h5_file')).value())
450
+ self.h5saver.show_file_content()
451
+ except Exception as e:
452
+ logger.exception(str(e))
453
+
454
+ def show_navigator(self):
455
+
456
+ if self.navigator is None:
457
+ # loading navigator
458
+ self.navigator_dock = gutils.Dock('Navigator')
459
+ widgnav = QtWidgets.QWidget()
460
+ self.navigator_dock.addWidget(widgnav)
461
+ self.dockarea.addDock(self.navigator_dock)
462
+ self.navigator_dock.float()
463
+
464
+ self.navigator = Navigator(widgnav)
465
+
466
+ self.navigator.log_signal[str].connect(self.dashboard.add_status)
467
+ self.navigator.settings.child('settings', 'Load h5').hide()
468
+ self.navigator.set_action_visible('load_scan', False)
469
+
470
+ self.navigator.sig_double_clicked.connect(self.move_at)
471
+ self.navigator.h5saver = self.h5saver
472
+ self.navigator.list_2D_scans()
473
+
474
+ self.show_scan_selector()
475
+
476
+ def show_scan_selector(self):
477
+ viewer_items = []
478
+ if self.navigator is not None:
479
+ viewer_items.append(SelectorItem(self.navigator.viewer, name='Navigator'))
480
+ #
481
+ # for viewer in self.live_plotter.viewers:
482
+ # viewer_items.update({viewer.title: dict(viewers=[viewer], names=[viewer.title])})
483
+ self.scan_selector = ScanSelector(viewer_items)
484
+
485
+ self.ui.add_scanner_settings(self.scan_selector.settings_tree)
486
+
487
+ self.scan_selector.scan_select_signal.connect(self.scanner.update_from_scan_selector)
488
+
489
+ ################
490
+ # LOADING SAVING
491
+
492
+ def load_file(self):
493
+ self.h5saver.load_file(self.h5saver.h5_file_path)
494
+
495
+ def save_file(self):
496
+ if not os.path.isdir(self.h5saver.settings['base_path']):
497
+ os.mkdir(self.h5saver.settings['base_path'])
498
+ filename = gutils.file_io.select_file(self.h5saver.settings['base_path'], save=True, ext='h5')
499
+ self.h5saver.h5_file.copy_file(str(filename))
500
+
501
+ def save_metadata(self, node, type_info='dataset_info'):
502
+ """
503
+ Switch the type_info value with :
504
+ * *'dataset_info'* : Give the params attributes the dataset_attributes values
505
+ * *'dataset'* : Give the params attributes the scan_attributes values
506
+
507
+ |
508
+ | Once done, course the params and add string casted date/time metadata as an element of attributes array.
509
+ | Save the contents of given parameter object into a xml string unde the attributes settings.
510
+
511
+ =============== =================== =========================================
512
+ **Parameters** **Type** **Description**
513
+ *node* pytables h5 node Root node to be treated
514
+ *type_info* string File type info between :
515
+ * 'dataset_info'
516
+ * 'scan_info'
517
+ =============== =================== =========================================
518
+
519
+ See Also
520
+ --------
521
+ custom_tree.parameter_to_xml_string
522
+ """
523
+
524
+ attr = node.attrs
525
+ if type_info == 'dataset_info':
526
+ attr['type'] = 'dataset'
527
+ params = self.dataset_attributes
528
+ else:
529
+ attr['type'] = 'scan'
530
+ params = self.scan_attributes
531
+ for child in params.child(type_info).children():
532
+ if type(child.value()) is QDateTime:
533
+ attr[child.name()] = child.value().toString('dd/mm/yyyy HH:MM:ss')
534
+ else:
535
+ attr[child.name()] = child.value()
536
+ if type_info == 'dataset_info':
537
+ # save contents of given parameter object into an xml string under the attribute settings
538
+ settings_str = b'<All_settings title="All Settings" type="group">' + \
539
+ ioxml.parameter_to_xml_string(params) + \
540
+ ioxml.parameter_to_xml_string(self.settings)
541
+ # ioxml.parameter_to_xml_string(
542
+ # self.dashboard.preset_manager.preset_params) +\
543
+ settings_str += b'</All_settings>'
544
+ attr['settings'] = settings_str
545
+
546
+ elif type_info == 'scan_info':
547
+ settings_all = [ioxml.parameter_to_xml_string(params),
548
+ ioxml.parameter_to_xml_string(self.settings),
549
+ ioxml.parameter_to_xml_string(self.h5saver.settings),
550
+ ioxml.parameter_to_xml_string(self.scanner.settings)]
551
+
552
+ settings_str = b'<All_settings title="All Settings" type="group">'
553
+ for set in settings_all:
554
+ if len(settings_str + set) < 60000:
555
+ # size limit for any object header (including all the other attributes) is 64kb
556
+ settings_str += set
557
+ else:
558
+ break
559
+ settings_str += b'</All_settings>'
560
+ attr['settings'] = settings_str
561
+
562
+ def create_new_file(self, new_file):
563
+ if new_file:
564
+ self._metada_dataset_set = False
565
+ self.h5saver.init_file(update_h5=new_file)
566
+ self.module_and_data_saver.h5saver = self.h5saver
567
+ res = self.update_file_settings()
568
+ if new_file:
569
+ self.ui.enable_start_stop()
570
+ return res
571
+
572
+ def update_file_settings(self):
573
+ try:
574
+ res = True
575
+ if not self._metada_dataset_set:
576
+ res = self.set_metadata_about_dataset()
577
+ self.save_metadata(self.h5saver.raw_group, 'dataset_info')
578
+
579
+ if self.navigator is not None:
580
+ self.navigator.update_h5file(self.h5saver.h5_file)
581
+ self.navigator.settings.child('settings', 'filepath').setValue(self.h5saver.h5_file.filename)
582
+
583
+ # # set attributes to the current group, such as scan_type....
584
+ # self.scan_attributes.child('scan_info', 'scan_type').setValue(
585
+ # self.scanner.settings.child('scan_type').value())
586
+ # self.scan_attributes.child('scan_info', 'scan_sub_type').setValue(
587
+ # self.scanner.settings.child('scan_sub_type').value())
588
+ #
589
+ # scan_node = self.module_and_data_saver.get_set_node()
590
+ # self.scan_attributes.child('scan_info', 'scan_name').setValue(scan_node.name)
591
+ # self.scan_attributes.child('scan_info', 'description').setValue('')
592
+ #
593
+ # res = self.set_metadata_about_current_scan()
594
+
595
+ return res
596
+
597
+ except Exception as e:
598
+ logger.exception(str(e))
599
+
600
+ def update_scan_info(self):
601
+ # set attributes to the current group, such as scan_type....
602
+ self.scan_attributes.child('scan_info', 'scan_type').setValue(
603
+ self.scanner.settings.child('scan_type').value())
604
+ self.scan_attributes.child('scan_info', 'scan_sub_type').setValue(
605
+ self.scanner.settings.child('scan_sub_type').value())
606
+ scan_node = self.module_and_data_saver.get_set_node(new=False)
607
+ if scan_node.attrs['scan_done']:
608
+ scan_name = self.module_and_data_saver.get_next_node_name()
609
+ else:
610
+ scan_name = scan_node.name
611
+ self.scan_attributes.child('scan_info', 'scan_name').setValue(scan_name)
612
+ self.scan_attributes.child('scan_info', 'description').setValue('')
613
+
614
+ res = self.set_metadata_about_current_scan()
615
+ return res
616
+
617
+ # PROCESS MODIFICATIONS
618
+ def update_actuators(self, actuators: List[str]):
619
+ self.scanner.actuators = self.modules_manager.actuators
620
+
621
+ def move_to_crosshair(self, *args, **kwargs):
622
+ if self.ui.is_action_checked('move_at'):
623
+ self.modules_manager.connect_actuators()
624
+ self.live_plotter.connect_double_clicked(self.move_at)
625
+ else:
626
+ self.live_plotter.disconnect(self.move_at)
627
+ self.modules_manager.connect_actuators(False)
628
+
629
+ def move_at(self, posx: float, posy: float = None):
630
+ if logging.getLevelName(logger.level) == 'DEBUG':
631
+ print(f'clicked at: {posx}, {posy}')
632
+ positions = [posx, posy]
633
+ positions = positions[:self.scanner.n_axes]
634
+ self.modules_manager.move_actuators(positions)
635
+
636
+ def value_changed(self, param):
637
+ """
638
+
639
+ """
640
+ if param.name() == 'scan_average':
641
+ self.ui.show_average_step(param.value() > 1)
642
+ elif param.name() == 'prepare_viewers':
643
+ self.prepare_viewers()
644
+ elif param.name() == 'plot_probe':
645
+ self.plot_from()
646
+ elif param.name() == 'plot_at_each_step':
647
+ self.settings.child('plot_options', 'refresh_live').show(not param.value())
648
+
649
+ def clear_plot_from(self):
650
+ self.settings.child('plot_options', 'plot_0d').setValue(dict(all_items=[], selected=[]))
651
+ self.settings.child('plot_options', 'plot_1d').setValue(dict(all_items=[], selected=[]))
652
+
653
+ def prepare_viewers(self):
654
+
655
+ viewers_enum = [ViewersEnum('Data0D').increase_dim(self.scanner.n_axes)
656
+ for _ in range(len(self.settings['plot_options', 'plot_0d']['selected']))]
657
+ data_names = self.settings['plot_options', 'plot_0d']['selected']
658
+
659
+ if self.settings['scan_options', 'group0D'] and len(viewers_enum) > 0 and ViewersEnum('Data1D') in viewers_enum:
660
+ viewers_enum = [ViewersEnum('Data1D')]
661
+ data_names = [self.live_plotter.grouped_data1D_fullname]
662
+ viewers_enum.extend([ViewersEnum('Data1D').increase_dim(self.scanner.n_axes)
663
+ for _ in range(len(self.settings['plot_options', 'plot_1d']['selected']))])
664
+ data_names.extend(self.settings['plot_options', 'plot_1d']['selected'])
665
+ self.live_plotter.prepare_viewers(viewers_enum, viewers_name=data_names)
666
+
667
+ def update_status(self, txt, wait_time=0, log_type=None):
668
+ """
669
+ Show the txt message in the status bar with a delay of wait_time ms.
670
+
671
+ =============== =========== =======================
672
+ **Parameters** **Type** **Description**
673
+ *txt* string The message to show
674
+ *wait_time* int the delay of showing
675
+ *log_type* string the type of the log
676
+ =============== =========== =======================
677
+ """
678
+ self.ui.display_status(txt, wait_time)
679
+ self.status_signal.emit(txt)
680
+ logger.info(txt)
681
+
682
+ def save_scan(self):
683
+ #todo update with v4 layout
684
+ pass
685
+
686
+ @Slot(list)
687
+ def thread_status(self, status): # general function to get datas/infos from all threads back to the main
688
+ """
689
+ | General function to get datas/infos from all threads back to the main.
690
+ |
691
+
692
+ Switch the status with :
693
+ * *"Update status"* : Update the status bar with the status attribute txt message
694
+ * *"Update_scan_index"* : Set the value of the User Interface - indice_scan_sb attribute.
695
+ * *"Scan_done"* : Save the scan and init the positions
696
+ * *"Timeout"* : Set the "Timeout occured" in the User Interface-log message
697
+
698
+ See Also
699
+ --------
700
+ update_status, save_scan, set_ini_positions
701
+ """
702
+ if status[0] == "Update_Status":
703
+ self.update_status(status[1], wait_time=self.wait_time)
704
+
705
+ elif status[0] == "Update_scan_index":
706
+ # status[1] = [ind_scan,ind_average]
707
+ self.ind_scan = status[1][0]
708
+ self.ui.set_scan_step(status[1][0] + 1)
709
+ self.ind_average = status[1][1]
710
+ self.ui.set_scan_step_average(status[1][1] + 1)
711
+
712
+ elif status[0] == "Scan_done":
713
+ self.modules_manager.reset_signals()
714
+ self.live_timer.stop()
715
+ self.ui.set_scan_done()
716
+ scan_node = self.module_and_data_saver.get_set_node()
717
+ scan_node.attrs['scan_done'] = True
718
+ self.save_scan()
719
+ if not self.batch_started:
720
+ if not self.dashboard.overshoot:
721
+ self.set_ini_positions()
722
+ self.ui.set_action_enabled('ini_positions', True)
723
+ self.ui.set_action_enabled('start', True)
724
+
725
+ # reactivate module controls usiong remote_control
726
+ if hasattr(self.dashboard, 'remote_manager'):
727
+ remote_manager = getattr(self.dashboard, 'remote_manager')
728
+ remote_manager.activate_all(True)
729
+ if self.navigator is not None:
730
+ self.navigator.list_2D_scans()
731
+ else:
732
+ self.ind_batch += 1
733
+ self.loop_scan_batch()
734
+
735
+ elif status[0] == "Timeout":
736
+ self.ui.set_permanent_status('Timeout occurred')
737
+
738
+ ############
739
+ # PLOTTING
740
+
741
+ def save_temp_live_data(self, scan_data: ScanDataTemp):
742
+ if scan_data.scan_index == 0:
743
+ nav_axes = self.scanner.get_nav_axes()
744
+
745
+ #
746
+ # for nav_axe in nav_axes:
747
+ # nav_axe.index += 1
748
+ # nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
749
+ # index=0))
750
+ self.extended_saver.add_nav_axes(self.h5temp.raw_group, nav_axes)
751
+
752
+ self.extended_saver.add_data(self.h5temp.raw_group, scan_data.data, scan_data.indexes,
753
+ distribution=self.scanner.distribution)
754
+ if self.settings['plot_options', 'plot_at_each_step']:
755
+ self.update_live_plots()
756
+
757
+ def update_live_plots(self):
758
+
759
+ if self.settings['scan_options', 'scan_average'] > 1:
760
+ average_axis = 0
761
+ else:
762
+ average_axis = None
763
+ try:
764
+ self.live_plotter.load_plot_data(group_1D=self.settings['scan_options', 'group0D'],
765
+ average_axis=average_axis, average_index=self.ind_average,
766
+ target_at=self.scanner.positions[self.ind_scan])
767
+ except Exception as e:
768
+ logger.exception(str(e))
769
+ #################
770
+ # SCAN FLOW
771
+
772
+ def set_scan(self, scan=None) -> bool:
773
+ """
774
+ Sets the current scan given the selected settings. Makes some checks, increments the h5 file scans.
775
+ In case the dialog is cancelled, return False and aborts the scan
776
+ """
777
+ try:
778
+ # set the filename and path
779
+ if self.h5saver.h5_file is None: # only the first time start scan is called
780
+ self.create_new_file(True)
781
+ res = self.update_scan_info()
782
+ if not res:
783
+ return False
784
+
785
+ is_oversteps = self.scanner.set_scan()
786
+ if is_oversteps:
787
+ messagebox(text=f"An error occurred when establishing the scan steps. Actual settings "
788
+ f"gives approximately {int(self.scanner.n_steps)} steps."
789
+ f" Please check the steps number "
790
+ f"limit in the config file ({config['scan']['steps_limit']}) or modify"
791
+ f" your scan settings.")
792
+
793
+ if self.modules_manager.Nactuators != self.scanner.n_axes:
794
+ messagebox(text="There are not enough or too much selected move modules for this scan")
795
+ return False
796
+
797
+ if self.scanner.scan_sub_type == 'Adaptive':
798
+ #todo include this in scanners objects for the adaptive scanners
799
+ if len(self.modules_manager.get_selected_probed_data('0D')) == 0:
800
+ messagebox(text="In adaptive mode, you have to pick a 0D signal from which the algorithm will"
801
+ " determine the next positions to scan, see 'probe_data' in the modules selector"
802
+ " panel")
803
+ return False
804
+
805
+ self.ui.n_scan_steps = self.scanner.n_steps
806
+
807
+ # check if the modules are initialized
808
+ for module in self.modules_manager.actuators:
809
+ if not module.initialized_state:
810
+ raise exceptions.DAQ_ScanException('module ' + module.title + " is not initialized")
811
+
812
+ for module in self.modules_manager.detectors:
813
+ if not module.initialized_state:
814
+ raise exceptions.DAQ_ScanException('module ' + module.title + " is not initialized")
815
+
816
+ self.ui.enable_start_stop(True)
817
+ return True
818
+
819
+ except Exception as e:
820
+ logger.exception(str(e))
821
+ self.ui.enable_start_stop(False)
822
+
823
+ def set_metadata_about_current_scan(self):
824
+ """
825
+ Set the date/time and author values of the scan_info child of the scan_attributes tree.
826
+ Show the 'scan' file attributes.
827
+
828
+ See Also
829
+ --------
830
+ show_file_attributes
831
+ """
832
+ date = QDateTime(QDate.currentDate(), QTime.currentTime())
833
+ self.scan_attributes.child('scan_info', 'date_time').setValue(date)
834
+ self.scan_attributes.child('scan_info', 'author').setValue(
835
+ self.dataset_attributes.child('dataset_info', 'author').value())
836
+ if not self.batch_started:
837
+ res = self.show_file_attributes('scan')
838
+ else:
839
+ res = True
840
+ return res
841
+
842
+ def set_metadata_about_dataset(self):
843
+ """
844
+ Set the date value of the data_set_info-date_time child of the data_set_attributes tree.
845
+ Show the 'dataset' file attributes.
846
+
847
+ See Also
848
+ --------
849
+ show_file_attributes
850
+ """
851
+ date = QDateTime(QDate.currentDate(), QTime.currentTime())
852
+ self.dataset_attributes.child('dataset_info', 'date_time').setValue(date)
853
+ res = self.show_file_attributes('dataset')
854
+ self._metada_dataset_set = True
855
+ return res
856
+
857
+ def start_scan(self):
858
+ """
859
+ Start an acquisition calling the set_scan function.
860
+ Emit the command_DAQ signal "start_acquisition".
861
+
862
+ See Also
863
+ --------
864
+ set_scan
865
+ """
866
+ self.ui.display_status('Starting acquisition')
867
+ self.dashboard.overshoot = False
868
+ #deactivate double_clicked
869
+ if self.ui.is_action_checked('move_at'):
870
+ self.ui.get_action('move_at').trigger()
871
+
872
+ res = self.set_scan()
873
+ if res:
874
+ # deactivate module controls using remote_control
875
+ if hasattr(self.dashboard, 'remote_manager'):
876
+ remote_manager = getattr(self.dashboard, 'remote_manager')
877
+ remote_manager.activate_all(False)
878
+
879
+ self.module_and_data_saver.h5saver = self.h5saver
880
+ new_scan = self.module_and_data_saver.get_set_node(new=False).attrs['scan_done']
881
+ scan_node = self.module_and_data_saver.get_set_node(new=new_scan)
882
+ self.save_metadata(scan_node, 'scan_info')
883
+
884
+ self._init_live()
885
+
886
+ # mandatory to deal with multithreads
887
+ if self.scan_thread is not None:
888
+ self.command_daq_signal.disconnect()
889
+ if self.scan_thread.isRunning():
890
+ self.scan_thread.terminate()
891
+ while not self.scan_thread.isFinished():
892
+ QThread.msleep(100)
893
+ self.scan_thread = None
894
+
895
+ self.scan_thread = QThread()
896
+
897
+ scan_acquisition = DAQScanAcquisition(self.settings, self.scanner, self.h5saver.settings,
898
+ self.modules_manager,
899
+ module_saver=self.module_and_data_saver)
900
+ if config['scan']['scan_in_thread']:
901
+ scan_acquisition.moveToThread(self.scan_thread)
902
+ self.command_daq_signal[utils.ThreadCommand].connect(scan_acquisition.queue_command)
903
+ scan_acquisition.scan_data_tmp[ScanDataTemp].connect(self.save_temp_live_data)
904
+ scan_acquisition.status_sig[list].connect(self.thread_status)
905
+
906
+ self.scan_thread.scan_acquisition = scan_acquisition
907
+ self.scan_thread.start()
908
+
909
+ self.ui.set_action_enabled('ini_positions', False)
910
+ self.ui.set_action_enabled('start', False)
911
+ self.ui.set_scan_done(False)
912
+ if not self.settings['plot_options', 'plot_at_each_step']:
913
+ self.live_timer.start(self.settings['plot_options', 'refresh_live'])
914
+ self.command_daq_signal.emit(utils.ThreadCommand('start_acquisition'))
915
+ self.ui.set_permanent_status('Running acquisition')
916
+ logger.info('Running acquisition')
917
+
918
+ def _init_live(self):
919
+ Naverage = self.settings['scan_options', 'scan_average']
920
+ if Naverage > 1:
921
+ scan_shape = [Naverage]
922
+ scan_shape.extend(self.scanner.get_scan_shape())
923
+ else:
924
+ scan_shape = self.scanner.get_scan_shape()
925
+ if self.temp_path is not None:
926
+ try:
927
+ self.h5temp.close()
928
+ self.temp_path.cleanup()
929
+ except Exception as e:
930
+ logger.exception(str(e))
931
+
932
+ self.h5temp = H5Saver()
933
+ self.temp_path = tempfile.TemporaryDirectory(prefix='pymo')
934
+ addhoc_file_path = Path(self.temp_path.name).joinpath('temp_data.h5')
935
+ self.h5temp.init_file(custom_naming=True, addhoc_file_path=addhoc_file_path)
936
+ self.extended_saver: data_saving.DataToExportExtendedSaver =\
937
+ data_saving.DataToExportExtendedSaver(self.h5temp, extended_shape=scan_shape)
938
+ self.live_plotter.h5saver = self.h5temp
939
+
940
+ self.prepare_viewers()
941
+ QtWidgets.QApplication.processEvents()
942
+
943
+ def set_ini_positions(self):
944
+ """
945
+ Send the command_DAQ signal with "set_ini_positions" list item as an attribute.
946
+ """
947
+ self.command_daq_signal.emit(utils.ThreadCommand("set_ini_positions"))
948
+
949
+ def stop_scan(self):
950
+ """
951
+ Emit the command_DAQ signal "stop_acquisition".
952
+
953
+ See Also
954
+ --------
955
+ set_ini_positions
956
+ """
957
+ self.ui.set_permanent_status('Stoping acquisition')
958
+ self.command_daq_signal.emit(utils.ThreadCommand("stop_acquisition"))
959
+
960
+ if not self.dashboard.overshoot:
961
+ self.set_ini_positions() # do not set ini position again in case overshoot fired
962
+ status = 'Data Acquisition has been stopped by user'
963
+ else:
964
+ status = 'Data Acquisition has been stopped due to overshoot'
965
+
966
+ self.update_status(status, log_type='log')
967
+ self.ui.set_permanent_status('')
968
+
969
+ self.ui.set_action_enabled('ini_positions', True)
970
+ self.ui.set_action_enabled('start', True)
971
+
972
+ def do_scan(self, start_scan=True):
973
+ """Public method to start the scan programmatically"""
974
+ if start_scan:
975
+ if not self.ui.is_action_enabled('start'):
976
+ self.ui.get_action('set_scan').trigger()
977
+ QtWidgets.QApplication.processEvents()
978
+ self.ui.get_action('start').trigger()
979
+ else:
980
+ self.ui.get_action('stop').trigger()
981
+
982
+
983
+ class DAQScanAcquisition(QObject):
984
+ """
985
+ =========================== ========================================
986
+
987
+ =========================== ========================================
988
+
989
+ """
990
+ scan_data_tmp = Signal(ScanDataTemp)
991
+ status_sig = Signal(list)
992
+
993
+ def __init__(self, scan_settings: Parameter = None, scanner: Scanner = None,
994
+ h5saver_settings: Parameter = None, modules_manager: ModulesManager = None,
995
+ module_saver: module_saving.ScanSaver = None):
996
+
997
+ """
998
+ DAQScanAcquisition deal with the acquisition part of daq_scan, that is transferring commands to modules,
999
+ getting back data, saviong and letting know th UI about the scan status
1000
+
1001
+ """
1002
+
1003
+ super().__init__()
1004
+
1005
+ self.scan_settings = scan_settings
1006
+ self.modules_manager = modules_manager
1007
+ self.scanner = scanner
1008
+
1009
+ self.stop_scan_flag = False
1010
+ self.Naverage = self.scan_settings['scan_options', 'scan_average']
1011
+ self.ind_average = 0
1012
+ self.ind_scan = 0
1013
+
1014
+ self.isadaptive = self.scanner.scan_sub_type == 'Adaptive'
1015
+
1016
+ self.modules_manager.timeout_signal.connect(self.timeout)
1017
+ self.timeout_scan_flag = False
1018
+
1019
+
1020
+ self.move_done_flag = False
1021
+ self.det_done_flag = False
1022
+
1023
+ self.det_done_datas = data_mod.DataToExport('ScanData')
1024
+
1025
+ self.h5saver = H5Saver()
1026
+ self.h5saver.settings.restoreState(h5saver_settings.saveState())
1027
+ self.h5saver.init_file(addhoc_file_path=self.h5saver.settings['current_h5_file'])
1028
+
1029
+ self.module_and_data_saver: module_saving.ScanSaver = module_saver
1030
+
1031
+ # update the DAQ_Viewer's detector saver to DetectorExtendedSaver to take into account extended
1032
+ # arrays due to scan shape and eventual averaging
1033
+ scan_shape = self.scanner.get_scan_shape()
1034
+ if self.Naverage > 1:
1035
+ self.scan_shape = [self.Naverage]
1036
+ self.scan_shape.extend(scan_shape)
1037
+ else:
1038
+ self.scan_shape = scan_shape
1039
+
1040
+ for det in self.modules_manager.detectors:
1041
+ det.module_and_data_saver = module_saving.DetectorExtendedSaver(det, self.scan_shape)
1042
+ self.module_and_data_saver.h5saver = self.h5saver # will update its h5saver and all submodules's h5saver
1043
+
1044
+ @Slot(utils.ThreadCommand)
1045
+ def queue_command(self, command):
1046
+ """Process the commands sent by the main ui
1047
+
1048
+ Parameters
1049
+ ----------
1050
+ command: utils.ThreadCommand
1051
+ """
1052
+ if command.command == "start_acquisition":
1053
+ self.start_acquisition()
1054
+
1055
+ elif command.command == "stop_acquisition":
1056
+ self.stop_scan_flag = True
1057
+
1058
+ elif command.command == "set_ini_positions":
1059
+ self.set_ini_positions()
1060
+
1061
+ elif command.command == "move_stages":
1062
+ self.modules_manager.move_actuators(command.attribute)
1063
+
1064
+ def set_ini_positions(self):
1065
+ """
1066
+ | Set the positions from the scan_move attribute.
1067
+ |
1068
+ | Move all activated modules to specified positions.
1069
+ | Check the module corresponding to the name assigned in pos.
1070
+
1071
+ See Also
1072
+ --------
1073
+ DAQ_Move_main.daq_move.move_Abs
1074
+ """
1075
+ try:
1076
+ if self.scanner.scan_sub_type != 'Adaptive':
1077
+ self.modules_manager.move_actuators(list(self.scanner.positions[0]))
1078
+
1079
+ except Exception as e:
1080
+ logger.exception(str(e))
1081
+
1082
+ def start_acquisition(self):
1083
+ try:
1084
+ #todo hoaw to apply newlayout to adaptive mode?
1085
+
1086
+ self.modules_manager.connect_actuators()
1087
+ self.modules_manager.connect_detectors()
1088
+
1089
+ self.stop_scan_flag = False
1090
+
1091
+ Naxes = self.scanner.n_axes
1092
+ scan_type = self.scanner.scan_type
1093
+ self.navigation_axes = self.scanner.get_nav_axes()
1094
+ self.status_sig.emit(["Update_Status", "Acquisition has started", 'log'])
1095
+
1096
+ self.timeout_scan_flag = False
1097
+ for ind_average in range(self.Naverage):
1098
+ self.ind_average = ind_average
1099
+ self.ind_scan = -1
1100
+ while True:
1101
+ self.ind_scan += 1
1102
+ if not self.isadaptive:
1103
+ if self.ind_scan >= len(self.scanner.positions):
1104
+ break
1105
+ positions = self.scanner.positions[self.ind_scan] # get positions
1106
+ else:
1107
+ pass
1108
+ #todo update for v4
1109
+ # positions = learner.ask(1)[0][-1] # next point to probe
1110
+ # if self.scanner.scan_type == 'Tabular': # translate normalized curvilinear position to real coordinates
1111
+ # self.curvilinear = positions
1112
+ # length = 0.
1113
+ # for v in self.scanner.vectors:
1114
+ # length += v.norm()
1115
+ # if length >= self.curvilinear:
1116
+ # vec = v
1117
+ # frac_curvilinear = (self.curvilinear - (length - v.norm())) / v.norm()
1118
+ # break
1119
+ #
1120
+ # position = (vec.vectorize() * frac_curvilinear).translate_to(vec.p1()).p2()
1121
+ # positions = [position.x(), position.y()]
1122
+
1123
+ self.status_sig.emit(["Update_scan_index", [self.ind_scan, ind_average]])
1124
+
1125
+ if self.stop_scan_flag or self.timeout_scan_flag:
1126
+ break
1127
+
1128
+ #move motors of modules and wait for move completion
1129
+ positions = self.modules_manager.order_positions(self.modules_manager.move_actuators(positions))
1130
+
1131
+ QThread.msleep(self.scan_settings.child('time_flow', 'wait_time_between').value())
1132
+
1133
+ #grab datas and wait for grab completion
1134
+ self.det_done(self.modules_manager.grab_datas(positions=positions), positions)
1135
+
1136
+ if self.isadaptive:
1137
+ #todo update for v4
1138
+ # det_channel = self.modules_manager.get_selected_probed_data()
1139
+ # det, channel = det_channel[0].split('/')
1140
+ # if self.scanner.scan_type == 'Tabular':
1141
+ # self.curvilinear_array.append(np.array([self.curvilinear]))
1142
+ # new_positions = self.curvilinear
1143
+ # elif self.scanner.scan_type == 'Scan1D':
1144
+ # new_positions = positions[0]
1145
+ # else:
1146
+ # new_positions = positions[:]
1147
+ # learner.tell(new_positions, self.modules_manager.det_done_datas[det]['data0D'][channel]['data'])
1148
+ pass
1149
+
1150
+ # daq_scan wait time
1151
+ QThread.msleep(self.scan_settings.child('time_flow', 'wait_time').value())
1152
+
1153
+ self.h5saver.h5_file.flush()
1154
+ self.modules_manager.connect_actuators(False)
1155
+ self.modules_manager.connect_detectors(False)
1156
+
1157
+ self.status_sig.emit(["Update_Status", "Acquisition has finished", 'log'])
1158
+ self.status_sig.emit(["Scan_done"])
1159
+
1160
+ except Exception as e:
1161
+ logger.exception(str(e))
1162
+ # self.status_sig.emit(["Update_Status", getLineInfo() + str(e), 'log'])
1163
+
1164
+ def det_done(self, det_done_datas: data_mod.DataToExport, positions=[]):
1165
+ """
1166
+
1167
+ """
1168
+ try:
1169
+ indexes = self.scanner.get_indexes_from_scan_index(self.ind_scan)
1170
+ if self.Naverage > 1:
1171
+ indexes = [self.ind_average] + list(indexes)
1172
+ indexes = tuple(indexes)
1173
+ if self.ind_scan == 0:
1174
+ nav_axes = self.scanner.get_nav_axes()
1175
+ if self.Naverage > 1:
1176
+ for nav_axis in nav_axes:
1177
+ nav_axis.index += 1
1178
+ nav_axes.append(data_mod.Axis('Average', data=np.linspace(0, self.Naverage - 1, self.Naverage),
1179
+ index=0))
1180
+ self.module_and_data_saver.add_nav_axes(nav_axes)
1181
+
1182
+ self.module_and_data_saver.add_data(indexes=indexes, distribution=self.scanner.distribution)
1183
+
1184
+ #todo related to adaptive (solution lies along the Enlargeable data saver)
1185
+ if self.isadaptive:
1186
+ for ind_ax, nav_axis in enumerate(self.navigation_axes):
1187
+ nav_axis.append(np.array(positions[ind_ax]))
1188
+
1189
+ self.det_done_flag = True
1190
+
1191
+ full_names: list = self.scan_settings['plot_options', 'plot_0d']['selected'][:]
1192
+ full_names.extend(self.scan_settings['plot_options', 'plot_1d']['selected'][:])
1193
+ data_temp = det_done_datas.get_data_from_full_names(full_names, deepcopy=False)
1194
+
1195
+ self.scan_data_tmp.emit(ScanDataTemp(self.ind_scan, indexes, data_temp))
1196
+
1197
+ except Exception as e:
1198
+ logger.exception(str(e))
1199
+ # self.status_sig.emit(["Update_Status", getLineInfo() + str(e), 'log'])
1200
+
1201
+ def timeout(self):
1202
+ """
1203
+ Send the status signal *'Time out during acquisition'*.
1204
+ """
1205
+ self.timeout_scan_flag = True
1206
+ self.status_sig.emit(["Update_Status", "Timeout during acquisition", 'log'])
1207
+ self.status_sig.emit(["Timeout"])
1208
+
1209
+
1210
+
1211
+ def main_test(init_qt=True):
1212
+ from pymodaq.utils.data import DataToExport
1213
+
1214
+ LABEL = 'A Label'
1215
+ UNITS = 'units'
1216
+ OFFSET = -20.4
1217
+ SCALING = 0.22
1218
+ SIZE = 20
1219
+ DATA = OFFSET + SCALING * np.linspace(0, SIZE - 1, SIZE)
1220
+
1221
+ DATA0D = np.array([2.7])
1222
+ DATA1D = np.arange(0, 10)
1223
+ DATA2D = np.arange(0, 5 * 6).reshape((5, 6))
1224
+ DATAND = np.arange(0, 5 * 6 * 3).reshape((5, 6, 3))
1225
+
1226
+ def init_axis(data=None, index=0):
1227
+ if data is None:
1228
+ data = DATA
1229
+ return data_mod.Axis(label=LABEL, units=UNITS, data=data, index=index)
1230
+
1231
+ def init_data(data=None, Ndata=1, axes=[], name='myData') -> data_mod.DataWithAxes:
1232
+ if data is None:
1233
+ data = DATA2D
1234
+ return data_mod.DataWithAxes(name, data_mod.DataSource(0), data=[data for ind in range(Ndata)],
1235
+ axes=axes)
1236
+
1237
+ class ActuatorMock(QtCore.QObject):
1238
+ mod_name = 'act'
1239
+ move_done_signal = Signal(str, float)
1240
+ command_hardware = Signal(utils.ThreadCommand)
1241
+
1242
+ def __init__(self, ind):
1243
+ super().__init__()
1244
+ self.title = f'{self.mod_name}_{ind:02d}'
1245
+ self.units = f'unit_{ind:02d}'
1246
+ self.initialized_state = True
1247
+ self.module_and_data_saver = module_saving.ActuatorSaver(self)
1248
+ self.settings = Parameter.create(name='settings', type='str', value='mysettings')
1249
+ self.ui = None
1250
+ self.command_hardware.connect(self.move_done)
1251
+
1252
+
1253
+ def move_done(self, command: utils.ThreadCommand):
1254
+ self.move_done_signal.emit(self.title, command.attribute[0])
1255
+
1256
+ class DetectorMock(QtCore.QObject):
1257
+ mod_name = 'det'
1258
+ grab_done_signal = Signal(DataToExport)
1259
+ command_hardware = Signal(utils.ThreadCommand)
1260
+
1261
+ def __init__(self, ind):
1262
+ super().__init__()
1263
+ self.title = f'{self.mod_name}_{ind:02d}'
1264
+ self.initialized_state = True
1265
+ self.module_and_data_saver = module_saving.DetectorSaver(self)
1266
+ self.settings = Parameter.create(name='settings', type='str', value='mysettings')
1267
+ self.ui = None
1268
+ self.command_hardware.connect(self.grab_done)
1269
+
1270
+ def grab_done(self):
1271
+ dat1 = init_data(data=DATA2D, Ndata=2, name=f'{self.title}/data2D')
1272
+ dat2 = init_data(data=DATA1D, Ndata=3, name=f'{self.title}/data1D')
1273
+ data = data_mod.DataToExport(name=f'{self.title}', data=[dat1, dat2])
1274
+ self.grab_done_signal.emit(data)
1275
+
1276
+ class DashBoardTest:
1277
+ def __init__(self):
1278
+ self.title = 'DashBoardTest'
1279
+ self.detector_modules = [DetectorMock(ind) for ind in range(2)]
1280
+ self.actuators_modules = [ActuatorMock(ind) for ind in range(3)]
1281
+
1282
+ if init_qt: # used for the test suite
1283
+ app = QtWidgets.QApplication(sys.argv)
1284
+ if config['style']['darkstyle']:
1285
+ import qdarkstyle
1286
+ app.setStyleSheet(qdarkstyle.load_stylesheet())
1287
+
1288
+ win = QtWidgets.QMainWindow()
1289
+ area = gutils.dock.DockArea()
1290
+ win.setCentralWidget(area)
1291
+ #win.resize(1000, 500)
1292
+ win.setWindowTitle('PyMoDAQ Dashboard')
1293
+
1294
+ dashboard = DashBoardTest()
1295
+ daq_scan = DAQScan(dockarea=area, dashboard=dashboard)
1296
+ win.show()
1297
+
1298
+ if init_qt:
1299
+ sys.exit(app.exec_())
1300
+ return dashboard, daq_scan, win
1301
+
1302
+
1303
+ def main(init_qt=True):
1304
+ if init_qt: # used for the test suite
1305
+ app = QtWidgets.QApplication(sys.argv)
1306
+ if config['style']['darkstyle']:
1307
+ import qdarkstyle
1308
+ app.setStyleSheet(qdarkstyle.load_stylesheet())
1309
+
1310
+ from pymodaq.dashboard import DashBoard
1311
+
1312
+ win = QtWidgets.QMainWindow()
1313
+ area = gutils.dock.DockArea()
1314
+ win.setCentralWidget(area)
1315
+ win.resize(1000, 500)
1316
+ win.setWindowTitle('PyMoDAQ Dashboard')
1317
+
1318
+ dashboard = DashBoard(area)
1319
+ daq_scan = None
1320
+ file = Path(get_set_preset_path()).joinpath(f"{config('presets', 'default_preset_for_scan')}.xml")
1321
+ if file.exists():
1322
+ dashboard.set_preset_mode(file)
1323
+ daq_scan = dashboard.load_scan_module()
1324
+ else:
1325
+ msgBox = QtWidgets.QMessageBox()
1326
+ msgBox.setText(f"The default file specified in the configuration file does not exists!\n"
1327
+ f"{file}\n"
1328
+ f"Impossible to load the DAQScan Module")
1329
+ msgBox.setStandardButtons(msgBox.Ok)
1330
+ ret = msgBox.exec()
1331
+
1332
+ if init_qt:
1333
+ sys.exit(app.exec_())
1334
+ return dashboard, daq_scan, win
1335
+
1336
+
1337
+ if __name__ == '__main__':
1338
+ main()
1339
+ #main_test()