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

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

Potentially problematic release.


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

Files changed (233) hide show
  1. pymodaq/__init__.py +13 -6
  2. pymodaq/control_modules/__init__.py +0 -7
  3. pymodaq/control_modules/daq_move.py +965 -2
  4. pymodaq/control_modules/daq_move_ui.py +319 -0
  5. pymodaq/control_modules/daq_viewer.py +1573 -3
  6. pymodaq/control_modules/daq_viewer_ui.py +393 -0
  7. pymodaq/control_modules/mocks.py +51 -0
  8. pymodaq/control_modules/move_utility_classes.py +709 -8
  9. pymodaq/control_modules/utils.py +256 -0
  10. pymodaq/control_modules/viewer_utility_classes.py +663 -6
  11. pymodaq/daq_utils.py +89 -0
  12. pymodaq/dashboard.py +91 -72
  13. pymodaq/examples/custom_app.py +12 -11
  14. pymodaq/examples/custom_viewer.py +10 -10
  15. pymodaq/examples/function_plotter.py +16 -13
  16. pymodaq/examples/nonlinearscanner.py +8 -6
  17. pymodaq/examples/parameter_ex.py +7 -7
  18. pymodaq/examples/preset_MockCamera.xml +1 -0
  19. pymodaq/extensions/__init__.py +16 -0
  20. pymodaq/extensions/console.py +76 -0
  21. pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
  22. pymodaq/extensions/daq_scan.py +1339 -0
  23. pymodaq/extensions/daq_scan_ui.py +240 -0
  24. pymodaq/extensions/h5browser.py +23 -0
  25. pymodaq/{pid → extensions/pid}/__init__.py +4 -2
  26. pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
  27. pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
  28. pymodaq/{pid → extensions/pid}/utils.py +52 -6
  29. pymodaq/extensions/utils.py +40 -0
  30. pymodaq/post_treatment/__init__.py +6 -0
  31. pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
  32. pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
  33. pymodaq/post_treatment/load_and_plot.py +219 -0
  34. pymodaq/post_treatment/process_to_scalar.py +263 -0
  35. pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
  36. pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
  37. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
  38. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
  39. pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
  40. pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
  41. pymodaq/resources/VERSION +1 -1
  42. pymodaq/resources/config_template.toml +32 -13
  43. pymodaq/resources/preset_default.xml +1 -1
  44. pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
  45. pymodaq/utils/__init__.py +0 -29
  46. pymodaq/utils/abstract/__init__.py +48 -0
  47. pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
  48. pymodaq/utils/array_manipulation.py +379 -8
  49. pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
  50. pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
  51. pymodaq/utils/config.py +448 -0
  52. pymodaq/utils/conftests.py +5 -0
  53. pymodaq/utils/daq_utils.py +828 -8
  54. pymodaq/utils/data.py +1873 -7
  55. pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
  56. pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
  57. pymodaq/{daq_utils → utils}/enums.py +12 -7
  58. pymodaq/utils/exceptions.py +37 -0
  59. pymodaq/utils/factory.py +82 -0
  60. pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
  61. pymodaq/utils/gui_utils/custom_app.py +129 -0
  62. pymodaq/utils/gui_utils/file_io.py +66 -0
  63. pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
  64. pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
  65. pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
  66. pymodaq/utils/gui_utils/widgets/label.py +24 -0
  67. pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
  68. pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
  69. pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
  70. pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
  71. pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
  72. pymodaq/utils/h5modules/__init__.py +1 -0
  73. pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
  74. pymodaq/utils/h5modules/browsing.py +683 -0
  75. pymodaq/utils/h5modules/data_saving.py +839 -0
  76. pymodaq/utils/h5modules/h5logging.py +110 -0
  77. pymodaq/utils/h5modules/module_saving.py +350 -0
  78. pymodaq/utils/h5modules/saving.py +914 -0
  79. pymodaq/utils/h5modules/utils.py +85 -0
  80. pymodaq/utils/logger.py +64 -6
  81. pymodaq/utils/managers/action_manager.py +460 -0
  82. pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
  83. pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
  84. pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
  85. pymodaq/utils/managers/parameter_manager.py +110 -0
  86. pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
  87. pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
  88. pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
  89. pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
  90. pymodaq/utils/math_utils.py +546 -10
  91. pymodaq/{daq_utils → utils}/messenger.py +5 -1
  92. pymodaq/utils/parameter/__init__.py +2 -15
  93. pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
  94. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
  95. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
  96. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
  97. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
  98. pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
  99. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
  100. pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
  101. pymodaq/utils/parameter/utils.py +206 -11
  102. pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
  103. pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
  104. pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
  105. pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
  106. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
  107. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
  108. pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
  109. pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
  110. pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
  111. pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
  112. pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
  113. pymodaq/utils/plotting/navigator.py +355 -0
  114. pymodaq/utils/plotting/scan_selector.py +480 -0
  115. pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
  116. pymodaq/utils/plotting/utils/filter.py +538 -0
  117. pymodaq/utils/plotting/utils/lineout.py +224 -0
  118. pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
  119. pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
  120. pymodaq/utils/plotting/widgets.py +76 -0
  121. pymodaq/utils/scanner/__init__.py +10 -0
  122. pymodaq/utils/scanner/scan_factory.py +204 -0
  123. pymodaq/utils/scanner/scanner.py +271 -0
  124. pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
  125. pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
  126. pymodaq/utils/scanner/scanners/sequential.py +192 -0
  127. pymodaq/utils/scanner/scanners/tabular.py +294 -0
  128. pymodaq/utils/scanner/utils.py +83 -0
  129. pymodaq/utils/slicing.py +47 -0
  130. pymodaq/utils/svg/__init__.py +6 -0
  131. pymodaq/utils/svg/svg_renderer.py +20 -0
  132. pymodaq/utils/svg/svg_view.py +35 -0
  133. pymodaq/utils/svg/svg_viewer2D.py +51 -0
  134. pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
  135. pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
  136. pymodaq/utils/units.py +216 -0
  137. pymodaq-4.0.1.dist-info/METADATA +159 -0
  138. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
  139. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
  140. pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
  141. pymodaq/daq_move/daq_move_gui.py +0 -279
  142. pymodaq/daq_move/daq_move_gui.ui +0 -534
  143. pymodaq/daq_move/daq_move_main.py +0 -1042
  144. pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
  145. pymodaq/daq_move/utility_classes.py +0 -671
  146. pymodaq/daq_scan.py +0 -2160
  147. pymodaq/daq_utils/array_manipulation.py +0 -386
  148. pymodaq/daq_utils/config.py +0 -273
  149. pymodaq/daq_utils/conftests.py +0 -7
  150. pymodaq/daq_utils/custom_parameter_tree.py +0 -9
  151. pymodaq/daq_utils/daq_enums.py +0 -133
  152. pymodaq/daq_utils/daq_utils.py +0 -1402
  153. pymodaq/daq_utils/exceptions.py +0 -71
  154. pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
  155. pymodaq/daq_utils/gui_utils/file_io.py +0 -75
  156. pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
  157. pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
  158. pymodaq/daq_utils/h5exporters.py +0 -242
  159. pymodaq/daq_utils/h5modules.py +0 -1559
  160. pymodaq/daq_utils/h5utils.py +0 -241
  161. pymodaq/daq_utils/managers/action_manager.py +0 -236
  162. pymodaq/daq_utils/managers/parameter_manager.py +0 -57
  163. pymodaq/daq_utils/math_utils.py +0 -705
  164. pymodaq/daq_utils/parameter/__init__.py +0 -1
  165. pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
  166. pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
  167. pymodaq/daq_utils/parameter/utils.py +0 -136
  168. pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
  169. pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
  170. pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
  171. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
  172. pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
  173. pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
  174. pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
  175. pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
  176. pymodaq/daq_utils/plotting/items/__init__.py +0 -0
  177. pymodaq/daq_utils/plotting/navigator.py +0 -500
  178. pymodaq/daq_utils/plotting/scan_selector.py +0 -289
  179. pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
  180. pymodaq/daq_utils/plotting/utils/filter.py +0 -236
  181. pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
  182. pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
  183. pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
  184. pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
  185. pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
  186. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
  187. pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
  188. pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
  189. pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
  190. pymodaq/daq_utils/scanner.py +0 -1289
  191. pymodaq/daq_utils/tree_layout/__init__.py +0 -0
  192. pymodaq/daq_viewer/__init__.py +0 -0
  193. pymodaq/daq_viewer/daq_gui_settings.py +0 -237
  194. pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
  195. pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
  196. pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
  197. pymodaq/daq_viewer/utility_classes.py +0 -673
  198. pymodaq/examples/logger_image/__init__.py +0 -0
  199. pymodaq/examples/logger_image/logger_displayer.py +0 -121
  200. pymodaq/examples/logger_image/setup.svg +0 -3119
  201. pymodaq/examples/logger_image/setup_svg.py +0 -114
  202. pymodaq/h5browser.py +0 -39
  203. pymodaq/utils/scanner.py +0 -15
  204. pymodaq-3.6.12.dist-info/METADATA +0 -39
  205. pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
  206. pymodaq-3.6.12.dist-info/top_level.txt +0 -1
  207. /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
  208. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
  209. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
  210. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
  211. /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
  212. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
  213. /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
  214. /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
  215. /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
  216. /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
  217. /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
  218. /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
  219. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
  220. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
  221. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
  222. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
  223. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
  224. /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
  225. /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
  226. /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
  227. /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
  228. /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
  229. /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
  230. /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
  231. /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
  232. /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
  233. {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,914 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created the 15/11/2022
4
+
5
+ @author: Sebastien Weber
6
+ """
7
+ import copy
8
+ import datetime
9
+ from dateutil import parser
10
+ from numbers import Number
11
+ import os
12
+ from pathlib import Path
13
+ from typing import Union, Iterable
14
+
15
+
16
+ import numpy as np
17
+ from qtpy.QtCore import QObject, Signal
18
+ from qtpy import QtWidgets
19
+
20
+ from pymodaq.utils.logger import set_logger, get_module_name
21
+ from pymodaq.utils.parameter import Parameter, ParameterTree
22
+ from pymodaq.utils.parameter import utils as putils
23
+ from pymodaq.utils.managers.parameter_manager import ParameterManager
24
+ from pymodaq.utils.gui_utils.file_io import select_file
25
+
26
+ from pymodaq.utils.gui_utils.utils import dashboard_submodules_params
27
+ from pymodaq.utils import daq_utils as utils
28
+ from pymodaq.utils.config import Config
29
+ from pymodaq.utils.data import DataDim, DataToExport, Axis, DataWithAxes
30
+ from pymodaq.utils.enums import BaseEnum, enum_checker
31
+ from pymodaq.utils.scanner.utils import ScanType
32
+ from pymodaq.utils.messenger import deprecation_msg
33
+
34
+
35
+ from .backends import (H5Backend, backends_available, SaveType, InvalidSave, InvalidExport, InvalidDataType,
36
+ InvalidGroupType, InvalidGroupDataType, Node, GroupType, InvalidDataDimension, InvalidScanType,
37
+ GROUP, VLARRAY)
38
+ from . import browsing
39
+
40
+
41
+ config = Config()
42
+ logger = set_logger(get_module_name(__file__))
43
+
44
+
45
+ class FileType(BaseEnum):
46
+ detector = 0
47
+ actuator = 1
48
+ axis = 2
49
+ scan = 3
50
+
51
+
52
+ class DataType(BaseEnum):
53
+ data = 'Data'
54
+ axis = 'Axis'
55
+ live_scan = 'Live'
56
+ external_h5 = 'ExtData'
57
+ strings = 'Strings'
58
+ bkg = 'Bkg'
59
+ data_enlargeable = 'EnlData'
60
+
61
+
62
+ class H5SaverLowLevel(H5Backend):
63
+ """Object containing basic methods in order to structure and interact with a h5file compatible with the h5browser
64
+
65
+ See Also
66
+ --------
67
+ H5Browser
68
+
69
+ Attributes
70
+ ----------
71
+ h5_file: pytables hdf5 file
72
+ object used to save all datas and metadas
73
+ h5_file_path: str or Path
74
+ The file path
75
+ """
76
+
77
+ def __init__(self, save_type: SaveType = 'scan', backend='tables'):
78
+ H5Backend.__init__(self, backend)
79
+
80
+ self.save_type = enum_checker(SaveType, save_type)
81
+
82
+ self.h5_file_path = None
83
+ self.h5_file_name = None
84
+ self.file_loaded = False
85
+
86
+ self._current_group = None
87
+ self._raw_group: Union[GROUP, str] = '/RawData'
88
+ self._logger_array = None
89
+
90
+ @property
91
+ def raw_group(self):
92
+ return self._raw_group
93
+
94
+ @property
95
+ def h5_file(self):
96
+ return self._h5file
97
+
98
+ def init_file(self, file_name: Path, raw_group_name='RawData', new_file=False, metadata: dict = None):
99
+ """Initializes a new h5 file.
100
+
101
+ Parameters
102
+ ----------
103
+ file_name: Path
104
+ a complete Path pointing to a h5 file
105
+ raw_group_name: str
106
+ Base node name
107
+ new_file: bool
108
+ If True create a new file, otherwise append to a potential existing one
109
+
110
+ Returns
111
+ -------
112
+ bool
113
+ True if new file has been created, False otherwise
114
+ """
115
+ datetime_now = datetime.datetime.now()
116
+
117
+ if file_name is not None and isinstance(file_name, Path):
118
+ self.h5_file_name = file_name.stem + ".h5"
119
+ self.h5_file_path = file_name.parent
120
+ if not self.h5_file_path.joinpath(self.h5_file_name).is_file():
121
+ new_file = True
122
+
123
+ else:
124
+ self.h5_file_name = select_file(save=True, ext='h5')
125
+ self.h5_file_path = self.h5_file_name.parent
126
+ new_file = True
127
+
128
+ self.close_file()
129
+ self.open_file(self.h5_file_path.joinpath(self.h5_file_name), 'w' if new_file else 'a', title='PyMoDAQ file')
130
+
131
+ self._raw_group = self.get_set_group(self.root(), raw_group_name, title='Data from PyMoDAQ modules')
132
+ self.get_set_logger(self._raw_group)
133
+
134
+ if new_file:
135
+ self._raw_group.attrs['type'] = self.save_type.name # first possibility to set a node attribute
136
+ self.root().set_attr('file', self.h5_file_name) # second possibility
137
+
138
+ self.set_attr(self.root(), 'date', datetime_now.date().isoformat())
139
+ self.set_attr(self.root(), 'time', datetime_now.time().isoformat())
140
+
141
+ if metadata is not None:
142
+ for metadata_key in metadata:
143
+ self._raw_group.attrs[metadata_key] = metadata[metadata_key]
144
+
145
+ def save_file(self, filename=None):
146
+ if filename is None:
147
+ filename = select_file(None, save=True, ext='h5')
148
+ if filename != '':
149
+ super().save_file_as(filename)
150
+
151
+ def get_set_logger(self, where: Node = None) -> VLARRAY:
152
+ """ Retrieve or create (if absent) a logger enlargeable array to store logs
153
+ Get attributed to the class attribute ``logger_array``
154
+ Parameters
155
+ ----------
156
+ where: node
157
+ location within the tree where to save or retrieve the array
158
+
159
+ Returns
160
+ -------
161
+ vlarray
162
+ enlargeable array accepting strings as elements
163
+ """
164
+ if where is None:
165
+ where = self.raw_group
166
+ if isinstance(where, Node):
167
+ where = where.node
168
+ logger = 'Logger'
169
+ if logger not in list(self.get_children(where)):
170
+ # check if logger node exist
171
+ self._logger_array = self.add_string_array(where, logger)
172
+ self._logger_array.attrs['type'] = 'log'
173
+ else:
174
+ self._logger_array = self.get_node(where, name=logger)
175
+ return self._logger_array
176
+
177
+ def add_log(self, msg):
178
+ self._logger_array.append(msg)
179
+
180
+ def add_string_array(self, where, name, title='', metadata=dict([])):
181
+ array = self.create_vlarray(where, name, dtype='string', title=title)
182
+ array.attrs['shape'] = (0,)
183
+ array.attrs['data_type'] = 'strings'
184
+
185
+ for metadat in metadata:
186
+ array.attrs[metadat] = metadata[metadat]
187
+ return array
188
+
189
+ def add_array(self, where: Union[GROUP, str], name: str, data_type: DataType, array_to_save: np.ndarray = None,
190
+ data_shape: tuple = None, array_type: np.dtype = None, data_dimension: DataDim = None,
191
+ scan_shape: tuple = tuple([]), add_scan_dim=False, enlargeable: bool = False,
192
+ title: str = '', metadata=dict([]), ):
193
+
194
+ """save data arrays on the hdf5 file together with metadata
195
+ Parameters
196
+ ----------
197
+ where: GROUP
198
+ node where to save the array
199
+ name: str
200
+ name of the array in the hdf5 file
201
+ data_type: DataType
202
+ mandatory so that the h5Browser can interpret correctly the array
203
+ data_shape: Iterable
204
+ the shape of the array to save, mandatory if array_to_save is None
205
+ data_dimension: DataDim
206
+ The data's dimension
207
+ scan_shape: Iterable
208
+ the shape of the scan dimensions
209
+ title: str
210
+ the title attribute of the array node
211
+ array_to_save: ndarray or None
212
+ data to be saved in the array. If None, array_type and data_shape should be specified in order to init
213
+ correctly the memory
214
+ array_type: np.dtype or numpy types
215
+ eg np.float, np.int32 ...
216
+ enlargeable: bool
217
+ if False, data are saved as a CARRAY, otherwise as a EARRAY (for ragged data, see add_sting_array)
218
+ metadata: dict
219
+ dictionnary whose keys will be saved as the array attributes
220
+ add_scan_dim: if True, the scan axes dimension (scan_shape iterable) is prepended to the array shape on the hdf5
221
+ In that case, the array is usually initialized as zero and further populated
222
+
223
+ Returns
224
+ -------
225
+ array (CARRAY or EARRAY)
226
+
227
+ See Also
228
+ --------
229
+ add_data, add_string_array
230
+ """
231
+ if array_type is None:
232
+ if array_to_save is None:
233
+ array_type = config('data_saving', 'data_type', 'dynamic')
234
+ else:
235
+ array_type = array_to_save.dtype
236
+
237
+ data_type = enum_checker(DataType, data_type)
238
+ data_dimension = enum_checker(DataDim, data_dimension)
239
+
240
+ if enlargeable:
241
+ if data_shape == (1,):
242
+ data_shape = None
243
+ array = self.create_earray(where, utils.capitalize(name), dtype=np.dtype(array_type),
244
+ data_shape=data_shape, title=title)
245
+ else:
246
+ if add_scan_dim: # means it is an array initialization to zero
247
+ shape = list(scan_shape[:])
248
+ if not(len(data_shape) == 1 and data_shape[0] == 1): # means data are not ndarrays of scalars
249
+ shape.extend(data_shape)
250
+ if array_to_save is None:
251
+ array_to_save = np.zeros(shape, dtype=np.dtype(array_type))
252
+
253
+ array = self.create_carray(where, utils.capitalize(name), obj=array_to_save, title=title)
254
+ self.set_attr(array, 'data_type', data_type.name)
255
+ self.set_attr(array, 'data_dimension', data_dimension.name)
256
+
257
+ for metadat in metadata:
258
+ self.set_attr(array, metadat, metadata[metadat])
259
+ return array
260
+
261
+ def get_set_group(self, where, name, title=''):
262
+ """Get the group located at where if it exists otherwise creates it
263
+
264
+ This also set the _current_group property
265
+ """
266
+ self._current_group = super().get_set_group(where, name, title)
267
+ return self._current_group
268
+
269
+ def get_groups(self, where: Union[str, GROUP], group_type: GroupType):
270
+ """Get all groups hanging from a Group and of a certain type"""
271
+ groups = []
272
+ for node_name in list(self.get_children(where)):
273
+ group = self.get_node(where, node_name)
274
+ if 'type' in group.attrs and group.attrs['type'] == group_type.name:
275
+ groups.append(group)
276
+ return groups
277
+
278
+ def get_last_group(self, where: GROUP, group_type: GroupType):
279
+ groups = self.get_groups(where, group_type)
280
+ if len(groups) != 0:
281
+ return groups[-1]
282
+ else:
283
+ return None
284
+
285
+ def get_node_from_attribute_match(self, where, attr_name, attr_value):
286
+ """Get a Node starting from a given node (Group) matching a pair of node attribute name and value"""
287
+ for node in self.walk_nodes(where):
288
+ if attr_name in node.attrs and node.attrs[attr_name] == attr_value:
289
+ return node
290
+
291
+ def get_node_from_title(self, where, title: str):
292
+ """Get a Node starting from a given node (Group) matching the given title"""
293
+ return self.get_node_from_attribute_match(where, 'TITLE', title)
294
+
295
+ def add_data_group(self, where, data_dim: DataDim, title='', settings_as_xml='', metadata=dict([])):
296
+ """Creates a group node at given location in the tree
297
+
298
+ Parameters
299
+ ----------
300
+ where: group node
301
+ where to create data group
302
+ group_data_type: DataDim
303
+ title: str, optional
304
+ a title for this node, will be saved as metadata
305
+ settings_as_xml: str, optional
306
+ XML string created from a Parameter object to be saved as metadata
307
+ metadata: dict, optional
308
+ will be saved as a new metadata attribute with name: key and value: dict value
309
+
310
+ Returns
311
+ -------
312
+ group: group node
313
+
314
+ See Also
315
+ --------
316
+ :py:meth:`add_group`
317
+ """
318
+ data_dim = enum_checker(DataDim, data_dim)
319
+ metadata.update(settings=settings_as_xml)
320
+ group = self.add_group(data_dim.name, 'data_dim', where, title, metadata)
321
+ return group
322
+
323
+ def add_incremental_group(self, group_type, where, title='', settings_as_xml='', metadata=dict([])):
324
+ """
325
+ Add a node in the h5 file tree of the group type with an increment in the given name
326
+ Parameters
327
+ ----------
328
+ group_type: str or GroupType enum
329
+ one of the possible values of **group_types**
330
+ where: str or node
331
+ parent node where to create the new group
332
+ title: str
333
+ node title
334
+ settings_as_xml: str
335
+ XML string containing Parameter representation
336
+ metadata: dict
337
+ extra metadata to be saved with this new group node
338
+
339
+ Returns
340
+ -------
341
+ node: newly created group node
342
+ """
343
+ group_type = enum_checker(GroupType, group_type)
344
+
345
+ nodes = [name for name in self.get_children(self.get_node(where))]
346
+ nodes_tmp = []
347
+ for node in nodes:
348
+ if utils.capitalize(group_type.name) in node:
349
+ nodes_tmp.append(node)
350
+ nodes_tmp.sort()
351
+ if len(nodes_tmp) == 0:
352
+ ind_group = -1
353
+ else:
354
+ ind_group = int(nodes_tmp[-1][-3:])
355
+ group = self.get_set_group(where, f'{utils.capitalize(group_type.name)}{ind_group + 1:03d}', title)
356
+ self.set_attr(group, 'settings', settings_as_xml)
357
+ if group_type.name.lower() != 'ch':
358
+ self.set_attr(group, 'type', group_type.name.lower())
359
+ else:
360
+ self.set_attr(group, 'type', '')
361
+ for metadat in metadata:
362
+ self.set_attr(group, metadat, metadata[metadat])
363
+ return group
364
+
365
+ def add_act_group(self, where, title='', settings_as_xml='', metadata=dict([])):
366
+ """
367
+ Add a new group of type detector
368
+ See Also
369
+ -------
370
+ add_incremental_group
371
+ """
372
+ group = self.add_incremental_group('actuator', where, title, settings_as_xml, metadata)
373
+ return group
374
+
375
+ def add_det_group(self, where, title='', settings_as_xml='', metadata=dict([])):
376
+ """
377
+ Add a new group of type detector
378
+ See Also
379
+ -------
380
+ add_incremental_group
381
+ """
382
+ group = self.add_incremental_group('detector', where, title, settings_as_xml, metadata)
383
+ return group
384
+
385
+ def add_scan_group(self, where='/RawData', title='', settings_as_xml='', metadata=dict([])):
386
+ """
387
+ Add a new group of type scan
388
+ See Also
389
+ -------
390
+ add_incremental_group
391
+ """
392
+ metadata.update(dict(description='', scan_done=False))
393
+ group = self.add_incremental_group('scan', where, title, settings_as_xml, metadata)
394
+ return group
395
+
396
+ def add_ch_group(self, where, title='', settings_as_xml='', metadata=dict([])):
397
+ """
398
+ Add a new group of type channel
399
+ See Also
400
+ -------
401
+ add_incremental_group
402
+ """
403
+ group = self.add_incremental_group('ch', where, title, settings_as_xml, metadata)
404
+ return group
405
+
406
+
407
+ def add_move_group(self, where, title='', settings_as_xml='', metadata=dict([])):
408
+ """
409
+ Add a new group of type actuator
410
+ See Also
411
+ -------
412
+ add_incremental_group
413
+ """
414
+ group = self.add_incremental_group('actuator', where, title, settings_as_xml, metadata)
415
+ return group
416
+
417
+ def show_file_content(self):
418
+ form = QtWidgets.QWidget()
419
+ if not self.isopen():
420
+ if self.h5_file_path is not None:
421
+ if self.h5_file_path.exists():
422
+ self.analysis_prog = browsing.H5Browser(form, h5file_path=self.h5_file_path)
423
+ else:
424
+ logger.warning('The h5 file path has not been defined yet')
425
+ else:
426
+ logger.warning('The h5 file path has not been defined yet')
427
+ else:
428
+ self.flush()
429
+ self.analysis_prog = browsing.H5Browser(form, h5file=self.h5file)
430
+ form.show()
431
+
432
+
433
+ class H5SaverBase(H5SaverLowLevel, ParameterManager):
434
+ """Object containing all methods in order to save datas in a *hdf5 file* with a hierarchy compatible with
435
+ the H5Browser. The saving parameters are contained within a **Parameter** object: self.settings that can be displayed
436
+ on a UI using the widget self.settings_tree. At the creation of a new file, a node
437
+ group named **Raw_datas** and represented by the attribute ``raw_group`` is created and set with a metadata attribute:
438
+
439
+ * 'type' given by the **save_type** class parameter
440
+
441
+ The root group of the file is then set with a few metadata:
442
+
443
+ * 'pymodaq_version' the current pymodaq version, e.g. 1.6.2
444
+ * 'file' the file name
445
+ * 'date' the current date
446
+ * 'time' the current time
447
+
448
+ All datas will then be saved under this node in various groups
449
+
450
+ See Also
451
+ --------
452
+ H5Browser
453
+
454
+ Parameters
455
+ ----------
456
+ h5_file: pytables hdf5 file
457
+ object used to save all datas and metadas
458
+ h5_file_path: str or Path
459
+ Signal signal represented by a float. Is emitted each time the hardware reached the target
460
+ position within the epsilon precision (see comon_parameters variable)
461
+ save_type: str
462
+ an element of the enum module attribute SaveType
463
+ * 'scan' is used for DAQScan module and should be used for similar application
464
+ * 'detector' is used for DAQ_Viewer module and should be used for similar application
465
+ * 'custom' should be used for customized applications
466
+
467
+ Attributes
468
+ ----------
469
+
470
+ settings: Parameter
471
+ Parameter instance (pyqtgraph) containing all settings (could be represented using the settings_tree widget)
472
+
473
+ settings_tree: ParameterTree
474
+ Widget representing as a Tree structure, all the settings defined in the class preamble variable ``params``
475
+
476
+ """
477
+ settings_name = 'h5saver_settings'
478
+ params = [
479
+ {'title': 'Save type:', 'name': 'save_type', 'type': 'list', 'limits': SaveType.names(), 'readonly': True},
480
+ ] + dashboard_submodules_params + \
481
+ [{'title': 'Backend:', 'name': 'backend', 'type': 'group', 'children': [
482
+ {'title': 'Backend type:', 'name': 'backend_type', 'type': 'list', 'limits': backends_available,
483
+ 'readonly': True},
484
+ {'title': 'HSDS Server:', 'name': 'hsds_options', 'type': 'group', 'visible': False, 'children': [
485
+ {'title': 'Endpoint:', 'name': 'endpoint', 'type': 'str',
486
+ 'value': config('data_saving', 'hsds', 'root_url'), 'readonly': False},
487
+ {'title': 'User:', 'name': 'user', 'type': 'str',
488
+ 'value': config('data_saving', 'hsds', 'username'), 'readonly': False},
489
+ {'title': 'password:', 'name': 'password', 'type': 'str',
490
+ 'value': config('data_saving', 'hsds', 'pwd'), 'readonly': False},
491
+ ]},
492
+ ]},
493
+
494
+ {'title': 'custom_name?:', 'name': 'custom_name', 'type': 'bool', 'default': False, 'value': False},
495
+ {'title': 'show file content?', 'name': 'show_file', 'type': 'bool_push', 'default': False,
496
+ 'value': False},
497
+ {'title': 'Base path:', 'name': 'base_path', 'type': 'browsepath',
498
+ 'value': config('data_saving', 'h5file', 'save_path'), 'filetype': False, 'readonly': True, },
499
+ {'title': 'Base name:', 'name': 'base_name', 'type': 'str', 'value': 'Scan', 'readonly': True},
500
+ {'title': 'Current scan:', 'name': 'current_scan_name', 'type': 'str', 'value': '', 'readonly': True},
501
+ {'title': 'Current path:', 'name': 'current_scan_path', 'type': 'text',
502
+ 'value': config('data_saving', 'h5file', 'save_path'), 'readonly': True, 'visible': False},
503
+ {'title': 'h5file:', 'name': 'current_h5_file', 'type': 'text', 'value': '', 'readonly': True},
504
+ {'title': 'New file', 'name': 'new_file', 'type': 'action'},
505
+ {'title': 'Saving dynamic', 'name': 'dynamic', 'type': 'list',
506
+ 'limits': config('data_saving', 'data_type', 'dynamics'),
507
+ 'value': config('data_saving', 'data_type', 'dynamic')},
508
+ {'title': 'Compression options:', 'name': 'compression_options', 'type': 'group', 'children': [
509
+ {'title': 'Compression library:', 'name': 'h5comp_library', 'type': 'list', 'value': 'zlib',
510
+ 'limits': ['zlib', 'gzip']},
511
+ {'title': 'Compression level:', 'name': 'h5comp_level', 'type': 'int',
512
+ 'value': config('data_saving', 'h5file', 'compression_level'), 'min': 0, 'max': 9},
513
+ ]},
514
+ ]
515
+
516
+ def __init__(self, save_type='scan', backend='tables'):
517
+ """
518
+
519
+ Parameters
520
+ ----------
521
+ save_type (str): one of ['scan', 'detector', 'logger', 'custom']
522
+ backend (str): either 'tables' for pytables backend, 'h5py' for h5py backends or 'h5pyd' for HSDS backend
523
+
524
+ See Also
525
+ --------
526
+ https://github.com/HDFGroup/hsds
527
+ """
528
+ H5SaverLowLevel.__init__(self, save_type, backend)
529
+ ParameterManager.__init__(self)
530
+
531
+ self.current_scan_group = None
532
+ self.current_scan_name = None
533
+
534
+ self.settings.child('save_type').setValue(self.save_type.name)
535
+
536
+ def show_settings(self, show=True):
537
+ self.settings_tree.setVisible(show)
538
+
539
+ def init_file(self, update_h5=False, custom_naming=False, addhoc_file_path=None, metadata=dict([])):
540
+ """Initializes a new h5 file.
541
+ Could set the h5_file attributes as:
542
+
543
+ * a file with a name following a template if ``custom_naming`` is ``False`` and ``addhoc_file_path`` is ``None``
544
+ * a file within a name set using a file dialog popup if ``custom_naming`` is ``True``
545
+ * a file with a custom name if ``addhoc_file_path`` is a ``Path`` object or a path string
546
+
547
+ Parameters
548
+ ----------
549
+ update_h5: bool
550
+ create a new h5 file with name specified by other parameters
551
+ if false try to open an existing file and will append new data to it
552
+ custom_naming: bool
553
+ if True, a selection file dialog opens to set a new file name
554
+ addhoc_file_path: Path or str
555
+ supplied name by the user for the new file
556
+ metadata: dict
557
+ dictionnary with pair of key, value that should be saved as attributes of the root group
558
+ Returns
559
+ -------
560
+ update_h5: bool
561
+ True if new file has been created, False otherwise
562
+ """
563
+ datetime_now = datetime.datetime.now()
564
+ if addhoc_file_path is None:
565
+ if not os.path.isdir(self.settings['base_path']):
566
+ os.mkdir(self.settings['base_path'])
567
+
568
+ # set the filename and path
569
+ base_name = self.settings['base_name']
570
+
571
+ if not custom_naming:
572
+ custom_naming = self.settings['custom_name']
573
+
574
+ if not custom_naming:
575
+ scan_type = self.settings['save_type'] == 'scan'
576
+ scan_path, current_scan_name, save_path = self.update_file_paths(update_h5)
577
+ self.current_scan_name = current_scan_name
578
+ self.settings.child('current_scan_name').setValue(current_scan_name)
579
+ self.settings.child('current_scan_path').setValue(str(scan_path))
580
+
581
+ if not scan_type:
582
+ self.h5_file_path = save_path.parent # will remove the dataset part used for DAQ_scan datas
583
+ self.h5_file_name = base_name + datetime_now.strftime('_%Y%m%d_%H_%M_%S.h5')
584
+ else:
585
+ self.h5_file_name = save_path.name + ".h5"
586
+ self.h5_file_path = save_path.parent
587
+
588
+ else:
589
+ self.h5_file_name = select_file(start_path=base_name, save=True, ext='h5')
590
+ self.h5_file_path = self.h5_file_name.parent
591
+
592
+ else:
593
+ if isinstance(addhoc_file_path, str):
594
+ addhoc_file_path = Path(addhoc_file_path)
595
+ self.h5_file_path = addhoc_file_path.parent
596
+ self.h5_file_name = addhoc_file_path.name
597
+
598
+ fullpathname = self.h5_file_path.joinpath(self.h5_file_name)
599
+ self.settings.child('current_h5_file').setValue(str(fullpathname))
600
+
601
+ super().init_file(fullpathname, new_file=update_h5, metadata=metadata)
602
+
603
+ self.get_set_logger(self.raw_group)
604
+
605
+ return update_h5
606
+
607
+ def update_file_paths(self, update_h5=False):
608
+ """
609
+
610
+ Parameters
611
+ ----------
612
+ update_h5: bool
613
+ if True, will increment the file name and eventually the current scan index
614
+ if False, get the current scan index in the h5 file
615
+
616
+ Returns
617
+ -------
618
+ scan_path: Path
619
+ current_filename: str
620
+ dataset_path: Path
621
+
622
+ """
623
+
624
+ try:
625
+ # set the filename and path
626
+ base_path = self.settings['base_path']
627
+ base_name = self.settings['base_name']
628
+ current_scan = self.settings['current_scan_name']
629
+ scan_type = self.settings['save_type'] == 'scan'
630
+ ind_dataset = None
631
+ if current_scan == '' or update_h5:
632
+ next_scan_index = 0
633
+ update_h5 = True # just started the main program so one should create a new h5
634
+ self.file_loaded = False
635
+ else:
636
+ next_scan_index = self.get_scan_index()
637
+ if self.file_loaded:
638
+ ind_dataset = int(os.path.splitext(self.h5_file_name)[0][-3:])
639
+ try:
640
+ curr_date = datetime.date.fromisoformat(self.get_attr(self.root(), 'date'))
641
+ except ValueError:
642
+ curr_date = parser.parse(self.get_attr(self.root(), 'date')).date()
643
+ else:
644
+ curr_date = datetime.date.today()
645
+
646
+ scan_path, current_filename, dataset_path = self.set_current_scan_path(base_path, base_name, update_h5,
647
+ next_scan_index,
648
+ create_dataset_folder=False,
649
+ curr_date=curr_date,
650
+ ind_dataset=ind_dataset)
651
+ self.settings.child('current_scan_path').setValue(str(dataset_path))
652
+
653
+ return scan_path, current_filename, dataset_path
654
+
655
+ except Exception as e:
656
+ logger.exception(str(e))
657
+
658
+ @classmethod
659
+ def find_part_in_path_and_subpath(cls, base_dir, part='', create=False, increment=True):
660
+ """
661
+ Find path from part time.
662
+
663
+ =============== ============ =============================================
664
+ **Parameters** **Type** **Description**
665
+ *base_dir* Path object The directory to browse
666
+ *part* string The date of the directory to find/create
667
+ *create* boolean Indicate the creation flag of the directory
668
+ =============== ============ =============================================
669
+
670
+ Returns
671
+ -------
672
+ Path object
673
+ found path from part
674
+ """
675
+ found_path = None
676
+ if part in base_dir.parts: # check if current year is in the given base path
677
+ if base_dir.name == part:
678
+ found_path = base_dir
679
+ else:
680
+ for ind in range(len(base_dir.parts)):
681
+ tmp_path = base_dir.parents[ind]
682
+ if tmp_path.name == part:
683
+ found_path = base_dir.parents[ind]
684
+ break
685
+ else: # if not check if year is in the subfolders
686
+ subfolders_year_name = [x.name for x in base_dir.iterdir() if x.is_dir()]
687
+ subfolders_found_path = [x for x in base_dir.iterdir() if x.is_dir()]
688
+ if part not in subfolders_year_name:
689
+ if increment:
690
+ found_path = base_dir.joinpath(part)
691
+ else:
692
+ found_path = base_dir
693
+ if create:
694
+ found_path.mkdir()
695
+ else:
696
+ ind_path = subfolders_year_name.index(part)
697
+ found_path = subfolders_found_path[ind_path]
698
+ return found_path
699
+
700
+ @classmethod
701
+ def set_current_scan_path(cls, base_dir, base_name='Scan', update_h5=False, next_scan_index=0,
702
+ create_scan_folder=False,
703
+ create_dataset_folder=True, curr_date=None, ind_dataset=None):
704
+ """
705
+
706
+ Parameters
707
+ ----------
708
+ base_dir
709
+ base_name
710
+ update_h5
711
+ next_scan_index
712
+ create_scan_folder
713
+ create_dataset_folder
714
+
715
+ Returns
716
+ -------
717
+
718
+ """
719
+ base_dir = Path(base_dir)
720
+ if curr_date is None:
721
+ curr_date = datetime.date.today()
722
+
723
+ year_path = cls.find_part_in_path_and_subpath(base_dir, part=str(curr_date.year),
724
+ create=True) # create directory of the year if it doen't exist and return it
725
+ day_path = cls.find_part_in_path_and_subpath(year_path, part=curr_date.strftime('%Y%m%d'),
726
+ create=True) # create directory of the day if it doen't exist and return it
727
+ dataset_base_name = curr_date.strftime('Dataset_%Y%m%d')
728
+ dataset_paths = sorted([path for path in day_path.glob(dataset_base_name + "*"+".h5") if path.is_file()])
729
+
730
+ if ind_dataset is None:
731
+ if dataset_paths == []:
732
+
733
+ ind_dataset = 0
734
+ else:
735
+ if update_h5:
736
+ ind_dataset = int(dataset_paths[-1].stem.partition(dataset_base_name + "_")[2]) + 1
737
+ else:
738
+ ind_dataset = int(dataset_paths[-1].stem.partition(dataset_base_name + "_")[2])
739
+
740
+ dataset_path = cls.find_part_in_path_and_subpath(day_path,
741
+ part=dataset_base_name + "_{:03d}".format(ind_dataset),
742
+ create=False, increment=True)
743
+ scan_paths = sorted([path for path in dataset_path.glob(base_name + '*') if path.is_dir()])
744
+ ind_scan = next_scan_index
745
+ return dataset_path, base_name + '{:03d}'.format(ind_scan), dataset_path
746
+
747
+ def get_last_scan(self):
748
+ """Gets the last scan node within the h5_file and under the **raw_group**
749
+
750
+ Returns
751
+ -------
752
+ scan_group: pytables group or None
753
+
754
+
755
+ """
756
+ return self.get_last_group(self.raw_group, GroupType['scan'])
757
+
758
+ def get_scan_groups(self):
759
+ return self.get_groups(self.raw_group, GroupType['scan'])
760
+
761
+ def get_scan_index(self):
762
+ """ return the scan group index in the "scan templating": Scan000, Scan001 as an integer
763
+ """
764
+
765
+ last_scan = self.get_last_scan()
766
+ return int(last_scan.name[4:]) if last_scan is not None else 0
767
+
768
+ def load_file(self, base_path=None, file_path=None):
769
+ """Opens a file dialog to select a h5file saved on disk to be used
770
+
771
+ Parameters
772
+ ----------
773
+ base_path
774
+ file_path
775
+
776
+ See Also
777
+ --------
778
+ :py:meth:`init_file`
779
+
780
+ """
781
+ if base_path is None:
782
+ base_path = self.settings.child('base_path').value()
783
+ if not os.path.isdir(base_path):
784
+ base_path = None
785
+
786
+ if file_path is None:
787
+ file_path = select_file(base_path, save=False, ext='h5')
788
+
789
+ if not (file_path is None or file_path == ''):
790
+ if not isinstance(file_path, Path):
791
+ file_path = Path(file_path)
792
+
793
+ if 'h5' not in file_path.suffix:
794
+ raise IOError('Invalid file type, should be a h5 file')
795
+
796
+ self.init_file(addhoc_file_path=file_path)
797
+ self.file_loaded = True
798
+
799
+ def save_file(self, filename=None):
800
+ if filename is None:
801
+ filename = select_file(None, save=True, ext='h5')
802
+ if filename != '':
803
+ super().save_file_as(filename)
804
+
805
+
806
+ # def add_data_live_scan(self, channel_group, data_dict, scan_type='scan1D', title='', scan_subtype=''):
807
+ # isadaptive = scan_subtype == 'Adaptive'
808
+ # if not isadaptive:
809
+ # shape, dimension, size = utils.get_data_dimension(data_dict['data'], scan_type=scan_type,
810
+ # remove_scan_dimension=True)
811
+ # else:
812
+ # shape, dimension, size = data_dict['data'].shape, '0D', 1
813
+ # data_array = self.add_array(channel_group, 'Data', 'data', array_type=np.float,
814
+ # title=title,
815
+ # data_shape=shape,
816
+ # data_dimension=dimension, scan_type=scan_type,
817
+ # scan_subtype=scan_subtype,
818
+ # array_to_save=data_dict['data'])
819
+ # if 'x_axis' in data_dict:
820
+ # if not isinstance(data_dict['x_axis'], dict):
821
+ # array_to_save = data_dict['x_axis']
822
+ # tmp_dict = dict(label='', units='')
823
+ # else:
824
+ # tmp_dict = copy.deepcopy(data_dict['x_axis'])
825
+ # array_to_save = tmp_dict.pop('data')
826
+ # self.add_array(channel_group, 'x_axis', 'axis',
827
+ # array_type=np.float, array_to_save=array_to_save,
828
+ # enlargeable=False, data_dimension='1D', metadata=tmp_dict)
829
+ # if 'y_axis' in data_dict:
830
+ # if not isinstance(data_dict['y_axis'], dict):
831
+ # array_to_save = data_dict['y_axis']
832
+ # tmp_dict = dict(label='', units='')
833
+ # else:
834
+ # tmp_dict = copy.deepcopy(data_dict['y_axis'])
835
+ # array_to_save = tmp_dict.pop('data')
836
+ # self.add_array(channel_group, 'y_axis', 'axis',
837
+ # array_type=np.float, array_to_save=array_to_save,
838
+ # enlargeable=False, data_dimension='1D', metadata=tmp_dict)
839
+ # return data_array
840
+
841
+
842
+ def value_changed(self, param):
843
+ if param.name() == 'show_file':
844
+ param.setValue(False)
845
+ self.show_file_content()
846
+
847
+ elif param.name() == 'base_path':
848
+ try:
849
+ if not os.path.isdir(param.value()):
850
+ os.mkdir(param.value())
851
+ except Exception as e:
852
+ self.update_status(f"The base path couldn't be set, please check your options: {str(e)}")
853
+
854
+ elif param.name() in putils.iter_children(self.settings.child('compression_options'), []):
855
+ compression = self.settings.child('compression_options', 'h5comp_library').value()
856
+ compression_opts = self.settings.child('compression_options', 'h5comp_level').value()
857
+ self.define_compression(compression, compression_opts)
858
+
859
+ def update_status(self, status):
860
+ logger.warning(status)
861
+
862
+ def show_file_content(self):
863
+ win = QtWidgets.QMainWindow()
864
+ if not self.isopen():
865
+ if self.h5_file_path is not None:
866
+ if self.h5_file_path.exists():
867
+ self.analysis_prog = browsing.H5Browser(win, h5file_path=self.h5_file_path)
868
+ else:
869
+ logger.warning('The h5 file path has not been defined yet')
870
+ else:
871
+ logger.warning('The h5 file path has not been defined yet')
872
+ else:
873
+ self.flush()
874
+ self.analysis_prog = browsing.H5Browser(win, h5file=self.h5file)
875
+ win.show()
876
+
877
+
878
+ class H5Saver(H5SaverBase, QObject):
879
+ """
880
+ status_sig: Signal
881
+ emits a signal of type Threadcommand in order to senf log information to a main UI
882
+ new_file_sig: Signal
883
+ emits a boolean signal to let the program know when the user pressed the new file button on the UI
884
+ """
885
+
886
+ status_sig = Signal(utils.ThreadCommand)
887
+ new_file_sig = Signal(bool)
888
+
889
+ def __init__(self, *args, **kwargs):
890
+ """
891
+
892
+ Parameters
893
+ ----------
894
+ args
895
+ kwargs
896
+ """
897
+ QObject.__init__(self)
898
+ H5SaverBase.__init__(self, *args, **kwargs)
899
+
900
+ self.settings.child('new_file').sigActivated.connect(lambda: self.emit_new_file(True))
901
+
902
+ def close(self):
903
+ self.close_file()
904
+
905
+ def emit_new_file(self, status):
906
+ """Emits the new_file_sig
907
+
908
+ Parameters
909
+ ----------
910
+ status: bool
911
+ emits True if a new file has been asked by the user pressing the new file button on the UI
912
+ """
913
+ self.new_file_sig.emit(status)
914
+