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,7 +1,1577 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- Created the 21/10/2022
3
+ Created on Wed Jan 10 16:54:14 2018
4
4
 
5
- @author: Sebastien Weber
5
+ @author: Weber Sébastien
6
6
  """
7
- from pymodaq.daq_viewer.daq_viewer_main import DAQ_Viewer
7
+
8
+ from collections import OrderedDict
9
+ import copy
10
+ import datetime
11
+ import os
12
+ from pathlib import Path
13
+ import sys
14
+ from typing import List, Tuple, Union
15
+ import time
16
+
17
+ from easydict import EasyDict as edict
18
+ import numpy as np
19
+ from qtpy import QtWidgets
20
+ from qtpy.QtCore import Qt, QObject, Slot, QThread, Signal
21
+
22
+ from pymodaq.utils.data import DataRaw, DataFromPlugins, DataToExport, Axis, DataDistribution
23
+ from pymodaq.utils.logger import set_logger, get_module_name
24
+ from pymodaq.control_modules.utils import ControlModule
25
+ from pymodaq.utils.gui_utils.file_io import select_file
26
+ from pymodaq.utils.gui_utils.utils import widget_to_png_to_bytes
27
+ import pymodaq.utils.scanner
28
+ from pymodaq.utils.tcp_server_client import TCPClient
29
+ from pymodaq.utils.gui_utils.widgets.lcd import LCD
30
+ from pymodaq.utils.config import Config, get_set_local_dir
31
+ from pymodaq.utils.h5modules.browsing import browse_data
32
+ from pymodaq.utils.h5modules.saving import H5Saver
33
+ from pymodaq.utils.h5modules import module_saving
34
+ from pymodaq.utils.daq_utils import ThreadCommand
35
+ from pymodaq.utils.parameter import ioxml
36
+ from pymodaq.utils.parameter import utils as putils
37
+ from pymodaq.control_modules.viewer_utility_classes import params as daq_viewer_params
38
+ from pymodaq.utils import daq_utils as utils
39
+ from pymodaq.utils.messenger import deprecation_msg
40
+ from pymodaq.utils.gui_utils import DockArea, get_splash_sc, Dock
41
+ from pymodaq.utils.managers.parameter_manager import ParameterManager, Parameter
42
+ from pymodaq.control_modules.daq_viewer_ui import DAQ_Viewer_UI
43
+ from pymodaq.control_modules.utils import DET_TYPES, get_viewer_plugins, DAQTypesEnum
44
+ from pymodaq.utils.plotting.data_viewers.viewer import ViewerBase, ViewersEnum
45
+ from pymodaq.utils.enums import enum_checker
46
+
47
+
48
+ logger = set_logger(get_module_name(__file__))
49
+ config = Config()
50
+
51
+ local_path = get_set_local_dir()
52
+
53
+
54
+ class DAQ_Viewer(ParameterManager, ControlModule):
55
+ """ Main PyMoDAQ class to drive detectors
56
+
57
+ Qt object and generic UI to drive actuators. The class is giving you full functionality to select (daq_detector),
58
+ initialize detectors (init_hardware), grab or snap data (grab_data) and save them (save_new, save_current). If
59
+ a DockArea is given as parent widget, the full User Interface (DAQ_Viewer_UI) is loaded allowing easy control of the
60
+ instrument.
61
+
62
+ Attributes
63
+ ----------
64
+ grab_done_signal: Signal[OrderedDict]
65
+ Signal emitted when the data from the plugin (and eventually from the data viewers) has been received. To be
66
+ used by connected objects.
67
+ custom_sig: Signal[ThreadCommand]
68
+ use this to propagate info/data coming from the hardware plugin to another object
69
+ overshoot_signal: Signal[bool]
70
+ This signal is emitted when some 0D data from the plugin is higher than the overshoot threshold set in the
71
+ settings
72
+
73
+ See Also
74
+ --------
75
+ ControlModule, DAQ_Viewer_UI, ParameterManager
76
+
77
+ Notes
78
+ -----
79
+ A particular signal from the 2D DataViewer is directly connected to the plugin: ROI_select_signal. The position and
80
+ size of the corresponding ROI is then directly transferred to a plugin function named `ROISelect` that you have to
81
+ create if one want to receive infos from the ROI
82
+ """
83
+ settings_name = 'daq_viewer_settings'
84
+ custom_sig = Signal(ThreadCommand) # particular case where DAQ_Viewer is used for a custom module
85
+
86
+ grab_done_signal = Signal(DataToExport)
87
+
88
+ _update_settings_signal = Signal(edict)
89
+ overshoot_signal = Signal(bool)
90
+ data_saved = Signal()
91
+ grab_status = Signal(bool)
92
+
93
+ params = daq_viewer_params
94
+
95
+ def __init__(self, parent=None, title="Testing",
96
+ daq_type=config('viewer', 'daq_type'),
97
+ dock_settings=None, dock_viewer=None):
98
+
99
+ self.logger = set_logger(f'{logger.name}.{title}')
100
+ self.logger.info(f'Initializing DAQ_Viewer: {title}')
101
+
102
+ QObject.__init__(self)
103
+ ParameterManager.__init__(self)
104
+ ControlModule.__init__(self)
105
+
106
+ daq_type = enum_checker(DAQTypesEnum, daq_type)
107
+ self._daq_type: DAQTypesEnum = daq_type
108
+
109
+ self._viewer_types: List[ViewersEnum] = []
110
+ self._viewers: List[ViewerBase] = []
111
+
112
+ if isinstance(parent, DockArea):
113
+ self.dockarea = parent
114
+ else:
115
+ self.dockarea = None
116
+
117
+ self.parent = parent
118
+ if parent is not None:
119
+ self.ui: DAQ_Viewer_UI = DAQ_Viewer_UI(parent, title, daq_type=daq_type,
120
+ dock_settings=dock_settings,
121
+ dock_viewer=dock_viewer)
122
+ else:
123
+ self.ui: DAQ_Viewer_UI = None
124
+
125
+ if self.ui is not None:
126
+ QtWidgets.QApplication.processEvents()
127
+ self.ui.add_setting_tree(self.settings_tree)
128
+ self.ui.command_sig.connect(self.process_ui_cmds)
129
+ self.ui.add_setting_tree(self.settings_tree)
130
+ self.viewers = self.ui.viewers
131
+ self._viewer_types = self.ui.viewer_types
132
+
133
+ self.splash_sc = get_splash_sc()
134
+
135
+ self._title = title
136
+
137
+ self.module_and_data_saver: Union[module_saving.DetectorSaver,
138
+ module_saving.DetectorEnlargeableSaver,
139
+ module_saving.DetectorExtendedSaver] = None
140
+ self.setup_saving_objects()
141
+
142
+ self._external_h5_data = None
143
+
144
+ self.settings.child('main_settings', 'DAQ_type').setValue(self.daq_type.name)
145
+ self._detectors: List[str] = [det_dict['name'] for det_dict in DET_TYPES[self.daq_type.name]]
146
+ if len(self._detectors) > 0: # will be 0 if no valid plugins are installed
147
+ self._detector: str = self._detectors[0]
148
+ self.settings.child('main_settings', 'detector_type').setValue(self._detector)
149
+
150
+ self._grabing: bool = False
151
+ self._do_bkg: bool = False
152
+ self._take_bkg: bool = False
153
+
154
+ self._grab_done: bool = False
155
+ self._start_grab_time: float = 0. # used for the refreshing rate
156
+ self._received_data: int = 0
157
+
158
+ self._lcd: LCD = None
159
+
160
+ self._bkg: List[DataFromPlugins] = None # buffer to store background
161
+
162
+ self._save_file_pathname: Path = None # to store last active path, will be an Path object
163
+
164
+ self._snapshot_pathname: Path = None
165
+ self._current_data: DataToExport = None
166
+ self._data_to_save_export: DataToExport = None
167
+
168
+ self._do_save_data: bool = False
169
+
170
+ self._set_setting_tree() # to activate parameters of default Mock detector
171
+
172
+ self.grab_done_signal.connect(self._save_export_data)
173
+
174
+ def __repr__(self):
175
+ return f'{self.__class__.__name__}: {self.title} ({self.daq_type}/{self.detector}'
176
+
177
+ def setup_saving_objects(self):
178
+ self.module_and_data_saver = module_saving.DetectorSaver(self)
179
+ self._h5saver_continuous = H5Saver(save_type='detector')
180
+ self._h5saver_continuous.show_settings(False)
181
+ self._h5saver_continuous.settings.child('do_save').sigValueChanged.connect(self._init_continuous_save)
182
+ if self.ui is not None:
183
+ self.ui.add_setting_tree(self._h5saver_continuous.settings_tree)
184
+
185
+ def process_ui_cmds(self, cmd: utils.ThreadCommand):
186
+ """Process commands sent by actions done in the ui
187
+
188
+ Parameters
189
+ ----------
190
+ cmd: ThreadCommand
191
+ Possible values are:
192
+ * init
193
+ * quit
194
+ * grab
195
+ * snap
196
+ * stop
197
+ * show_log
198
+ * detector_changed
199
+ * daq_type_changed
200
+ * save_current
201
+ * save_new
202
+ * do_bkg
203
+ * take_bkg
204
+ * viewers_changed
205
+ """
206
+
207
+ if cmd.command == 'init':
208
+ self.init_hardware(cmd.attribute[0])
209
+ elif cmd.command == 'quit':
210
+ self.quit_fun()
211
+ elif cmd.command == 'stop':
212
+ self.stop()
213
+ elif cmd.command == 'show_log':
214
+ self.show_log()
215
+ elif cmd.command == 'grab':
216
+ self.grab_data(cmd.attribute, snap_state=False)
217
+ elif cmd.command == 'snap':
218
+ self.grab_data(False, snap_state=True)
219
+ elif cmd.command == 'save_new':
220
+ self.save_new()
221
+ elif cmd.command == 'save_current':
222
+ self.save_current()
223
+ elif cmd.command == 'open':
224
+ self.load_data()
225
+ elif cmd.command == 'detector_changed':
226
+ if cmd.attribute != '':
227
+ self.detector_changed_from_ui(cmd.attribute)
228
+ elif cmd.command == 'daq_type_changed':
229
+ if cmd.attribute != '':
230
+ self.daq_type_changed_from_ui(cmd.attribute)
231
+ elif cmd.command == 'take_bkg':
232
+ self.take_bkg()
233
+ elif cmd.command == 'do_bkg':
234
+ self.do_bkg = cmd.attribute
235
+ elif cmd.command == 'viewers_changed':
236
+ self._viewer_types: List[ViewersEnum] = cmd.attribute['viewer_types']
237
+ self.viewers = cmd.attribute['viewers']
238
+
239
+ @property
240
+ def bkg(self):
241
+ return self._bkg
242
+
243
+ @property
244
+ def viewer_docks(self):
245
+ """:obj:`list` of Viewer Docks from the UI"""
246
+ if self.ui is not None:
247
+ return self.ui.viewer_docks
248
+
249
+ def daq_type_changed_from_ui(self, daq_type: DAQTypesEnum):
250
+ daq_type = enum_checker(DAQTypesEnum, daq_type)
251
+ self._daq_type = daq_type
252
+ self.settings.child('main_settings', 'DAQ_type').setValue(daq_type.name)
253
+ self.detectors = [det_dict['name'] for det_dict in DET_TYPES[daq_type.name]]
254
+ self.detector = self.detectors[0]
255
+
256
+ @property
257
+ def daq_type(self) -> DAQTypesEnum:
258
+ """:obj:`DAQTypesEnum`: Get/Set the daq_type
259
+
260
+ Update the detector property with the list of available detectors of a given daq_type
261
+ """
262
+ return self._daq_type
263
+
264
+ @daq_type.setter
265
+ def daq_type(self, daq_type: DAQTypesEnum):
266
+ daq_type = enum_checker(DAQTypesEnum, daq_type)
267
+
268
+ self._daq_type = daq_type
269
+ if self.ui is not None:
270
+ self.ui.daq_type = daq_type
271
+ self.settings.child('main_settings', 'DAQ_type').setValue(daq_type.name)
272
+ self.detectors = [det_dict['name'] for det_dict in DET_TYPES[daq_type.name]]
273
+ self.detector = self.detectors[0]
274
+
275
+ @property
276
+ def daq_types(self):
277
+ """:obj:`list` of :obj:`str`: List of available DAQ_TYPES"""
278
+ return DAQTypesEnum.names()
279
+
280
+ def detector_changed_from_ui(self, detector):
281
+ self._detector = detector
282
+ self._set_setting_tree()
283
+
284
+ @property
285
+ def detector(self):
286
+ """:obj:`str`: Get/Set the detector among detectors property"""
287
+ return self._detector
288
+
289
+ @detector.setter
290
+ def detector(self, det):
291
+ if det not in self.detectors:
292
+ raise ValueError(f'{det} is not a valid Detector: {self.detectors}')
293
+ self._detector = det
294
+ if self.ui is not None:
295
+ self.ui.detector = det
296
+ self._set_setting_tree()
297
+
298
+ def detectors_changed_from_ui(self, detectors):
299
+ self._detectors = detectors
300
+
301
+ @property
302
+ def detectors(self):
303
+ """:obj:`list` of :obj:`str`: List of available detectors of the current daq_type"""
304
+ return self._detectors
305
+
306
+ @detectors.setter
307
+ def detectors(self, detectors):
308
+ self._detectors = detectors
309
+ if self.ui is not None:
310
+ self.ui.detectors = detectors
311
+
312
+ @property
313
+ def grab_state(self):
314
+ """:obj:`bool`: Get the current grabbing status"""
315
+ return self._grabing
316
+
317
+
318
+ @property
319
+ def do_bkg(self):
320
+ """:obj:`bool`: Get/Set if background subtraction should be done"""
321
+ return self._do_bkg
322
+
323
+ @do_bkg.setter
324
+ def do_bkg(self, doit: bool):
325
+ self._do_bkg = doit
326
+
327
+ @property
328
+ def viewers(self) -> List[ViewerBase]:
329
+ """:obj:`list` of Viewers from the UI"""
330
+ if self.ui is not None:
331
+ return self._viewers
332
+
333
+ @viewers.setter
334
+ def viewers(self, viewers):
335
+ for viewer in self._viewers:
336
+ try:
337
+ viewer.data_to_export_signal.disconnect()
338
+ except:
339
+ pass
340
+ for viewer in viewers:
341
+ viewer.data_to_export_signal.connect(self._get_data_from_viewer)
342
+ if hasattr(viewer, 'ROI_select_signal'):
343
+ viewer.ROI_select_signal.connect(
344
+ lambda roi_pos_size: self.command_hardware.emit(ThreadCommand('ROISelect', roi_pos_size)))
345
+ self._viewers = viewers
346
+
347
+ @property
348
+ def viewers_docks(self):
349
+ if self.ui is not None:
350
+ return self.ui.viewer_docks
351
+
352
+ def quit_fun(self):
353
+ """Quit the application, closing the hardware and other modules
354
+ """
355
+
356
+ # insert anything that needs to be closed before leaving
357
+
358
+ if self._initialized_state: # means initialized
359
+ self.init_hardware(False)
360
+ self.quit_signal.emit()
361
+
362
+ if self._lcd is not None:
363
+ try:
364
+ self._lcd.parent.close()
365
+ except Exception as e:
366
+ self.logger.exception(str(e))
367
+
368
+ try:
369
+ if self.ui is not None:
370
+ self.ui.close()
371
+
372
+ except Exception as e:
373
+ self.logger.exception(str(e))
374
+
375
+ if __name__ == '__main__':
376
+ self.parent.close()
377
+
378
+ # #####################################
379
+ # Methods for running the acquisition
380
+
381
+ def init_hardware_ui(self, do_init=True):
382
+ """Send a command to the underlying UI to click the init button"""
383
+ if self.ui is not None:
384
+ self.ui.do_init()
385
+
386
+ def init_det(self):
387
+ deprecation_msg(f'The function *init_det* is deprecated, use init_hardware_ui')
388
+ self.init_hardware_ui(True)
389
+
390
+ def ini_det_fun(self):
391
+ deprecation_msg(f'The function *ini_det_fun* is deprecated, use init_hardware')
392
+ self.init_hardware(True)
393
+
394
+ def init_hardware(self, do_init=True):
395
+ """Init the selected detector
396
+
397
+ Parameters
398
+ ----------
399
+ do_init: bool
400
+ If True, create a DAQ_Detector instance and move it into a separated thread, connected its signals/slots
401
+ to the DAQ_Viewer object (self)
402
+ If False, force the instrument to close and kill the Thread (still not done properly in some cases)
403
+ """
404
+ if not do_init:
405
+ try:
406
+ self.command_hardware.emit(ThreadCommand(command="close"))
407
+ QtWidgets.QApplication.processEvents()
408
+ if self.ui is not None:
409
+ self.ui.detector_init = False
410
+
411
+ except Exception as e:
412
+ self.logger.exception(str(e))
413
+ else:
414
+ try:
415
+
416
+ hardware = DAQ_Detector(self._title, self.settings, self.detector)
417
+ self._hardware_thread = QThread()
418
+ if config('viewer', 'viewer_in_thread'):
419
+ hardware.moveToThread(self._hardware_thread)
420
+
421
+ self.command_hardware[ThreadCommand].connect(hardware.queue_command)
422
+ hardware.data_detector_sig[list].connect(self.show_data)
423
+ hardware.data_detector_temp_sig[list].connect(self.show_temp_data)
424
+ hardware.status_sig[ThreadCommand].connect(self.thread_status)
425
+ self._update_settings_signal[edict].connect(hardware.update_settings)
426
+
427
+ self._hardware_thread.hardware = hardware
428
+ if config('viewer', 'viewer_in_thread'):
429
+ self._hardware_thread.start()
430
+ self.command_hardware.emit(ThreadCommand("ini_detector", attribute=[
431
+ self.settings.child('detector_settings').saveState(), self.controller]))
432
+ if self.ui is not None:
433
+ for dock in self.ui.viewer_docks:
434
+ dock.setEnabled(True)
435
+
436
+ except Exception as e:
437
+ self.logger.exception(str(e))
438
+
439
+ def snap(self):
440
+ """Programmatic click on the UI snap button"""
441
+ self.grab_data(False, snap_state=True)
442
+
443
+ def grab(self):
444
+ """Programmatic click on the UI grab button"""
445
+ if self.ui is not None:
446
+ self.manage_ui_actions('grab', 'setChecked', not self._grabing)
447
+ self.grab_data(not self._grabing, snap_state=False)
448
+
449
+ def snapshot(self, pathname=None, dosave=False, send_to_tcpip=False):
450
+ """Do one single grab (snap) and eventually save the data.
451
+
452
+ Parameters
453
+ ----------
454
+ pathname: str or Path object
455
+ The path where to save data
456
+ dosave: bool
457
+ Do save or just grab data
458
+ send_to_tcpip: bool
459
+ If True, send the grabed data through the TCP/IP pipe
460
+ """
461
+ try:
462
+ self._do_save_data = dosave
463
+ if pathname is None:
464
+ raise (ValueError("filepathanme has not been defined in snapshot"))
465
+
466
+ self._save_file_pathname = pathname
467
+ self.grab_data(grab_state=False, send_to_tcpip=send_to_tcpip, snap_state=True)
468
+ except Exception as e:
469
+ self.logger.exception(str(e))
470
+
471
+ def grab_data(self, grab_state=False, send_to_tcpip=False, snap_state=False):
472
+ """Generic method to grab or snap data from the selected (and initialized) detector
473
+
474
+ Parameters
475
+ ----------
476
+ grab_state: bool
477
+ Defines the grab status: if True: do live grabing if False stops the grab
478
+ send_to_tcpip: bool
479
+ If True, send the grabed data through the TCP/IP pipe
480
+ snap_state: bool
481
+ if True performs a single grab
482
+ """
483
+ self._grabing = grab_state
484
+ self._send_to_tcpip = send_to_tcpip
485
+ self._grab_done = False
486
+
487
+ if self.ui is not None:
488
+ self.ui.data_ready = False
489
+
490
+ self._start_grab_time = time.perf_counter()
491
+ if snap_state:
492
+ self.update_status(f'{self._title}: Snap')
493
+ self.command_hardware.emit(
494
+ ThreadCommand("single", [self.settings.child('main_settings', 'Naverage').value()]))
495
+ else:
496
+ if not grab_state:
497
+ self.update_status(f'{self._title}: Stop Grab')
498
+ self.command_hardware.emit(ThreadCommand("stop_grab", ))
499
+ else:
500
+ self.thread_status(ThreadCommand("update_channels", ))
501
+ self.update_status(f'{self._title}: Continuous Grab')
502
+ self.command_hardware.emit(
503
+ ThreadCommand("grab", [self.settings.child('main_settings', 'Naverage').value()]))
504
+
505
+ def take_bkg(self):
506
+ """Do a snap and store data to be used as background into an attribute: `self._bkg`
507
+
508
+ The content of the bkg will be saved if data is further saved with do_bkg property set to True
509
+ """
510
+ self._take_bkg = True
511
+ self.grab_data(snap_state=True)
512
+
513
+ def stop_grab(self):
514
+ if self.ui is not None:
515
+ self.manage_ui_actions('grab', 'setChecked', False)
516
+ self.stop()
517
+
518
+ def stop(self):
519
+ self.update_status(f'{self._title}: Stop Grab')
520
+ self.command_hardware.emit(ThreadCommand("stop_all", ))
521
+ self._grabing = False
522
+
523
+ @Slot()
524
+ def _raise_timeout(self):
525
+ """ Print the "timeout occurred" error message in the status bar via the update_status method.
526
+ """
527
+ self.update_status("Timeout occured", log_type="log")
528
+
529
+ @staticmethod
530
+ def load_data():
531
+ """Opens a H5 file in the H5Browser module
532
+
533
+ Convenience static method.
534
+ """
535
+ browse_data()
536
+
537
+
538
+ def save_current(self):
539
+ """Save current data into a h5file"""
540
+ self._do_save_data = True
541
+ self._save_file_pathname = select_file(start_path=self._save_file_pathname, save=True,
542
+ ext='h5') # see daq_utils
543
+ self._save_export_data(self._data_to_save_export)
544
+
545
+ def save_new(self):
546
+ """Snap data and save them into a h5file"""
547
+ self._do_save_data = True
548
+ self._save_file_pathname = select_file(start_path=self._save_file_pathname, save=True,
549
+ ext='h5') # see daq_utils
550
+ self.snapshot(pathname=self._save_file_pathname, dosave=True)
551
+
552
+ def _init_continuous_save(self):
553
+ if self._h5saver_continuous.settings.child('do_save').value():
554
+
555
+ self._h5saver_continuous.settings.child('base_name').setValue('Data')
556
+ self._h5saver_continuous.settings.child('N_saved').show()
557
+ self._h5saver_continuous.settings.child('N_saved').setValue(0)
558
+ self.module_and_data_saver.h5saver = self._h5saver_continuous
559
+ self._h5saver_continuous.init_file(update_h5=True)
560
+
561
+ self.module_and_data_saver = module_saving.DetectorEnlargeableSaver(self)
562
+ self.module_and_data_saver.h5saver = self._h5saver_continuous
563
+ self.module_and_data_saver.get_set_node()
564
+
565
+ self.grab_done_signal.connect(self.append_data)
566
+ else:
567
+ self._do_continuous_save = False
568
+ self._h5saver_continuous.settings.child('N_saved').hide()
569
+ self.grab_done_signal.disconnect(self.append_data)
570
+
571
+ try:
572
+ self._h5saver_continuous.close()
573
+ except Exception as e:
574
+ self.logger.exception(str(e))
575
+
576
+ def append_data(self, where=None):
577
+ """Appends DataToExport to a DetectorEnlargeableSaver
578
+
579
+ Parameters
580
+ ----------
581
+ data: DataToExport
582
+ The data to be added to an enlargeable h5 array
583
+
584
+ See Also
585
+ --------
586
+ DetectorEnlargeableSaver
587
+ """
588
+ self._add_data_to_saver(self._data_to_save_export, init_step=self._h5saver_continuous.settings['N_saved'] == 0,
589
+ where=None)
590
+ self._h5saver_continuous.settings.child('N_saved').setValue(self._h5saver_continuous.settings['N_saved'] + 1)
591
+
592
+ def insert_data(self, indexes: Tuple[int], where=None, distribution=DataDistribution['uniform']):
593
+ """Insert DataToExport to a DetectorExtendedSaver at specified indexes
594
+
595
+ Parameters
596
+ ----------
597
+ indexes: tuple(int)
598
+ The indexes within the extended array where to place these data
599
+ where: Node or str
600
+ distribution: DataDistribution enum
601
+ save_raw_only: bool
602
+ If True save only Raw data (no data processed from Roi)
603
+
604
+ See Also
605
+ --------
606
+ DAQ_Scan, DetectorExtendedSaver
607
+ """
608
+ self._add_data_to_saver(self._data_to_save_export, init_step=np.all(np.array(indexes) == 0), where=where,
609
+ indexes=indexes, distribution=distribution)
610
+
611
+ def _add_data_to_saver(self, data: DataToExport, init_step=False, where=None, **kwargs):
612
+ """Adds DataToExport data to the current node using the declared module_and_data_saver
613
+
614
+ Filters the data to be saved by DataSource as specified in the current H5Saver (see self.module_and_data_saver)
615
+
616
+ Parameters
617
+ ----------
618
+ data: DataToExport
619
+ The data to be saved
620
+ init_step: bool
621
+ If True, means this is the first step of saving (if multisaving), then save background if any and a png image
622
+ kwargs: dict
623
+ Other named parameters to be passed as is to the module_and_data_saver
624
+
625
+ See Also
626
+ --------
627
+ DetectorSaver, DetectorEnlargeableSaver, DetectorExtendedSaver
628
+
629
+ """
630
+ detector_node = self.module_and_data_saver.get_set_node(where)
631
+ data = data if not self.module_and_data_saver.h5saver.settings['save_raw_only'] else\
632
+ self._data_to_save_export.get_data_from_source('raw')
633
+
634
+ self.module_and_data_saver.add_data(detector_node, data, **kwargs)
635
+
636
+ if init_step:
637
+ if self._do_bkg and self._bkg is not None:
638
+ self.module_and_data_saver.add_bkg(detector_node, self._bkg)
639
+
640
+ if self._external_h5_data is not None:
641
+ # todo test this functionnality
642
+ self.module_and_data_saver.add_external_h5(self._external_h5_data)
643
+
644
+ def _save_data(self, path=None, data: DataToExport = None):
645
+ """Private. Practical implementation to save data into a h5file altogether with metadata, axes, background...
646
+
647
+ Parameters
648
+ ----------
649
+ path: Path
650
+ where to save the data as returned from browse_file for instance
651
+ data: DataToExport
652
+
653
+ See Also
654
+ --------
655
+ browse_file, _get_data_from_viewers
656
+ """
657
+ if path is not None:
658
+ path = Path(path)
659
+ h5saver = H5Saver(save_type='detector')
660
+ h5saver.init_file(update_h5=True, custom_naming=False, addhoc_file_path=path)
661
+ self.module_and_data_saver = module_saving.DetectorSaver(self)
662
+ self.module_and_data_saver.h5saver = h5saver
663
+
664
+ self._add_data_to_saver(data)
665
+
666
+ if self.ui is not None:
667
+ (root, filename) = os.path.split(str(path))
668
+ filename, ext = os.path.splitext(filename)
669
+ image_path = os.path.join(root, filename + '.png')
670
+ self.dockarea.parent().grab().save(image_path)
671
+
672
+ h5saver.close_file()
673
+ self.data_saved.emit()
674
+
675
+ @Slot(OrderedDict)
676
+ def _save_export_data(self, data: DataToExport):
677
+ """Auxiliary method (Slot) to receive all data (raw and processed from rois) and save them
678
+
679
+ Parameters
680
+ ----------
681
+ data: DataToExport
682
+ contains a timestamp and data (raw and extracted from roi in dataviewers) on the dorm:
683
+ `_data_to_save_export = OrderedDict(Ndatas=Ndatas, acq_time_s=acq_time, name=name,
684
+ control_module='DAQ_Viewer')`
685
+ with extra keys for data dimensionality such as Data0D=OrderedDict(...)
686
+
687
+ See Also
688
+ --------
689
+ _save_data
690
+ """
691
+
692
+ if self._do_save_data:
693
+ self._save_data(self._save_file_pathname, data)
694
+ self._do_save_data = False
695
+
696
+ def _get_data_from_viewer(self, data: DataToExport):
697
+ """Get all data emitted by the current viewers
698
+
699
+ Each viewer *data_to_export_signal* is connected to this slot. The collected data is stored in another
700
+ DataToExport `self._data_to_save_export` for further processing. All raw data are also stored in this attribute.
701
+ When all viewers have emitted this signal, the collected data are emitted with the
702
+ `grab_done_signal` signal.
703
+
704
+ Parameters
705
+ ---------_
706
+ data: DataToExport
707
+ All data collected from the viewers
708
+
709
+ """
710
+ if self._data_to_save_export is not None: # means that somehow data are not initialized so no further procsessing
711
+ self._received_data += 1
712
+ if len(data) != 0:
713
+ for dat in data:
714
+ dat.origin = self.title
715
+ self._data_to_save_export.append(data)
716
+
717
+ if self._received_data == len(self.viewers):
718
+ self._grab_done = True
719
+ self.grab_done_signal.emit(self._data_to_save_export)
720
+
721
+ @property
722
+ def current_data(self) -> DataToExport:
723
+ return self._data_to_save_export
724
+
725
+ @Slot(list)
726
+ def show_temp_data(self, data: List[DataFromPlugins]):
727
+ """Send data to their dedicated viewers but those will not emit processed data signal
728
+
729
+ Slot receiving data from plugins emitted with the `data_grabed_signal_temp`
730
+
731
+ Parameters
732
+ ----------
733
+ data: list of DataFromPlugins
734
+ """
735
+ self._init_show_data(data)
736
+ if self.ui is not None:
737
+ self.set_data_to_viewers(data, temp=True)
738
+
739
+ @Slot(list)
740
+ def show_data(self, data: List[DataFromPlugins]):
741
+ """Send data to their dedicated viewers but those will not emit processed data signal
742
+
743
+ Slot receiving data from plugins emitted with the `data_grabed_signal`
744
+ Process the data as specified in the settings, display them into the dedicated data viewers depending on the
745
+ settings:
746
+ * create a container (OrderedDict `_data_to_save_export`) with info from this DAQ_Viewer (title), a timestamp...
747
+ * call `_process_data`
748
+ * do background subtraction if any
749
+ * check refresh time (if set in the settings) to send or not data to data viewers
750
+ * either send to the data viewers (if refresh time is ok and/or show data option in settings is set)
751
+ * either
752
+ * send grab_done_signal (to the slot _save_export_data ) to save the data
753
+
754
+ Parameters
755
+ ----------
756
+ data: list of DataFromPlugins
757
+
758
+ See Also
759
+ --------
760
+ _init_show_data, _process_data
761
+ """
762
+ try:
763
+ if self.settings.child('main_settings', 'tcpip', 'tcp_connected').value() and self._send_to_tcpip:
764
+ self._command_tcpip.emit(ThreadCommand('data_ready', data))
765
+ if self.ui is not None:
766
+ self.ui.data_ready = True
767
+ self._init_show_data(data)
768
+
769
+ # store raw data for further processing
770
+ self._data_to_save_export = DataToExport(self._title, control_module='DAQ_Viewer', data=data)
771
+
772
+ if self.settings['main_settings', 'live_averaging']:
773
+ self.settings.child('main_settings', 'N_live_averaging').setValue(self._ind_continuous_grab)
774
+ self._current_data = copy.deepcopy(self._data_to_save_export)
775
+
776
+ self._ind_continuous_grab += 1
777
+ if self._ind_continuous_grab > 1:
778
+ self._data_to_save_export = \
779
+ self._data_to_save_export.average(self._current_data, self._ind_continuous_grab)
780
+
781
+ if self._take_bkg:
782
+ self._bkg = copy.deepcopy(self._data_to_save_export)
783
+ self._take_bkg = False
784
+
785
+ if self._grabing: # if live
786
+ refresh_time = self.settings['main_settings', 'refresh_time']
787
+ refresh = time.perf_counter() - self._start_grab_time > refresh_time / 1000
788
+ if refresh:
789
+ self._start_grab_time = time.perf_counter()
790
+ else:
791
+ refresh = True # if single
792
+ if self.ui is not None and self.settings.child('main_settings', 'show_data').value() and refresh:
793
+ self._received_data = 0 # so that data send back from viewers can be properly counted
794
+ data_to_plot = copy.deepcopy(self._data_to_save_export)
795
+ # process bkg if needed
796
+ if self.do_bkg and self._bkg is not None:
797
+ data_to_plot -= self._bkg
798
+ self.set_data_to_viewers(data_to_plot.data)
799
+ else:
800
+ self._grab_done = True
801
+ self.grab_done_signal.emit(self._data_to_save_export)
802
+
803
+ except Exception as e:
804
+ self.logger.exception(str(e))
805
+
806
+ def _init_show_data(self, data):
807
+ """Processing before showing data
808
+
809
+ * process the data to check if they overshoot
810
+ * check the data dimensionality to update the dedicated viewers
811
+
812
+ Parameters
813
+ ----------
814
+ data: list of DataFromPlugins
815
+
816
+ See Also
817
+ --------
818
+ _process_overshoot
819
+ """
820
+ self._process_overshoot(data)
821
+ self._viewer_types = [ViewersEnum(data.dim.name) for data in data]
822
+ if self.ui is not None:
823
+ if self.ui.viewer_types != self._viewer_types:
824
+ self.ui.update_viewers(self._viewer_types)
825
+
826
+ def set_data_to_viewers(self, data, temp=False):
827
+ """Process data dimensionality and send appropriate data to their data viewers
828
+
829
+ Parameters
830
+ ----------
831
+ data: list of DataFromPlugins
832
+ temp: bool
833
+ if True notify the data viewers to display data as temporary (meaning not exporting processed data from roi)
834
+
835
+ See Also
836
+ --------
837
+ ViewerBase, Viewer0D, Viewer1D, Viewer2D
838
+ """
839
+ for ind, data in enumerate(data):
840
+ self.viewers[ind].title = data.name
841
+ self.viewer_docks[ind].setTitle(self._title + ' ' + data.name)
842
+
843
+ if temp:
844
+ self.viewers[ind].show_data_temp(data)
845
+ else:
846
+ self.viewers[ind].show_data(data)
847
+
848
+ def value_changed(self, param):
849
+ """ParameterManager subclassed method. Process events from value changed by user in the UI Settings
850
+
851
+ Parameters
852
+ ----------
853
+ param: Parameter
854
+ a given parameter whose value has been changed by user
855
+ """
856
+ path = self.settings.childPath(param)
857
+ if param.name() == 'DAQ_type':
858
+ self._h5saver_continuous.settings.child('do_save').setValue(False)
859
+ self.settings.child('main_settings', 'axes').show(param.value() == 'DAQ2D')
860
+
861
+ elif param.name() == 'show_averaging':
862
+ self.settings.child('main_settings', 'live_averaging').setValue(False)
863
+ self._update_settings_signal.emit(edict(path=path, param=param, change='value'))
864
+
865
+ elif param.name() == 'live_averaging':
866
+ self.settings.child('main_settings', 'show_averaging').setValue(False)
867
+ if param.value():
868
+ self.settings.child('main_settings', 'N_live_averaging').show()
869
+ self._ind_continuous_grab = 0
870
+ self.settings.child('main_settings', 'N_live_averaging').setValue(0)
871
+ else:
872
+ self.settings.child('main_settings', 'N_live_averaging').hide()
873
+
874
+ elif param.name() in putils.iter_children(self.settings.child('main_settings', 'axes'), []):
875
+ if self.daq_type.name == "DAQ2D":
876
+ if param.name() == 'use_calib':
877
+ if param.value() != 'None':
878
+ params = ioxml.XML_file_to_parameter(
879
+ os.path.join(local_path, 'camera_calibrations', param.value() + '.xml'))
880
+ param_obj = Parameter.create(name='calib', type='group', children=params)
881
+ self.settings.child('main_settings', 'axes').restoreState(
882
+ param_obj.child('axes').saveState(), addChildren=False, removeChildren=False)
883
+ self.settings.child('main_settings', 'axes').show()
884
+ else:
885
+ for viewer in self.viewers:
886
+ viewer.x_axis, viewer.y_axis = self.get_scaling_options()
887
+
888
+ elif param.name() == 'continuous_saving_opt':
889
+ self._h5saver_continuous.show_settings(param.value())
890
+
891
+ elif param.name() == 'wait_time':
892
+ self.command_hardware.emit(ThreadCommand('update_wait_time', [param.value()]))
893
+
894
+ elif param.name() == 'connect_server':
895
+ if param.value():
896
+ self.connect_tcp_ip()
897
+ else:
898
+ self._command_tcpip.emit(ThreadCommand('quit', ))
899
+
900
+ elif param.name() == 'ip_address' or param.name == 'port':
901
+ self._command_tcpip.emit(
902
+ ThreadCommand('update_connection', dict(ipaddress=self.settings.child('main_settings', 'tcpip',
903
+ 'ip_address').value(),
904
+ port=self.settings.child('main_settings', 'tcpip',
905
+ 'port').value())))
906
+
907
+ if path is not None:
908
+ if 'main_settings' not in path:
909
+ self._update_settings_signal.emit(edict(path=path, param=param, change='value'))
910
+
911
+ if self.settings.child('main_settings', 'tcpip', 'tcp_connected').value():
912
+ self._command_tcpip.emit(ThreadCommand('send_info', dict(path=path, param=param)))
913
+
914
+ def child_added(self, param, data):
915
+ """Non-mandatory method to be subclassed for actions to perform when a param has been added in self.settings
916
+
917
+ Parameters
918
+ ----------
919
+ param: Parameter
920
+ the parameter where child will be added
921
+ data: Parameter
922
+ the child parameter
923
+ """
924
+ if param.name() not in putils.iter_children(self.settings.child('main_settings'), []):
925
+ self._update_settings_signal.emit(edict(path=putils.get_param_path(param)[1:], param=data[0], change='childAdded'))
926
+
927
+ def param_deleted(self, param):
928
+ """ParameterManager subclassed method. Process events from parameter deleted by user in the UI Settings
929
+
930
+ Parameters
931
+ ----------
932
+ param: Parameter
933
+ a given parameter whose value has been changed by user
934
+ """
935
+ if param.name() not in putils.iter_children(self.settings.child('main_settings'), []):
936
+ self._update_settings_signal.emit(edict(path=['detector_settings'], param=param, change='parent'))
937
+
938
+ def _set_setting_tree(self):
939
+ """Apply the specific settings of the selected detector (plugin)
940
+
941
+ Remove previous ones and load on the fly the new ones
942
+
943
+ See Also
944
+ --------
945
+ pymodaq.control_modules.utils:get_viewer_plugins
946
+ """
947
+
948
+ try:
949
+ if len(self.settings.child('detector_settings').children()) > 0:
950
+ for child in self.settings.child('detector_settings').children():
951
+ child.remove()
952
+
953
+ det_params, _class = get_viewer_plugins(self.daq_type.name, self.detector)
954
+ self.settings.child('detector_settings').addChildren(det_params.children())
955
+ except Exception as e:
956
+ self.logger.exception(str(e))
957
+
958
+ def _process_overshoot(self, data):
959
+ """Compare data value (0D) to the given overshoot setting
960
+ """
961
+ if self.settings.child('main_settings', 'overshoot', 'stop_overshoot').value():
962
+ for channels in data:
963
+ for channel in channels['data']:
964
+ if any(channel >= self.settings.child('main_settings', 'overshoot', 'overshoot_value').value()):
965
+ self.overshoot_signal.emit(True)
966
+
967
+ def get_scaling_options(self):
968
+ """Create axes scaling options depending on the ('main_settings', 'axes') settings
969
+
970
+ Returns
971
+ -------
972
+ pymodaq.utils.data.ScalingOptions
973
+ """
974
+ scaled_xaxis = Axis(label=self.settings['main_settings', 'axes', 'xaxis', 'xlabel'],
975
+ units=self.settings['main_settings', 'axes', 'xaxis', 'xunits'],
976
+ offset=self.settings['main_settings', 'axes', 'xaxis', 'xoffset'],
977
+ scaling=self.settings['main_settings', 'axes', 'xaxis', 'xscaling'])
978
+ scaled_yaxis = Axis(label=self.settings['main_settings', 'axes', 'yaxis', 'ylabel'],
979
+ units=self.settings['main_settings', 'axes', 'yaxis', 'yunits'],
980
+ offset=self.settings['main_settings', 'axes', 'yaxis', 'yoffset'],
981
+ scaling=self.settings['main_settings', 'axes', 'yaxis', 'yscaling'])
982
+ return scaled_xaxis, scaled_yaxis
983
+
984
+ @Slot(ThreadCommand)
985
+ def thread_status(self, status):
986
+ """Get back info (using the ThreadCommand object) from the hardware
987
+
988
+ And re-emit this ThreadCommand using the custom_sig signal if it should be used in a higher level module
989
+
990
+ Parameters
991
+ ----------
992
+ status: ThreadCommand
993
+ The info returned from the hardware, the command (str) can be either:
994
+ * Update_Status: display messages and log info
995
+ * ini_detector: update the status with "detector initialized" value and init state if attribute not null.
996
+ * close: close the current thread and delete corresponding attribute on cascade.
997
+ * grab : emit grab_status(True)
998
+ * grab_stopped: emit grab_status(False)
999
+ * x_axis: update x_axis from status attribute and User Interface viewer consequently. (Deprecated)
1000
+ * y_axis: update y_axis from status attribute and User Interface viewer consequently. (Deprecated)
1001
+ * update_channel: update the viewer channels in case of 0D DAQ_type (deprecated)
1002
+ * update_settings: Update the "detector setting" node in the settings tree.
1003
+ * update_main_settings: update the "main setting" node in the settings tree
1004
+ * raise_timeout:
1005
+ * show_splash: Display the splash screen with attribute as message
1006
+ * close_splash
1007
+ * init_lcd: display a LCD panel
1008
+ * lcd: display on the LCD panel, the content of the attribute
1009
+ * stop: stop the grab
1010
+ """
1011
+ if status.command == "Update_Status":
1012
+ if len(status.attribute) > 1:
1013
+ self.update_status(status.attribute[0], log=status.attribute[1])
1014
+ else:
1015
+ self.update_status(status.attribute[0])
1016
+
1017
+ elif status.command == "ini_detector":
1018
+ self.update_status("detector initialized: " + str(status.attribute[0]['initialized']))
1019
+ if self.ui is not None:
1020
+ self.ui.detector_init = status.attribute[0]['initialized']
1021
+ if status.attribute[0]['initialized']:
1022
+ self.controller = status.attribute[0]['controller']
1023
+ self._initialized_state = True
1024
+ else:
1025
+ self._initialized_state = False
1026
+
1027
+ self.init_signal.emit(self._initialized_state)
1028
+
1029
+ elif status.command == "close":
1030
+ try:
1031
+ self.update_status(status.attribute[0])
1032
+ self._hardware_thread.quit()
1033
+ self._hardware_thread.wait()
1034
+ finished = self._hardware_thread.isFinished()
1035
+ if finished:
1036
+ pass
1037
+ else:
1038
+ print('Thread still running')
1039
+ self._hardware_thread.terminate()
1040
+ self.update_status('thread is locked?!', 'log')
1041
+ except Exception as e:
1042
+ self.logger.exception(str(e))
1043
+
1044
+ self._initialized_state = False
1045
+ self.init_signal.emit(self._initialized_state)
1046
+
1047
+ elif status.command == "grab":
1048
+ self.grab_status.emit(True)
1049
+
1050
+ elif status.command == 'grab_stopped':
1051
+ self.grab_status.emit(False)
1052
+
1053
+ elif status.command == "x_axis":
1054
+ deprecation_msg(f'using emit_x_axis in plugins is deprecated use data emission with correct axis to set it.'
1055
+ f'To send it only once, use the data_grabed_signal_temp')
1056
+
1057
+ elif status.command == "y_axis":
1058
+ deprecation_msg(f'using emit_y_axis in plugins is deprecated use data emission with correct axis to set it.'
1059
+ f'To send it only once, use the data_grabed_signal_temp')
1060
+
1061
+ elif status.command == "update_channels":
1062
+ pass
1063
+
1064
+ elif status.command == 'update_main_settings':
1065
+ # this is a way for the plugins to update main settings of the ui (solely values, limits and options)
1066
+ try:
1067
+ if status.attribute[2] == 'value':
1068
+ self.settings.child('main_settings', *status.attribute[0]).setValue(status.attribute[1])
1069
+ elif status.attribute[2] == 'limits':
1070
+ self.settings.child('main_settings', *status.attribute[0]).setLimits(status.attribute[1])
1071
+ elif status.attribute[2] == 'options':
1072
+ self.settings.child('main_settings', *status.attribute[0]).setOpts(**status.attribute[1])
1073
+ except Exception as e:
1074
+ self.logger.exception(str(e))
1075
+
1076
+ elif status.command == 'update_settings':
1077
+ # using this the settings shown in the UI for the plugin reflects the real plugin settings
1078
+ try:
1079
+ self.settings.sigTreeStateChanged.disconnect(
1080
+ self.parameter_tree_changed) # any changes on the detcetor settings will update accordingly the gui
1081
+ except Exception as e:
1082
+ self.logger.exception(str(e))
1083
+ try:
1084
+ if status.attribute[2] == 'value':
1085
+ self.settings.child('detector_settings', *status.attribute[0]).setValue(status.attribute[1])
1086
+ elif status.attribute[2] == 'limits':
1087
+ self.settings.child('detector_settings', *status.attribute[0]).setLimits(status.attribute[1])
1088
+ elif status.attribute[2] == 'options':
1089
+ self.settings.child('detector_settings', *status.attribute[0]).setOpts(**status.attribute[1])
1090
+ elif status.attribute[2] == 'childAdded':
1091
+ child = Parameter.create(name='tmp')
1092
+ child.restoreState(status.attribute[1][0])
1093
+ self.settings.child('detector_settings', *status.attribute[0]).addChild(status.attribute[1][0])
1094
+
1095
+ except Exception as e:
1096
+ self.logger.exception(str(e))
1097
+ self.settings.sigTreeStateChanged.connect(self.parameter_tree_changed)
1098
+
1099
+ elif status.command == 'raise_timeout':
1100
+ self._raise_timeout()
1101
+
1102
+ elif status.command == 'show_splash':
1103
+ self.ui.settings_tree.setEnabled(False)
1104
+ self.splash_sc.show()
1105
+ self.splash_sc.raise_()
1106
+ self.splash_sc.showMessage(status.attribute[0], color=Qt.white)
1107
+
1108
+ elif status.command == 'close_splash':
1109
+ self.splash_sc.close()
1110
+ self.ui.settings_tree.setEnabled(True)
1111
+
1112
+ elif status.command == 'init_lcd':
1113
+ if self._lcd is not None:
1114
+ try:
1115
+ self._lcd.parent.close()
1116
+ except Exception as e:
1117
+ self.logger.exception(str(e))
1118
+ # lcd module
1119
+ lcd = QtWidgets.QWidget()
1120
+ self._lcd = LCD(lcd, **status.attribute[0])
1121
+ lcd.setVisible(True)
1122
+ QtWidgets.QApplication.processEvents()
1123
+
1124
+ elif status.command == 'lcd':
1125
+ self._lcd.setvalues(status.attribute[0])
1126
+
1127
+ elif status.command == 'stop':
1128
+ self.stop()
1129
+
1130
+ self.custom_sig.emit(status) # to be used if needed in custom application connected to this module
1131
+
1132
+ def connect_tcp_ip(self):
1133
+ """Init a TCPClient in a separated thread to communicate with a distant TCp/IP Server
1134
+
1135
+ Use the settings: ip_adress and port to specify the connection
1136
+
1137
+ See Also
1138
+ --------
1139
+ TCPServer
1140
+ """
1141
+ if self.settings.child('main_settings', 'tcpip', 'connect_server').value():
1142
+ self._tcpclient_thread = QThread()
1143
+
1144
+ tcpclient = TCPClient(self.settings.child('main_settings', 'tcpip', 'ip_address').value(),
1145
+ self.settings.child('main_settings', 'tcpip', 'port').value(),
1146
+ self.settings.child('detector_settings'))
1147
+ tcpclient.moveToThread(self._tcpclient_thread)
1148
+ self._tcpclient_thread.tcpclient = tcpclient
1149
+ tcpclient.cmd_signal.connect(self.process_tcpip_cmds)
1150
+
1151
+ self._command_tcpip[ThreadCommand].connect(tcpclient.queue_command)
1152
+
1153
+ self._tcpclient_thread.start()
1154
+ tcpclient.init_connection(extra_commands=[ThreadCommand('get_axis', )])
1155
+
1156
+ @Slot(ThreadCommand)
1157
+ def process_tcpip_cmds(self, status):
1158
+ """Receive commands from the TCP Server (if connected) and process them
1159
+
1160
+ Parameters
1161
+ ----------
1162
+ status: ThreadCommand
1163
+ Possible commands are:
1164
+ * 'Send Data: to trigger a snapshot
1165
+ * 'connected': show that connection is ok
1166
+ * 'disconnected': show that connection is not OK
1167
+ * 'Update_Status': update a status command
1168
+ * 'set_info': receive settings from the server side and update them on this side
1169
+ * 'get_axis': request the plugin to send its axis info
1170
+
1171
+
1172
+ See Also
1173
+ --------
1174
+ connect_tcp_ip, TCPServer
1175
+
1176
+ """
1177
+ if 'Send Data' in status.command:
1178
+ self.snapshot('', send_to_tcpip=True)
1179
+ elif status.command == 'connected':
1180
+ self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(True)
1181
+
1182
+ elif status.command == 'disconnected':
1183
+ self.settings.child('main_settings', 'tcpip', 'tcp_connected').setValue(False)
1184
+
1185
+ elif status.command == 'Update_Status':
1186
+ self.thread_status(status)
1187
+
1188
+ elif status.command == 'set_info':
1189
+ param_dict = ioxml.XML_string_to_parameter(status.attribute[1])[0]
1190
+ param_tmp = Parameter.create(**param_dict)
1191
+ param = self.settings.child('detector_settings', *status.attribute[0][1:])
1192
+
1193
+ param.restoreState(param_tmp.saveState())
1194
+
1195
+ elif status.command == 'get_axis':
1196
+ self.command_hardware.emit(
1197
+ ThreadCommand('get_axis', )) # tells the plugin to emit its axes so that the server will receive them
1198
+
1199
+
1200
+ class DAQ_Detector(QObject):
1201
+ """
1202
+ ========================= ==========================
1203
+ **Attributes** **Type**
1204
+ *status_sig* instance of pyqt Signal
1205
+ *data_detector_sig* instance of pyqt Signal
1206
+ *data_detector_temp_sig* instance of pyqt Signal
1207
+
1208
+ *waiting_for_data* boolean
1209
+ *controller* ???
1210
+ *detector_name* string
1211
+ *detector* ???
1212
+ *controller_adress* ???
1213
+ *grab_state* boolean
1214
+ *single_grab* boolean
1215
+ *x_axis* 1D numpy array
1216
+ *y_axis* 1D numpy array
1217
+ *datas* dictionnary
1218
+ *ind_average* int
1219
+ *Naverage* int
1220
+ *average_done* boolean
1221
+ *hardware_averaging* boolean
1222
+ *show_averaging* boolean
1223
+ *wait_time* int
1224
+ *daq_type* string
1225
+ ========================= ==========================
1226
+ """
1227
+ status_sig = Signal(ThreadCommand)
1228
+ data_detector_sig = Signal(list)
1229
+ data_detector_temp_sig = Signal(list)
1230
+
1231
+ def __init__(self, title, settings_parameter, detector_name):
1232
+ super().__init__()
1233
+ self.waiting_for_data = False
1234
+ self.controller = None
1235
+ self.logger = set_logger(f'{logger.name}.{title}.detector')
1236
+ self.detector_name = detector_name
1237
+ self.detector = None
1238
+ self.controller_adress = None
1239
+ self.grab_state = False
1240
+ self.single_grab = False
1241
+ self.datas = None
1242
+ self.ind_average = 0
1243
+ self.Naverage = None
1244
+ self.average_done = False
1245
+ self.hardware_averaging = False
1246
+ self.show_averaging = False
1247
+ self.wait_time = settings_parameter['main_settings', 'wait_time']
1248
+ self.daq_type = DAQTypesEnum[settings_parameter['main_settings', 'DAQ_type']]
1249
+
1250
+ @Slot(edict)
1251
+ def update_settings(self, settings_parameter_dict):
1252
+ """
1253
+ | Set attribute values in case of "main_settings" path with corresponding parameter values.
1254
+ | Recursively call the method on detector class attribute else.
1255
+
1256
+ ======================== ============== ======================================
1257
+ **Parameters** **Type** **Description**
1258
+ settings_parameter_dict dictionnary the (pathname,parameter) dictionnary
1259
+ ======================== ============== ======================================
1260
+
1261
+ See Also
1262
+ --------
1263
+ update_settings
1264
+ """
1265
+
1266
+ path = settings_parameter_dict['path']
1267
+ param = settings_parameter_dict['param']
1268
+ if path[0] == 'main_settings':
1269
+ if hasattr(self, path[-1]):
1270
+ setattr(self, path[-1], param.value())
1271
+
1272
+ elif path[0] == 'detector_settings':
1273
+ self.detector.update_settings(settings_parameter_dict)
1274
+
1275
+ @Slot(ThreadCommand)
1276
+ def queue_command(self, command: ThreadCommand):
1277
+ """Transfer command from the main module to the hardware module
1278
+
1279
+ Parameters
1280
+ ----------
1281
+ command: ThreadCommand
1282
+ The specific (or generic) command (str) to pass to the hardware, either:
1283
+ * ini_detector
1284
+ * close
1285
+ * grab
1286
+ * single
1287
+ * stop_grab
1288
+ * stop_all
1289
+ * update_scanner
1290
+ * move_at_navigator
1291
+ * update_com
1292
+ * update_wait_time
1293
+ * get_axis
1294
+ * any string that the hardware is able to understand
1295
+ """
1296
+ if command.command == "ini_detector":
1297
+ status = self.ini_detector(*command.attribute)
1298
+ self.status_sig.emit(ThreadCommand(command.command, [status, 'log']))
1299
+
1300
+ elif command.command == "close":
1301
+ status = self.close()
1302
+ self.status_sig.emit(ThreadCommand(command.command, [status, 'log']))
1303
+
1304
+ elif command.command == "grab":
1305
+ self.single_grab = False
1306
+ self.grab_state = True
1307
+ self.grab_data(*command.attribute)
1308
+
1309
+ elif command.command == "single":
1310
+ self.single_grab = True
1311
+ self.grab_state = True
1312
+ self.single(*command.attribute)
1313
+
1314
+ elif command.command == "stop_grab":
1315
+ self.grab_state = False
1316
+ self.status_sig.emit(ThreadCommand("Update_Status", ['Stoping grab']))
1317
+
1318
+ elif command.command == "stop_all":
1319
+ self.grab_state = False
1320
+ self.detector.stop()
1321
+ QtWidgets.QApplication.processEvents()
1322
+ self.status_sig.emit(ThreadCommand("Update_Status", ['Stoping grab']))
1323
+
1324
+ elif command.command == 'update_scanner':
1325
+ self.detector.update_scanner(command.attribute[0])
1326
+
1327
+ elif command.command == 'move_at_navigator':
1328
+ self.detector.move_at_navigator(*command.attribute)
1329
+
1330
+ elif command.command == 'update_com':
1331
+ self.detector.update_com()
1332
+
1333
+ elif command.command == 'update_wait_time':
1334
+ self.wait_time = command.attribute[0]
1335
+
1336
+ elif command.command == 'get_axis':
1337
+ self.detector.get_axis()
1338
+
1339
+ else: # custom commands for particular plugins (see ROISelect in relation to a Viewer2D and the plugin
1340
+ # Mock2D or the spectrometer module 'get_spectro_wl' for instance)
1341
+ if hasattr(self.detector, command.command):
1342
+ cmd = getattr(self.detector, command.command)
1343
+ cmd(command.attribute)
1344
+
1345
+ def ini_detector(self, params_state=None, controller=None):
1346
+ """
1347
+ Init the detector from params_state parameter and DAQ_type class attribute :
1348
+ * in **0D** profile : update the local status and send the "x_axis" Thread command via a status signal
1349
+ * in **1D** profile : update the local status and send the "x_axis" Thread command via a status signal
1350
+ * in **2D** profile : update the local status and send the "x_axis" and the "y_axis" Thread command via a status signal
1351
+
1352
+ =============== =========== ==========================================
1353
+ **Parameters** **Type** **Description**
1354
+ *params_state* ??? the parameter's state of initialization
1355
+ =============== =========== ==========================================
1356
+
1357
+ See Also
1358
+ --------
1359
+ ini_detector, daq_utils.ThreadCommand
1360
+ """
1361
+ try:
1362
+ # status="Not initialized"
1363
+ status = edict(initialized=False, info="", x_axis=None, y_axis=None)
1364
+ det_params, class_ = get_viewer_plugins(self.daq_type.name, self.detector_name)
1365
+ self.detector = class_(self, params_state)
1366
+
1367
+ try:
1368
+ self.detector.data_grabed_signal.connect(self.data_ready)
1369
+ self.detector.data_grabed_signal_temp.connect(self.emit_temp_data)
1370
+ infos = self.detector.ini_detector(controller) # return edict(info="", controller=, stage=)
1371
+ status.controller = self.detector.controller
1372
+
1373
+ except Exception as e:
1374
+ logger.exception('Hardware couldn\'t be initialized' + str(e))
1375
+ infos = str(e), False
1376
+ status.controller = None
1377
+
1378
+ if isinstance(infos, edict):
1379
+ status.update(infos)
1380
+ else:
1381
+ status.info = infos[0]
1382
+ status.initialized = infos[1]
1383
+
1384
+
1385
+ if status['x_axis'] is not None:
1386
+ x_axis = status['x_axis']
1387
+ self.status_sig.emit(ThreadCommand("x_axis", [x_axis]))
1388
+ if status['y_axis'] is not None:
1389
+ y_axis = status['y_axis']
1390
+ self.status_sig.emit(ThreadCommand("y_axis", [y_axis]))
1391
+
1392
+ self.hardware_averaging = class_.hardware_averaging # to check if averaging can be done directly by the hardware or done here software wise
1393
+
1394
+ return status
1395
+ except Exception as e:
1396
+ self.logger.exception(str(e))
1397
+ return status
1398
+
1399
+ @Slot(list)
1400
+ def emit_temp_data(self, datas):
1401
+ self.data_detector_temp_sig.emit(datas)
1402
+
1403
+ @Slot(list)
1404
+ def data_ready(self, datas):
1405
+ """
1406
+ | Update the local datas attribute from the given datas parameter if the averaging has to be done software wise.
1407
+ |
1408
+ | Else emit the data detector signals with datas parameter as an attribute.
1409
+
1410
+ =============== ===================== =========================
1411
+ **Parameters** **Type** **Description**
1412
+ *datas* list the datas to be emitted.
1413
+ =============== ===================== =========================
1414
+
1415
+ See Also
1416
+ --------
1417
+ daq_utils.ThreadCommand
1418
+ """
1419
+
1420
+ # datas validation check for backcompatibility with plugins not exporting new DataFromPlugins list of objects
1421
+
1422
+ for dat in datas:
1423
+ if not isinstance(dat, DataFromPlugins):
1424
+ if 'type' in dat:
1425
+ dat['dim'] = dat['type']
1426
+ dat['type'] = 'raw'
1427
+
1428
+ if not self.hardware_averaging: # to execute if the averaging has to be done software wise
1429
+ self.ind_average += 1
1430
+ if self.ind_average == 1:
1431
+ self.datas = datas
1432
+ else:
1433
+ try:
1434
+ for indpannel, dic in enumerate(datas):
1435
+ self.datas[indpannel]['data'] = \
1436
+ [((self.ind_average - 1) * self.datas[indpannel]['data'][ind] + datas[indpannel]['data'][
1437
+ ind]) / self.ind_average for ind in range(len(datas[indpannel]['data']))]
1438
+
1439
+ if self.show_averaging:
1440
+ self.emit_temp_data(self.datas)
1441
+
1442
+ except Exception as e:
1443
+ self.logger.exception(str(e))
1444
+
1445
+ if self.ind_average == self.Naverage:
1446
+ self.average_done = True
1447
+ self.data_detector_sig.emit(self.datas)
1448
+ self.ind_average = 0
1449
+ else:
1450
+ self.data_detector_sig.emit(datas)
1451
+ self.waiting_for_data = False
1452
+ if not self.grab_state:
1453
+ # self.status_sig.emit(["Update_Status","Grabing braked"])
1454
+ self.detector.stop()
1455
+
1456
+ def single(self, Naverage=1, args_as_dict={}):
1457
+ """
1458
+ Call the grab method with Naverage parameter as an attribute.
1459
+
1460
+ =============== =========== ==================
1461
+ **Parameters** **Type** **Description**
1462
+ *Naverage* int
1463
+ *savepath* str eventual savepath
1464
+ =============== =========== ==================
1465
+
1466
+ See Also
1467
+ --------
1468
+ daq_utils.ThreadCommand, grab
1469
+ """
1470
+ try:
1471
+ self.grab_data(Naverage, live=False, **args_as_dict)
1472
+
1473
+ except Exception as e:
1474
+ self.logger.exception(str(e))
1475
+
1476
+ def grab_data(self, Naverage=1, live=True, **kwargs):
1477
+ """
1478
+ | Update status with 'Start Grabing' Update_status sub command of the Thread command.
1479
+ | Process events and grab naverage is needed.
1480
+
1481
+ =============== =========== ==================
1482
+ **Parameters** **Type** **Description**
1483
+ *Naverage* int
1484
+ =============== =========== ==================
1485
+
1486
+ See Also
1487
+ --------
1488
+ daq_utils.ThreadCommand, grab
1489
+ """
1490
+ try:
1491
+ self.ind_average = 0
1492
+ self.Naverage = Naverage
1493
+ if Naverage > 1:
1494
+ self.average_done = False
1495
+ # self.status_sig.emit(ThreadCommand("Update_Status", [f'Start Grabing']))
1496
+ self.waiting_for_data = False
1497
+
1498
+ # for live mode:two possibilities: either snap one data and regrab softwarewise (while True) or if
1499
+ # self.detector.live_mode_available is True all data is continuously emited from the plugin
1500
+ if self.detector.live_mode_available:
1501
+ kwargs['wait_time'] = self.wait_time
1502
+ else:
1503
+ kwargs['wait_time'] = 0
1504
+ self.status_sig.emit(ThreadCommand('grab'))
1505
+ while True:
1506
+ try:
1507
+ if not self.waiting_for_data:
1508
+ self.waiting_for_data = True
1509
+ self.detector.grab_data(Naverage, live=live, **kwargs)
1510
+ QtWidgets.QApplication.processEvents()
1511
+ if self.single_grab:
1512
+ if self.hardware_averaging:
1513
+ break
1514
+ else:
1515
+ if self.average_done:
1516
+ break
1517
+ else:
1518
+ QThread.msleep(self.wait_time) #if in live mode apply a waiting time after acquisition
1519
+ if not self.grab_state:
1520
+ break
1521
+ if self.detector.live_mode_available:
1522
+ break
1523
+ except Exception as e:
1524
+ self.logger.exception(str(e))
1525
+ self.status_sig.emit(ThreadCommand('grab_stopped'))
1526
+
1527
+ except Exception as e:
1528
+ self.logger.exception(str(e))
1529
+
1530
+ def close(self):
1531
+ """
1532
+ close the current instance of DAQ_Detector.
1533
+ """
1534
+ try:
1535
+ self.detector.stop()
1536
+ status = self.detector.close()
1537
+ except Exception as e:
1538
+ self.logger.exception(str(e))
1539
+ status = str(e)
1540
+ return status
1541
+
1542
+
1543
+ def prepare_docks(area, title):
1544
+ dock_settings = Dock(title + " settings", size=(150, 250))
1545
+ dock_viewer = Dock(title + " viewer", size=(350, 350))
1546
+ area.addDock(dock_settings)
1547
+ area.addDock(dock_viewer, 'right', dock_settings)
1548
+ return dict(dock_settings=dock_settings, dock_viewer=dock_viewer)
1549
+
1550
+
1551
+ def main(init_qt=True, init_det=False):
1552
+ if init_qt: # used for the test suite
1553
+ app = QtWidgets.QApplication(sys.argv)
1554
+ if config('style', 'darkstyle'):
1555
+ import qdarkstyle
1556
+ app.setStyleSheet(qdarkstyle.load_stylesheet(qdarkstyle.DarkPalette))
1557
+
1558
+ win = QtWidgets.QMainWindow()
1559
+ area = DockArea()
1560
+ win.setCentralWidget(area)
1561
+ win.resize(1000, 500)
1562
+ win.setWindowTitle('PyMoDAQ Viewer')
1563
+ win.show()
1564
+
1565
+ title = "Testing"
1566
+ viewer = DAQ_Viewer(area, title="Testing", daq_type=config('viewer', 'daq_type'),
1567
+ **prepare_docks(area, title))
1568
+ if init_det:
1569
+ viewer.init_hardware_ui(init_det)
1570
+
1571
+ if init_qt:
1572
+ sys.exit(app.exec_())
1573
+ return viewer, win
1574
+
1575
+
1576
+ if __name__ == '__main__':
1577
+ main(init_det=False)