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