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.
- pymodaq/__init__.py +13 -6
- pymodaq/control_modules/__init__.py +0 -7
- pymodaq/control_modules/daq_move.py +965 -2
- pymodaq/control_modules/daq_move_ui.py +319 -0
- pymodaq/control_modules/daq_viewer.py +1573 -3
- pymodaq/control_modules/daq_viewer_ui.py +393 -0
- pymodaq/control_modules/mocks.py +51 -0
- pymodaq/control_modules/move_utility_classes.py +709 -8
- pymodaq/control_modules/utils.py +256 -0
- pymodaq/control_modules/viewer_utility_classes.py +663 -6
- pymodaq/daq_utils.py +89 -0
- pymodaq/dashboard.py +91 -72
- pymodaq/examples/custom_app.py +12 -11
- pymodaq/examples/custom_viewer.py +10 -10
- pymodaq/examples/function_plotter.py +16 -13
- pymodaq/examples/nonlinearscanner.py +8 -6
- pymodaq/examples/parameter_ex.py +7 -7
- pymodaq/examples/preset_MockCamera.xml +1 -0
- pymodaq/extensions/__init__.py +16 -0
- pymodaq/extensions/console.py +76 -0
- pymodaq/{daq_logger.py → extensions/daq_logger.py} +115 -65
- pymodaq/extensions/daq_scan.py +1339 -0
- pymodaq/extensions/daq_scan_ui.py +240 -0
- pymodaq/extensions/h5browser.py +23 -0
- pymodaq/{pid → extensions/pid}/__init__.py +4 -2
- pymodaq/{pid → extensions/pid}/daq_move_PID.py +2 -2
- pymodaq/{pid → extensions/pid}/pid_controller.py +48 -36
- pymodaq/{pid → extensions/pid}/utils.py +52 -6
- pymodaq/extensions/utils.py +40 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/{daq_analysis → post_treatment/daq_analysis}/daq_analysis_main.py +17 -17
- pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_main.py +8 -14
- pymodaq/post_treatment/load_and_plot.py +219 -0
- pymodaq/post_treatment/process_to_scalar.py +263 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/run_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/Icon_Library/stop_all.png +0 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat +1 -1
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc +1 -0
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py +109784 -109173
- pymodaq/resources/QtDesigner_Ressources/icons.svg +142 -0
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +32 -13
- pymodaq/resources/preset_default.xml +1 -1
- pymodaq/{daq_utils → utils}/Tuto innosetup/script_full_setup.iss +1 -1
- pymodaq/utils/__init__.py +0 -29
- pymodaq/utils/abstract/__init__.py +48 -0
- pymodaq/{daq_utils → utils}/abstract/logger.py +7 -3
- pymodaq/utils/array_manipulation.py +379 -8
- pymodaq/{daq_utils → utils}/calibration_camera.py +6 -6
- pymodaq/{daq_utils → utils}/chrono_timer.py +1 -1
- pymodaq/utils/config.py +448 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +828 -8
- pymodaq/utils/data.py +1873 -7
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger.py +86 -47
- pymodaq/{daq_utils → utils}/db/db_logger/db_logger_models.py +31 -10
- pymodaq/{daq_utils → utils}/enums.py +12 -7
- pymodaq/utils/exceptions.py +37 -0
- pymodaq/utils/factory.py +82 -0
- pymodaq/{daq_utils → utils}/gui_utils/__init__.py +1 -1
- pymodaq/utils/gui_utils/custom_app.py +129 -0
- pymodaq/utils/gui_utils/file_io.py +66 -0
- pymodaq/{daq_utils → utils}/gui_utils/layout.py +2 -2
- pymodaq/{daq_utils → utils}/gui_utils/utils.py +13 -3
- pymodaq/{daq_utils → utils}/gui_utils/widgets/__init__.py +2 -2
- pymodaq/utils/gui_utils/widgets/label.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/lcd.py +12 -7
- pymodaq/{daq_utils → utils}/gui_utils/widgets/push.py +66 -2
- pymodaq/{daq_utils → utils}/gui_utils/widgets/qled.py +6 -4
- pymodaq/utils/gui_utils/widgets/spinbox.py +24 -0
- pymodaq/{daq_utils → utils}/gui_utils/widgets/table.py +2 -2
- pymodaq/utils/h5modules/__init__.py +1 -0
- pymodaq/{daq_utils/h5backend.py → utils/h5modules/backends.py} +200 -112
- pymodaq/utils/h5modules/browsing.py +683 -0
- pymodaq/utils/h5modules/data_saving.py +839 -0
- pymodaq/utils/h5modules/h5logging.py +110 -0
- pymodaq/utils/h5modules/module_saving.py +350 -0
- pymodaq/utils/h5modules/saving.py +914 -0
- pymodaq/utils/h5modules/utils.py +85 -0
- pymodaq/utils/logger.py +64 -6
- pymodaq/utils/managers/action_manager.py +460 -0
- pymodaq/{daq_utils → utils}/managers/batchscan_manager.py +144 -112
- pymodaq/{daq_utils → utils}/managers/modules_manager.py +188 -114
- pymodaq/{daq_utils → utils}/managers/overshoot_manager.py +3 -3
- pymodaq/utils/managers/parameter_manager.py +110 -0
- pymodaq/{daq_utils → utils}/managers/preset_manager.py +17 -13
- pymodaq/{daq_utils → utils}/managers/preset_manager_utils.py +8 -7
- pymodaq/{daq_utils → utils}/managers/remote_manager.py +7 -6
- pymodaq/{daq_utils → utils}/managers/roi_manager.py +148 -57
- pymodaq/utils/math_utils.py +546 -10
- pymodaq/{daq_utils → utils}/messenger.py +5 -1
- pymodaq/utils/parameter/__init__.py +2 -15
- pymodaq/{daq_utils → utils}/parameter/ioxml.py +12 -6
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/__init__.py +1 -3
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/filedir.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/itemselect.py +3 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/led.py +1 -1
- pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py +161 -0
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/slide.py +1 -1
- pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/table.py +1 -1
- pymodaq/utils/parameter/utils.py +206 -11
- pymodaq/utils/plotting/data_viewers/__init__.py +6 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +393 -0
- pymodaq/utils/plotting/data_viewers/viewer0D.py +251 -0
- pymodaq/utils/plotting/data_viewers/viewer1D.py +574 -0
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer1Dbasic.py +8 -3
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D.py +292 -357
- pymodaq/{daq_utils → utils}/plotting/data_viewers/viewer2D_basic.py +58 -75
- pymodaq/utils/plotting/data_viewers/viewerND.py +738 -0
- pymodaq/{daq_utils → utils}/plotting/gant_chart.py +2 -2
- pymodaq/{daq_utils → utils}/plotting/items/axis_scaled.py +4 -2
- pymodaq/{daq_utils → utils}/plotting/items/image.py +8 -6
- pymodaq/utils/plotting/navigator.py +355 -0
- pymodaq/utils/plotting/scan_selector.py +480 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +88 -0
- pymodaq/utils/plotting/utils/filter.py +538 -0
- pymodaq/utils/plotting/utils/lineout.py +224 -0
- pymodaq/{daq_utils → utils}/plotting/utils/plot_utils.py +196 -84
- pymodaq/{daq_utils → utils}/plotting/utils/signalND.py +21 -13
- pymodaq/utils/plotting/widgets.py +76 -0
- pymodaq/utils/scanner/__init__.py +10 -0
- pymodaq/utils/scanner/scan_factory.py +204 -0
- pymodaq/utils/scanner/scanner.py +271 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +117 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +293 -0
- pymodaq/utils/scanner/scanners/sequential.py +192 -0
- pymodaq/utils/scanner/scanners/tabular.py +294 -0
- pymodaq/utils/scanner/utils.py +83 -0
- pymodaq/utils/slicing.py +47 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +51 -0
- pymodaq/{daq_utils → utils}/tcp_server_client.py +36 -37
- pymodaq/{daq_utils → utils}/tree_layout/tree_layout_main.py +50 -35
- pymodaq/utils/units.py +216 -0
- pymodaq-4.0.1.dist-info/METADATA +159 -0
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/RECORD +167 -170
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info}/WHEEL +1 -2
- pymodaq-4.0.1.dist-info/entry_points.txt +8 -0
- pymodaq/daq_move/daq_move_gui.py +0 -279
- pymodaq/daq_move/daq_move_gui.ui +0 -534
- pymodaq/daq_move/daq_move_main.py +0 -1042
- pymodaq/daq_move/process_from_QtDesigner_DAQ_Move_GUI.bat +0 -2
- pymodaq/daq_move/utility_classes.py +0 -671
- pymodaq/daq_scan.py +0 -2160
- pymodaq/daq_utils/array_manipulation.py +0 -386
- pymodaq/daq_utils/config.py +0 -273
- pymodaq/daq_utils/conftests.py +0 -7
- pymodaq/daq_utils/custom_parameter_tree.py +0 -9
- pymodaq/daq_utils/daq_enums.py +0 -133
- pymodaq/daq_utils/daq_utils.py +0 -1402
- pymodaq/daq_utils/exceptions.py +0 -71
- pymodaq/daq_utils/gui_utils/custom_app.py +0 -103
- pymodaq/daq_utils/gui_utils/file_io.py +0 -75
- pymodaq/daq_utils/gui_utils/widgets/spinbox.py +0 -9
- pymodaq/daq_utils/h5exporter_hyperspy.py +0 -115
- pymodaq/daq_utils/h5exporters.py +0 -242
- pymodaq/daq_utils/h5modules.py +0 -1559
- pymodaq/daq_utils/h5utils.py +0 -241
- pymodaq/daq_utils/managers/action_manager.py +0 -236
- pymodaq/daq_utils/managers/parameter_manager.py +0 -57
- pymodaq/daq_utils/math_utils.py +0 -705
- pymodaq/daq_utils/parameter/__init__.py +0 -1
- pymodaq/daq_utils/parameter/oldpymodaq_ptypes.py +0 -1626
- pymodaq/daq_utils/parameter/pymodaq_ptypes/pixmap.py +0 -85
- pymodaq/daq_utils/parameter/utils.py +0 -136
- pymodaq/daq_utils/plotting/data_viewers/__init__.py +0 -0
- pymodaq/daq_utils/plotting/data_viewers/process_from_QtDesigner_0DViewer_GUI.bat +0 -2
- pymodaq/daq_utils/plotting/data_viewers/viewer0D.py +0 -204
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.py +0 -89
- pymodaq/daq_utils/plotting/data_viewers/viewer0D_GUI.ui +0 -131
- pymodaq/daq_utils/plotting/data_viewers/viewer1D.py +0 -781
- pymodaq/daq_utils/plotting/data_viewers/viewerND.py +0 -894
- pymodaq/daq_utils/plotting/data_viewers/viewerbase.py +0 -64
- pymodaq/daq_utils/plotting/items/__init__.py +0 -0
- pymodaq/daq_utils/plotting/navigator.py +0 -500
- pymodaq/daq_utils/plotting/scan_selector.py +0 -289
- pymodaq/daq_utils/plotting/utils/__init__.py +0 -0
- pymodaq/daq_utils/plotting/utils/filter.py +0 -236
- pymodaq/daq_utils/plotting/viewer0D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer0D/viewer0D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewer1D/viewer1D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewer1D/viewer1Dbasic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_basic.py +0 -4
- pymodaq/daq_utils/plotting/viewer2D/viewer_2D_main.py +0 -4
- pymodaq/daq_utils/plotting/viewerND/__init__.py +0 -0
- pymodaq/daq_utils/plotting/viewerND/viewerND_main.py +0 -4
- pymodaq/daq_utils/scanner.py +0 -1289
- pymodaq/daq_utils/tree_layout/__init__.py +0 -0
- pymodaq/daq_viewer/__init__.py +0 -0
- pymodaq/daq_viewer/daq_gui_settings.py +0 -237
- pymodaq/daq_viewer/daq_gui_settings.ui +0 -441
- pymodaq/daq_viewer/daq_viewer_main.py +0 -2225
- pymodaq/daq_viewer/process_from_QtDesigner_DAQ_GUI_settings.bat +0 -2
- pymodaq/daq_viewer/utility_classes.py +0 -673
- pymodaq/examples/logger_image/__init__.py +0 -0
- pymodaq/examples/logger_image/logger_displayer.py +0 -121
- pymodaq/examples/logger_image/setup.svg +0 -3119
- pymodaq/examples/logger_image/setup_svg.py +0 -114
- pymodaq/h5browser.py +0 -39
- pymodaq/utils/scanner.py +0 -15
- pymodaq-3.6.12.dist-info/METADATA +0 -39
- pymodaq-3.6.12.dist-info/entry_points.txt +0 -8
- pymodaq-3.6.12.dist-info/top_level.txt +0 -1
- /pymodaq/{daq_analysis → post_treatment/daq_analysis}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/__init__.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.py +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/daq_measurement_GUI.ui +0 -0
- /pymodaq/{daq_measurement → post_treatment/daq_measurement}/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.odt +0 -0
- /pymodaq/{daq_utils → utils}/Tuto innosetup/Tuto innosetup.pdf +0 -0
- /pymodaq/{daq_move → utils/db}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils/db/db_logger}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/dock.py +0 -0
- /pymodaq/{daq_utils → utils}/gui_utils/list_picker.py +0 -0
- /pymodaq/{daq_utils/abstract → utils/managers}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/bool.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/date.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/list.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/numeric.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/tableview.py +0 -0
- /pymodaq/{daq_utils → utils}/parameter/pymodaq_ptypes/text.py +0 -0
- /pymodaq/{daq_utils/db → utils/plotting}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/image_viewer.py +0 -0
- /pymodaq/{daq_utils/db/db_logger → utils/plotting/items}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/plotting/items/crosshair.py +0 -0
- /pymodaq/{daq_utils/managers → utils/plotting/utils}/__init__.py +0 -0
- /pymodaq/{daq_utils → utils}/qvariant.py +0 -0
- /pymodaq/{daq_utils/plotting/viewer2D → utils/scanner/scanners}/__init__.py +0 -0
- /pymodaq/{daq_utils/plotting → utils/tree_layout}/__init__.py +0 -0
- {pymodaq-3.6.12.dist-info → pymodaq-4.0.1.dist-info/licenses}/LICENSE +0 -0
pymodaq/daq_utils/h5modules.py
DELETED
|
@@ -1,1559 +0,0 @@
|
|
|
1
|
-
# Standard imports
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
from collections import OrderedDict
|
|
5
|
-
import warnings
|
|
6
|
-
import logging
|
|
7
|
-
import copy
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
import importlib
|
|
10
|
-
|
|
11
|
-
# 3rd party imports
|
|
12
|
-
from qtpy import QtGui, QtCore, QtWidgets
|
|
13
|
-
from qtpy.QtCore import Qt, QObject, Signal, QByteArray
|
|
14
|
-
import numpy as np
|
|
15
|
-
import datetime
|
|
16
|
-
|
|
17
|
-
# Project imports
|
|
18
|
-
import pymodaq.daq_utils.parameter.ioxml
|
|
19
|
-
from pyqtgraph.parametertree import Parameter, ParameterTree
|
|
20
|
-
from pymodaq.daq_utils.config import Config
|
|
21
|
-
from pymodaq.daq_utils.parameter import utils as putils
|
|
22
|
-
from pymodaq.daq_utils.tree_layout.tree_layout_main import Tree_layout
|
|
23
|
-
from pymodaq.daq_utils.gui_utils.utils import h5tree_to_QTree, pngbinary2Qlabel
|
|
24
|
-
from pymodaq.daq_utils.gui_utils.file_io import select_file
|
|
25
|
-
from pymodaq.daq_utils.gui_utils.dock import DockArea
|
|
26
|
-
from pymodaq.daq_utils.gui_utils.utils import dashboard_submodules_params
|
|
27
|
-
from pymodaq.daq_utils.plotting.data_viewers.viewerND import ViewerND
|
|
28
|
-
from pymodaq.daq_utils.abstract.logger import AbstractLogger
|
|
29
|
-
from pymodaq.daq_utils.h5backend import H5Backend, backends_available, Node
|
|
30
|
-
from pymodaq.daq_utils.h5exporters import ExporterFactory
|
|
31
|
-
from pymodaq.daq_utils.h5utils import get_h5_data_from_node
|
|
32
|
-
from pymodaq.daq_utils.exceptions import InvalidSave, InvalidGroupDataType, InvalidDataDimension, \
|
|
33
|
-
InvalidDataType, InvalidScanType, InvalidGroupType
|
|
34
|
-
|
|
35
|
-
from pymodaq.daq_utils import daq_utils as utils
|
|
36
|
-
from pymodaq.daq_utils.scanner import SCAN_TYPES as stypes
|
|
37
|
-
|
|
38
|
-
from dateutil import parser
|
|
39
|
-
from packaging import version as version_mod
|
|
40
|
-
|
|
41
|
-
config = Config()
|
|
42
|
-
|
|
43
|
-
logger = utils.set_logger(utils.get_module_name(__file__))
|
|
44
|
-
|
|
45
|
-
version = '0.0.1'
|
|
46
|
-
save_types = ['scan', 'detector', 'logger', 'custom']
|
|
47
|
-
group_types = ['raw_datas', 'scan', 'detector', 'move', 'data', 'ch', '', 'external_h5']
|
|
48
|
-
group_data_types = ['data0D', 'data1D', 'data2D', 'dataND']
|
|
49
|
-
data_types = ['data', 'axis', 'live_scan', 'navigation_axis', 'external_h5', 'strings', 'bkg']
|
|
50
|
-
data_dimensions = ['0D', '1D', '2D', 'ND']
|
|
51
|
-
scan_types = ['']
|
|
52
|
-
scan_types.extend(stypes)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class H5LogHandler(logging.StreamHandler):
|
|
56
|
-
def __init__(self, h5saver):
|
|
57
|
-
super().__init__()
|
|
58
|
-
self.h5saver = h5saver
|
|
59
|
-
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
60
|
-
self.setFormatter(formatter)
|
|
61
|
-
|
|
62
|
-
def emit(self, record):
|
|
63
|
-
msg = self.format(record)
|
|
64
|
-
self.h5saver.add_log(msg)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class H5SaverBase(H5Backend):
|
|
68
|
-
"""Object containing all methods in order to save datas in a *hdf5 file* with a hierachy compatible with the
|
|
69
|
-
H5Browser. The saving parameters are contained within a **Parameter** object: self.settings that can be displayed
|
|
70
|
-
on a UI using the widget self.settings_tree. At the creation of a new file, a node group named **Raw_datas**
|
|
71
|
-
and represented by the attribute ``raw_group`` is created and set with a metadata attribute:
|
|
72
|
-
|
|
73
|
-
* 'type' given by the **save_type** class parameter
|
|
74
|
-
|
|
75
|
-
The root group of the file is then set with a few metadata:
|
|
76
|
-
|
|
77
|
-
* 'pymodaq_version' the current pymodaq version, e.g. 1.6.2
|
|
78
|
-
* 'file' the file name
|
|
79
|
-
* 'date' the current date
|
|
80
|
-
* 'time' the current time
|
|
81
|
-
|
|
82
|
-
All datas will then be saved under this node in various groups
|
|
83
|
-
|
|
84
|
-
See Also
|
|
85
|
-
--------
|
|
86
|
-
H5Browser
|
|
87
|
-
|
|
88
|
-
Parameters
|
|
89
|
-
----------
|
|
90
|
-
h5_file: pytables hdf5 file
|
|
91
|
-
object used to save all datas and metadas
|
|
92
|
-
h5_file_path: str or Path
|
|
93
|
-
Signal signal represented by a float. Is emitted each time the hardware reached the target
|
|
94
|
-
position within the epsilon precision (see comon_parameters variable)
|
|
95
|
-
save_type: str
|
|
96
|
-
an element of the list module attribute save_types = ['scan', 'detector', 'custom']
|
|
97
|
-
* 'scan' is used for DAQ_Scan module and should be used for similar application
|
|
98
|
-
* 'detector' is used for DAQ_Viewer module and should be used for similar application
|
|
99
|
-
* 'custom' should be used for customized applications
|
|
100
|
-
|
|
101
|
-
Attributes
|
|
102
|
-
----------
|
|
103
|
-
|
|
104
|
-
settings: Parameter
|
|
105
|
-
Parameter instance (pyqtgraph) containing all settings (can be represented with the settings_tree widget)
|
|
106
|
-
|
|
107
|
-
settings_tree: ParameterTree
|
|
108
|
-
Widget representing a Tree structure, all settings are defined in the class variable ``params``
|
|
109
|
-
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
params = [
|
|
113
|
-
{'title': 'Save type:', 'name': 'save_type', 'type': 'list', 'limits': save_types, 'readonly': True},
|
|
114
|
-
] + dashboard_submodules_params + \
|
|
115
|
-
[{'title': 'Backend:', 'name': 'backend', 'type': 'group', 'children': [
|
|
116
|
-
{'title': 'Backend type:', 'name': 'backend_type', 'type': 'list', 'limits': backends_available,
|
|
117
|
-
'readonly': True},
|
|
118
|
-
{'title': 'HSDS Server:', 'name': 'hsds_options', 'type': 'group', 'visible': False, 'children': [
|
|
119
|
-
{'title': 'Endpoint:', 'name': 'endpoint', 'type': 'str',
|
|
120
|
-
'value': config('data_saving', 'hsds', 'root_url'), 'readonly': False},
|
|
121
|
-
{'title': 'User:', 'name': 'user', 'type': 'str',
|
|
122
|
-
'value': config('data_saving', 'hsds', 'username'), 'readonly': False},
|
|
123
|
-
{'title': 'password:', 'name': 'password', 'type': 'str',
|
|
124
|
-
'value': config('data_saving', 'hsds', 'pwd'), 'readonly': False},
|
|
125
|
-
]},
|
|
126
|
-
]},
|
|
127
|
-
|
|
128
|
-
{'title': 'custom_name?:', 'name': 'custom_name', 'type': 'bool', 'default': False, 'value': False},
|
|
129
|
-
{'title': 'show file content?', 'name': 'show_file', 'type': 'bool_push', 'default': False,
|
|
130
|
-
'value': False},
|
|
131
|
-
{'title': 'Base path:', 'name': 'base_path', 'type': 'browsepath',
|
|
132
|
-
'value': config('data_saving', 'h5file', 'save_path'), 'filetype': False, 'readonly': True, },
|
|
133
|
-
{'title': 'Base name:', 'name': 'base_name', 'type': 'str', 'value': 'Scan', 'readonly': True},
|
|
134
|
-
{'title': 'Current scan:', 'name': 'current_scan_name', 'type': 'str', 'value': '', 'readonly': True},
|
|
135
|
-
{'title': 'Current path:', 'name': 'current_scan_path', 'type': 'text',
|
|
136
|
-
'value': config('data_saving', 'h5file', 'save_path'), 'readonly': True, 'visible': False},
|
|
137
|
-
{'title': 'h5file:', 'name': 'current_h5_file', 'type': 'text', 'value': '', 'readonly': True},
|
|
138
|
-
{'title': 'New file', 'name': 'new_file', 'type': 'action'},
|
|
139
|
-
{'title': 'Saving dynamic', 'name': 'dynamic', 'type': 'list',
|
|
140
|
-
'limits': config('data_saving', 'data_type', 'dynamics'),
|
|
141
|
-
'value': config('data_saving', 'data_type', 'dynamic')},
|
|
142
|
-
{'title': 'Compression options:', 'name': 'compression_options', 'type': 'group', 'children': [
|
|
143
|
-
{'title': 'Compression library:', 'name': 'h5comp_library', 'type': 'list', 'value': 'zlib',
|
|
144
|
-
'limits': ['zlib', 'gzip']},
|
|
145
|
-
{'title': 'Compression level:', 'name': 'h5comp_level', 'type': 'int',
|
|
146
|
-
'value': config('data_saving', 'h5file', 'compression_level'), 'min': 0, 'max': 9},
|
|
147
|
-
]},
|
|
148
|
-
]
|
|
149
|
-
|
|
150
|
-
def __init__(self, save_type='scan', backend='tables'):
|
|
151
|
-
"""
|
|
152
|
-
|
|
153
|
-
Parameters
|
|
154
|
-
----------
|
|
155
|
-
save_type (str): one of ['scan', 'detector', 'logger', 'custom']
|
|
156
|
-
backend (str): either 'tables' for pytables backend, 'h5py' for h5py backends or 'h5pyd' for HSDS backend
|
|
157
|
-
|
|
158
|
-
See Also
|
|
159
|
-
--------
|
|
160
|
-
https://github.com/HDFGroup/hsds
|
|
161
|
-
"""
|
|
162
|
-
H5Backend.__init__(self, backend)
|
|
163
|
-
if save_type not in save_types:
|
|
164
|
-
raise InvalidSave('Invalid saving type')
|
|
165
|
-
|
|
166
|
-
self.h5_file_path = None
|
|
167
|
-
self.h5_file_name = None
|
|
168
|
-
self.logger_array = None
|
|
169
|
-
self.file_loaded = False
|
|
170
|
-
|
|
171
|
-
self.current_group = None
|
|
172
|
-
self.current_scan_group = None
|
|
173
|
-
self.current_scan_name = None
|
|
174
|
-
self.raw_group = None
|
|
175
|
-
|
|
176
|
-
self.settings = Parameter.create(title='Saving settings', name='save_settings', type='group',
|
|
177
|
-
children=self.params)
|
|
178
|
-
self.settings.child(('save_type')).setValue(save_type)
|
|
179
|
-
|
|
180
|
-
# self.settings.child('saving_options', 'save_independent').show(save_type == 'scan')
|
|
181
|
-
# self.settings.child('saving_options', 'do_save').show(not save_type == 'scan')
|
|
182
|
-
# self.settings.child('saving_options', 'current_scan_name').show(save_type == 'scan')
|
|
183
|
-
|
|
184
|
-
self.settings.sigTreeStateChanged.connect(
|
|
185
|
-
self.parameter_tree_changed) # any changes on the settings will update accordingly the detector
|
|
186
|
-
|
|
187
|
-
@property
|
|
188
|
-
def h5_file(self):
|
|
189
|
-
return self._h5file
|
|
190
|
-
|
|
191
|
-
def init_file(self, update_h5=False, custom_naming=False, addhoc_file_path=None, metadata=dict([]),
|
|
192
|
-
raw_group_name='Raw_datas'):
|
|
193
|
-
"""Initializes a new h5 file.
|
|
194
|
-
Could set the h5_file attributes as:
|
|
195
|
-
|
|
196
|
-
* a file with a name following a template if ``custom_naming`` is ``False`` and ``addhoc_file_path`` is ``None``
|
|
197
|
-
* a file within a name set using a file dialog popup if ``custom_naming`` is ``True``
|
|
198
|
-
* a file with a custom name if ``addhoc_file_path`` is a ``Path`` object or a path string
|
|
199
|
-
|
|
200
|
-
Parameters
|
|
201
|
-
----------
|
|
202
|
-
update_h5: bool
|
|
203
|
-
create a new h5 file with name specified by other parameters
|
|
204
|
-
if false try to open an existing file and will append new data to it
|
|
205
|
-
custom_naming: bool
|
|
206
|
-
if True, a selection file dialog opens to set a new file name
|
|
207
|
-
addhoc_file_path: Path or str
|
|
208
|
-
supplied name by the user for the new file
|
|
209
|
-
metadata: dict
|
|
210
|
-
dictionnary with pair of key, value that should be saved as attributes of the root group
|
|
211
|
-
Returns
|
|
212
|
-
-------
|
|
213
|
-
update_h5: bool
|
|
214
|
-
True if new file has been created, False otherwise
|
|
215
|
-
"""
|
|
216
|
-
datetime_now = datetime.datetime.now()
|
|
217
|
-
|
|
218
|
-
if addhoc_file_path is None:
|
|
219
|
-
if not os.path.isdir(self.settings.child('base_path').value()):
|
|
220
|
-
os.mkdir(self.settings.child('base_path').value())
|
|
221
|
-
|
|
222
|
-
# set the filename and path
|
|
223
|
-
base_name = self.settings.child('base_name').value()
|
|
224
|
-
|
|
225
|
-
if not custom_naming:
|
|
226
|
-
custom_naming = self.settings.child('custom_name').value()
|
|
227
|
-
|
|
228
|
-
if not custom_naming:
|
|
229
|
-
scan_type = self.settings.child('save_type').value() == 'scan'
|
|
230
|
-
scan_path, current_scan_name, save_path = self.update_file_paths(update_h5)
|
|
231
|
-
self.current_scan_name = current_scan_name
|
|
232
|
-
self.settings.child('current_scan_name').setValue(current_scan_name)
|
|
233
|
-
self.settings.child('current_scan_path').setValue(str(scan_path))
|
|
234
|
-
|
|
235
|
-
if not scan_type:
|
|
236
|
-
self.h5_file_path = save_path.parent # will remove the dataset part used for DAQ_scan datas
|
|
237
|
-
self.h5_file_name = base_name + datetime_now.strftime('_%Y%m%d_%H_%M_%S.h5')
|
|
238
|
-
else:
|
|
239
|
-
self.h5_file_name = save_path.name + ".h5"
|
|
240
|
-
self.h5_file_path = save_path.parent
|
|
241
|
-
|
|
242
|
-
else:
|
|
243
|
-
self.h5_file_name = select_file(start_path=base_name, save=True, ext='h5')
|
|
244
|
-
self.h5_file_path = self.h5_file_name.parent
|
|
245
|
-
|
|
246
|
-
else:
|
|
247
|
-
if isinstance(addhoc_file_path, str):
|
|
248
|
-
addhoc_file_path = Path(addhoc_file_path)
|
|
249
|
-
self.h5_file_path = addhoc_file_path.parent
|
|
250
|
-
self.h5_file_name = addhoc_file_path.name
|
|
251
|
-
|
|
252
|
-
fullpathname = str(self.h5_file_path.joinpath(self.h5_file_name))
|
|
253
|
-
self.settings.child('current_h5_file').setValue(fullpathname)
|
|
254
|
-
|
|
255
|
-
if update_h5:
|
|
256
|
-
self.current_scan_group = None
|
|
257
|
-
|
|
258
|
-
scan_group = None
|
|
259
|
-
if self.current_scan_group is not None:
|
|
260
|
-
scan_group = self.get_node_name(self.current_scan_group)
|
|
261
|
-
|
|
262
|
-
if update_h5:
|
|
263
|
-
self.close_file()
|
|
264
|
-
self.open_file(fullpathname, 'w', title='PyMoDAQ file')
|
|
265
|
-
|
|
266
|
-
else:
|
|
267
|
-
self.close_file()
|
|
268
|
-
self.open_file(fullpathname, 'a', title='PyMoDAQ file')
|
|
269
|
-
|
|
270
|
-
self.raw_group = self.get_set_group(self.root(), raw_group_name, title='Data from PyMoDAQ modules')
|
|
271
|
-
self.get_set_logger(self.raw_group)
|
|
272
|
-
|
|
273
|
-
if scan_group is not None:
|
|
274
|
-
self.current_scan_group = self.get_set_group(self.raw_group, scan_group)
|
|
275
|
-
else:
|
|
276
|
-
self.current_scan_group = self.get_last_scan()
|
|
277
|
-
|
|
278
|
-
self.raw_group.attrs['type'] = self.settings.child(
|
|
279
|
-
('save_type')).value() # first possibility to set a node attribute
|
|
280
|
-
self.root().set_attr('file', self.h5_file_name) # second possibility
|
|
281
|
-
if update_h5:
|
|
282
|
-
self.set_attr(self.root(), 'date', datetime_now.date().isoformat())
|
|
283
|
-
self.set_attr(self.root(), 'time', datetime_now.time().isoformat())
|
|
284
|
-
for metadat in metadata:
|
|
285
|
-
self.raw_group.attrs[metadat] = metadata[metadat]
|
|
286
|
-
return update_h5
|
|
287
|
-
|
|
288
|
-
def add_scan_group(self, title='', settings_as_xml='', metadata=dict([])):
|
|
289
|
-
"""
|
|
290
|
-
Add a new group of type scan
|
|
291
|
-
See Also
|
|
292
|
-
-------
|
|
293
|
-
add_incremental_group
|
|
294
|
-
"""
|
|
295
|
-
if self.current_scan_group is not None:
|
|
296
|
-
if len(self.get_children(self.current_scan_group)) == 0:
|
|
297
|
-
new_scan = False
|
|
298
|
-
else:
|
|
299
|
-
new_scan = True
|
|
300
|
-
else:
|
|
301
|
-
new_scan = True
|
|
302
|
-
if new_scan:
|
|
303
|
-
self.current_scan_group = self.add_incremental_group('scan', self.raw_group, title, settings_as_xml,
|
|
304
|
-
metadata)
|
|
305
|
-
self.set_attr(self.current_scan_group, 'description', '')
|
|
306
|
-
self.settings.child(('current_scan_name')).setValue(self.get_node_name(self.current_scan_group))
|
|
307
|
-
|
|
308
|
-
return self.current_scan_group
|
|
309
|
-
|
|
310
|
-
def update_file_paths(self, update_h5=False):
|
|
311
|
-
"""
|
|
312
|
-
|
|
313
|
-
Parameters
|
|
314
|
-
----------
|
|
315
|
-
update_h5: bool
|
|
316
|
-
if True, will increment the file name and eventually the current scan index
|
|
317
|
-
if False, get the current scan index in the h5 file
|
|
318
|
-
|
|
319
|
-
Returns
|
|
320
|
-
-------
|
|
321
|
-
scan_path: Path
|
|
322
|
-
current_filename: str
|
|
323
|
-
dataset_path: Path
|
|
324
|
-
|
|
325
|
-
"""
|
|
326
|
-
|
|
327
|
-
try:
|
|
328
|
-
# set the filename and path
|
|
329
|
-
base_path = self.settings.child(('base_path')).value()
|
|
330
|
-
base_name = self.settings.child(('base_name')).value()
|
|
331
|
-
current_scan = self.settings.child(('current_scan_name')).value()
|
|
332
|
-
scan_type = self.settings.child(('save_type')).value() == 'scan'
|
|
333
|
-
ind_dataset = None
|
|
334
|
-
if current_scan == '' or update_h5:
|
|
335
|
-
next_scan_index = 0
|
|
336
|
-
update_h5 = True # just started the main program so one should create a new h5
|
|
337
|
-
self.file_loaded = False
|
|
338
|
-
else:
|
|
339
|
-
next_scan_index = self.get_scan_index()
|
|
340
|
-
if self.file_loaded:
|
|
341
|
-
ind_dataset = int(os.path.splitext(self.h5_file_name)[0][-3:])
|
|
342
|
-
try:
|
|
343
|
-
curr_date = datetime.date.fromisoformat(self.get_attr(self.root(), 'date'))
|
|
344
|
-
except ValueError:
|
|
345
|
-
curr_date = parser.parse(self.get_attr(self.root(), 'date')).date()
|
|
346
|
-
else:
|
|
347
|
-
curr_date = datetime.date.today()
|
|
348
|
-
|
|
349
|
-
scan_path, current_filename, dataset_path = self.set_current_scan_path(base_path, base_name, update_h5,
|
|
350
|
-
next_scan_index,
|
|
351
|
-
create_dataset_folder=False,
|
|
352
|
-
curr_date=curr_date,
|
|
353
|
-
ind_dataset=ind_dataset)
|
|
354
|
-
self.settings.child(('current_scan_path')).setValue(str(dataset_path))
|
|
355
|
-
|
|
356
|
-
return scan_path, current_filename, dataset_path
|
|
357
|
-
|
|
358
|
-
except Exception as e:
|
|
359
|
-
logger.exception(str(e))
|
|
360
|
-
|
|
361
|
-
@classmethod
|
|
362
|
-
def find_part_in_path_and_subpath(cls, base_dir, part='', create=False, increment=True):
|
|
363
|
-
"""
|
|
364
|
-
Find path from part time.
|
|
365
|
-
|
|
366
|
-
=============== ============ =============================================
|
|
367
|
-
**Parameters** **Type** **Description**
|
|
368
|
-
*base_dir* Path object The directory to browse
|
|
369
|
-
*part* string The date of the directory to find/create
|
|
370
|
-
*create* boolean Indicate the creation flag of the directory
|
|
371
|
-
=============== ============ =============================================
|
|
372
|
-
|
|
373
|
-
Returns
|
|
374
|
-
-------
|
|
375
|
-
Path object
|
|
376
|
-
found path from part
|
|
377
|
-
"""
|
|
378
|
-
found_path = None
|
|
379
|
-
if part in base_dir.parts: # check if current year is in the given base path
|
|
380
|
-
if base_dir.name == part:
|
|
381
|
-
found_path = base_dir
|
|
382
|
-
else:
|
|
383
|
-
for ind in range(len(base_dir.parts)):
|
|
384
|
-
tmp_path = base_dir.parents[ind]
|
|
385
|
-
if tmp_path.name == part:
|
|
386
|
-
found_path = base_dir.parents[ind]
|
|
387
|
-
break
|
|
388
|
-
else: # if not check if year is in the subfolders
|
|
389
|
-
subfolders_year_name = [x.name for x in base_dir.iterdir() if x.is_dir()]
|
|
390
|
-
subfolders_found_path = [x for x in base_dir.iterdir() if x.is_dir()]
|
|
391
|
-
if part not in subfolders_year_name:
|
|
392
|
-
if increment:
|
|
393
|
-
found_path = base_dir.joinpath(part)
|
|
394
|
-
else:
|
|
395
|
-
found_path = base_dir
|
|
396
|
-
if create:
|
|
397
|
-
found_path.mkdir()
|
|
398
|
-
else:
|
|
399
|
-
ind_path = subfolders_year_name.index(part)
|
|
400
|
-
found_path = subfolders_found_path[ind_path]
|
|
401
|
-
return found_path
|
|
402
|
-
|
|
403
|
-
@classmethod
|
|
404
|
-
def set_current_scan_path(cls, base_dir, base_name='Scan', update_h5=False, next_scan_index=0,
|
|
405
|
-
create_scan_folder=False,
|
|
406
|
-
create_dataset_folder=True, curr_date=None, ind_dataset=None):
|
|
407
|
-
"""
|
|
408
|
-
|
|
409
|
-
Parameters
|
|
410
|
-
----------
|
|
411
|
-
base_dir
|
|
412
|
-
base_name
|
|
413
|
-
update_h5
|
|
414
|
-
next_scan_index
|
|
415
|
-
create_scan_folder
|
|
416
|
-
create_dataset_folder
|
|
417
|
-
|
|
418
|
-
Returns
|
|
419
|
-
-------
|
|
420
|
-
|
|
421
|
-
"""
|
|
422
|
-
base_dir = Path(base_dir)
|
|
423
|
-
if curr_date is None:
|
|
424
|
-
curr_date = datetime.date.today()
|
|
425
|
-
|
|
426
|
-
year_path = cls.find_part_in_path_and_subpath(base_dir, part=str(curr_date.year),
|
|
427
|
-
create=True) # create directory of the year if it doen't exist and return it
|
|
428
|
-
day_path = cls.find_part_in_path_and_subpath(year_path, part=curr_date.strftime('%Y%m%d'),
|
|
429
|
-
create=True) # create directory of the day if it doen't exist and return it
|
|
430
|
-
dataset_base_name = curr_date.strftime('Dataset_%Y%m%d')
|
|
431
|
-
dataset_paths = sorted([path for path in day_path.glob(dataset_base_name + "*" + ".h5") if path.is_file()])
|
|
432
|
-
|
|
433
|
-
if ind_dataset is None:
|
|
434
|
-
if dataset_paths == []:
|
|
435
|
-
|
|
436
|
-
ind_dataset = 0
|
|
437
|
-
else:
|
|
438
|
-
if update_h5:
|
|
439
|
-
ind_dataset = int(dataset_paths[-1].stem.partition(dataset_base_name + "_")[2]) + 1
|
|
440
|
-
else:
|
|
441
|
-
ind_dataset = int(dataset_paths[-1].stem.partition(dataset_base_name + "_")[2])
|
|
442
|
-
|
|
443
|
-
dataset_path = cls.find_part_in_path_and_subpath(day_path,
|
|
444
|
-
part=dataset_base_name + "_{:03d}".format(ind_dataset),
|
|
445
|
-
create=False, increment=True)
|
|
446
|
-
scan_paths = sorted([path for path in dataset_path.glob(base_name + '*') if path.is_dir()])
|
|
447
|
-
# if scan_paths==[]:
|
|
448
|
-
# ind_scan=0
|
|
449
|
-
# else:
|
|
450
|
-
# if list(scan_paths[-1].iterdir())==[]:
|
|
451
|
-
# ind_scan=int(scan_paths[-1].name.partition(base_name)[2])
|
|
452
|
-
# else:
|
|
453
|
-
# ind_scan=int(scan_paths[-1].name.partition(base_name)[2])+1
|
|
454
|
-
ind_scan = next_scan_index
|
|
455
|
-
#
|
|
456
|
-
# scan_path = cls.find_part_in_path_and_subpath(dataset_path, part=base_name + '{:03d}'.format(ind_scan),
|
|
457
|
-
# create=create_scan_folder)
|
|
458
|
-
scan_path = ''
|
|
459
|
-
return dataset_path, base_name + '{:03d}'.format(ind_scan), dataset_path
|
|
460
|
-
|
|
461
|
-
def get_last_scan(self):
|
|
462
|
-
"""Gets the last scan node within the h5_file and under the the **raw_group**
|
|
463
|
-
|
|
464
|
-
Returns
|
|
465
|
-
-------
|
|
466
|
-
scan_group: pytables group or None
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
"""
|
|
470
|
-
groups = [group for group in list(self.get_children(self.raw_group)) if 'Scan' in group]
|
|
471
|
-
groups.sort()
|
|
472
|
-
if len(groups) != 0:
|
|
473
|
-
scan_group = self.get_node(self.raw_group, groups[-1])
|
|
474
|
-
else:
|
|
475
|
-
scan_group = None
|
|
476
|
-
return scan_group
|
|
477
|
-
|
|
478
|
-
def get_scan_index(self):
|
|
479
|
-
""" return the scan group index in the "scan templating": Scan000, Scan001 as an integer
|
|
480
|
-
"""
|
|
481
|
-
try:
|
|
482
|
-
if self.current_scan_group is None:
|
|
483
|
-
return 0
|
|
484
|
-
else:
|
|
485
|
-
|
|
486
|
-
groups = [group for group in self.get_children(self.raw_group) if 'Scan' in group]
|
|
487
|
-
groups.sort()
|
|
488
|
-
flag = False
|
|
489
|
-
if len(groups) != 0:
|
|
490
|
-
if 'scan_done' in self.get_attr(self.get_node(self.raw_group, groups[-1])):
|
|
491
|
-
if self.get_attr(self.get_node(self.raw_group, groups[-1]), 'scan_done'):
|
|
492
|
-
return len(groups)
|
|
493
|
-
return len(groups) - 1
|
|
494
|
-
return len(groups) - 1
|
|
495
|
-
return 0
|
|
496
|
-
|
|
497
|
-
except Exception as e:
|
|
498
|
-
logger.exception(str(e))
|
|
499
|
-
|
|
500
|
-
def load_file(self, base_path=None, file_path=None):
|
|
501
|
-
"""Opens a file dialog to select a h5file saved on disk to be used
|
|
502
|
-
|
|
503
|
-
Parameters
|
|
504
|
-
----------
|
|
505
|
-
base_path
|
|
506
|
-
file_path
|
|
507
|
-
|
|
508
|
-
See Also
|
|
509
|
-
--------
|
|
510
|
-
:py:meth:`init_file`
|
|
511
|
-
|
|
512
|
-
"""
|
|
513
|
-
if base_path is None:
|
|
514
|
-
base_path = self.settings.child('base_path').value()
|
|
515
|
-
if not os.path.isdir(base_path):
|
|
516
|
-
base_path = None
|
|
517
|
-
|
|
518
|
-
if file_path is None:
|
|
519
|
-
file_path = select_file(base_path, save=False, ext='h5')
|
|
520
|
-
|
|
521
|
-
if not (file_path is None or file_path == ''):
|
|
522
|
-
if not isinstance(file_path, Path):
|
|
523
|
-
file_path = Path(file_path)
|
|
524
|
-
|
|
525
|
-
if 'h5' not in file_path.suffix:
|
|
526
|
-
raise IOError('Invalid file type, should be a h5 file')
|
|
527
|
-
|
|
528
|
-
self.init_file(addhoc_file_path=file_path)
|
|
529
|
-
self.file_loaded = True
|
|
530
|
-
|
|
531
|
-
def save_file(self, filename=None):
|
|
532
|
-
if filename is None:
|
|
533
|
-
filename = select_file(None, save=True, ext='h5')
|
|
534
|
-
if filename != '':
|
|
535
|
-
super().save_file_as(filename)
|
|
536
|
-
|
|
537
|
-
def get_set_logger(self, where):
|
|
538
|
-
""" Retrieve or create (if absent) a logger enlargeable array to store logs
|
|
539
|
-
Get attributed to the class attribute ``logger_array``
|
|
540
|
-
Parameters
|
|
541
|
-
----------
|
|
542
|
-
where: node
|
|
543
|
-
location within the tree where to save or retrieve the array
|
|
544
|
-
|
|
545
|
-
Returns
|
|
546
|
-
-------
|
|
547
|
-
logger_array: vlarray
|
|
548
|
-
enlargeable array accepting strings as elements
|
|
549
|
-
"""
|
|
550
|
-
if isinstance(where, Node):
|
|
551
|
-
where = where.node
|
|
552
|
-
logger = 'Logger'
|
|
553
|
-
if logger not in list(self.get_children(where)):
|
|
554
|
-
# check if logger node exist
|
|
555
|
-
self.logger_array = self.add_string_array(where, logger)
|
|
556
|
-
self.logger_array.attrs['type'] = 'log'
|
|
557
|
-
else:
|
|
558
|
-
self.logger_array = self.get_node(where, name=logger)
|
|
559
|
-
return self.logger_array
|
|
560
|
-
|
|
561
|
-
def add_log(self, msg):
|
|
562
|
-
self.logger_array.append(msg)
|
|
563
|
-
|
|
564
|
-
def add_data_group(self, where, group_data_type, title='', settings_as_xml='', metadata=dict([])):
|
|
565
|
-
"""Creates a group node at given location in the tree
|
|
566
|
-
|
|
567
|
-
Parameters
|
|
568
|
-
----------
|
|
569
|
-
where: group node
|
|
570
|
-
where to create data group
|
|
571
|
-
group_data_type: list of str
|
|
572
|
-
either ['data0D', 'data1D', 'data2D', 'dataND']
|
|
573
|
-
title: str, optional
|
|
574
|
-
a title for this node, will be saved as metadata
|
|
575
|
-
settings_as_xml: str, optional
|
|
576
|
-
XML string created from a Parameter object to be saved as metadata
|
|
577
|
-
metadata: dict, optional
|
|
578
|
-
will be saved as a new metadata attribute with name: key and value: dict value
|
|
579
|
-
|
|
580
|
-
Returns
|
|
581
|
-
-------
|
|
582
|
-
group: group node
|
|
583
|
-
|
|
584
|
-
See Also
|
|
585
|
-
--------
|
|
586
|
-
:py:meth:`àdd_group`
|
|
587
|
-
"""
|
|
588
|
-
if group_data_type not in group_data_types:
|
|
589
|
-
raise InvalidGroupDataType('Invalid data group type')
|
|
590
|
-
metadata.update(settings=settings_as_xml)
|
|
591
|
-
group = self.add_group(group_data_type, '', where, title, metadata)
|
|
592
|
-
return group
|
|
593
|
-
|
|
594
|
-
def add_navigation_axis(self, data, parent_group, axis='x_axis', enlargeable=False, title='', metadata=dict([])):
|
|
595
|
-
"""
|
|
596
|
-
Create carray or earray for navigation axis within a scan
|
|
597
|
-
Parameters
|
|
598
|
-
----------
|
|
599
|
-
data: (ndarray) of dimension 1
|
|
600
|
-
parent_group: (str or node) parent node where to save new data
|
|
601
|
-
axis: (str) either x_axis, y_axis, z_axis or time_axis. 'x_axis', 'y_axis', 'z_axis', 'time_axis' are axes containing scalar values (floats or ints). 'time_axis' can be interpreted as the posix timestamp corresponding to a datetime object, see datetime.timestamp()
|
|
602
|
-
enlargeable: (bool) if True the created array is a earray type if False the created array is a carray type
|
|
603
|
-
"""
|
|
604
|
-
|
|
605
|
-
if axis not in ['x_axis', 'y_axis', 'z_axis', 'time_axis']:
|
|
606
|
-
if 'axis' not in axis: # this take care of the case of sequential scans where axes are labelled with indexes
|
|
607
|
-
raise NameError('Invalid navigation axis name')
|
|
608
|
-
|
|
609
|
-
array = self.add_array(parent_group, f"{self.settings.child(('save_type')).value()}_{axis}", 'navigation_axis',
|
|
610
|
-
data_shape=data.shape,
|
|
611
|
-
data_dimension='1D', array_to_save=data, enlargeable=enlargeable, title=title,
|
|
612
|
-
metadata=metadata)
|
|
613
|
-
return array
|
|
614
|
-
|
|
615
|
-
def add_data_live_scan(self, channel_group, data_dict, scan_type='scan1D', title='', scan_subtype=''):
|
|
616
|
-
isadaptive = scan_subtype == 'Adaptive'
|
|
617
|
-
if not isadaptive:
|
|
618
|
-
shape, dimension, size = utils.get_data_dimension(data_dict['data'], scan_type=scan_type,
|
|
619
|
-
remove_scan_dimension=True)
|
|
620
|
-
else:
|
|
621
|
-
shape, dimension, size = data_dict['data'].shape, '0D', 1
|
|
622
|
-
data_array = self.add_array(channel_group, 'Data', 'data', array_type=np.float,
|
|
623
|
-
title=title,
|
|
624
|
-
data_shape=shape,
|
|
625
|
-
data_dimension=dimension, scan_type=scan_type,
|
|
626
|
-
scan_subtype=scan_subtype,
|
|
627
|
-
array_to_save=data_dict['data'])
|
|
628
|
-
if 'x_axis' in data_dict:
|
|
629
|
-
if not isinstance(data_dict['x_axis'], dict):
|
|
630
|
-
array_to_save = data_dict['x_axis']
|
|
631
|
-
tmp_dict = dict(label='', units='')
|
|
632
|
-
else:
|
|
633
|
-
tmp_dict = copy.deepcopy(data_dict['x_axis'])
|
|
634
|
-
array_to_save = tmp_dict.pop('data')
|
|
635
|
-
self.add_array(channel_group, 'x_axis', 'axis',
|
|
636
|
-
array_type=np.float, array_to_save=array_to_save,
|
|
637
|
-
enlargeable=False, data_dimension='1D', metadata=tmp_dict)
|
|
638
|
-
if 'y_axis' in data_dict:
|
|
639
|
-
if not isinstance(data_dict['y_axis'], dict):
|
|
640
|
-
array_to_save = data_dict['y_axis']
|
|
641
|
-
tmp_dict = dict(label='', units='')
|
|
642
|
-
else:
|
|
643
|
-
tmp_dict = copy.deepcopy(data_dict['y_axis'])
|
|
644
|
-
array_to_save = tmp_dict.pop('data')
|
|
645
|
-
self.add_array(channel_group, 'y_axis', 'axis',
|
|
646
|
-
array_type=np.float, array_to_save=array_to_save,
|
|
647
|
-
enlargeable=False, data_dimension='1D', metadata=tmp_dict)
|
|
648
|
-
return data_array
|
|
649
|
-
|
|
650
|
-
def add_data(self, channel_group, data_dict, scan_type='scan1D', scan_subtype='',
|
|
651
|
-
scan_shape=[], title='', enlargeable=False,
|
|
652
|
-
init=False, add_scan_dim=False, metadata=dict([])):
|
|
653
|
-
"""save data within the hdf5 file together with axes data (if any) and metadata, node name will be 'Data'
|
|
654
|
-
|
|
655
|
-
Parameters
|
|
656
|
-
----------
|
|
657
|
-
channel_group: (hdf5 node) node where to save the array, in general within a channel type group
|
|
658
|
-
data_dict: (dict) dictionnary containing the data to save and all the axis and metadata mandatory key: 'data':
|
|
659
|
-
(ndarray) data to save other keys: 'xxx_axis' (for instance x_axis, y_axis, 'nav_x_axis'....) or background
|
|
660
|
-
scan_type: (str) either '', 'scan1D' or 'scan2D' or Tabular or sequential
|
|
661
|
-
scan_subtype: (str) see scanner module
|
|
662
|
-
scan_shape: (iterable) the shape of the scan dimensions
|
|
663
|
-
title: (str) the title attribute of the array node
|
|
664
|
-
enlargeable: (bool) if False, data are save as a CARRAY, otherwise as a EARRAY (for ragged data, see add_sting_array)
|
|
665
|
-
init: (bool) if True, the array saved in the h5 file is initialized with the correct type but all element equal
|
|
666
|
-
to zero. Else, the 'data' key of data_dict is saved as is
|
|
667
|
-
add_scan_dim: (bool) if True, the scan axes dimension (scan_shape iterable) is prepended to the array shape on the hdf5
|
|
668
|
-
In that case, the array is usually initialized as zero and further populated
|
|
669
|
-
metadata: (dict) dictionnary whose keys will be saved as the array attributes
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
Returns
|
|
673
|
-
-------
|
|
674
|
-
array (CARRAY or EARRAY)
|
|
675
|
-
|
|
676
|
-
See Also
|
|
677
|
-
--------
|
|
678
|
-
add_array, add_string_array
|
|
679
|
-
"""
|
|
680
|
-
|
|
681
|
-
shape, dimension, size = utils.get_data_dimension(data_dict['data'])
|
|
682
|
-
tmp_data_dict = copy.deepcopy(data_dict)
|
|
683
|
-
array_type = getattr(np, self.settings.child('dynamic').value())
|
|
684
|
-
# save axis
|
|
685
|
-
# this loop covers all type of axis : x_axis, y_axis... nav_x_axis, ...
|
|
686
|
-
axis_keys = [k for k in tmp_data_dict.keys() if 'axis' in k]
|
|
687
|
-
for key in axis_keys:
|
|
688
|
-
if not isinstance(tmp_data_dict[key], dict):
|
|
689
|
-
array_to_save = tmp_data_dict[key]
|
|
690
|
-
tmp_dict = dict(label='', units='')
|
|
691
|
-
else:
|
|
692
|
-
tmp_dict = copy.deepcopy(tmp_data_dict[key])
|
|
693
|
-
array_to_save = tmp_dict.pop('data')
|
|
694
|
-
tmp_data_dict.pop(key)
|
|
695
|
-
|
|
696
|
-
self.add_array(channel_group, key, 'axis', array_type=None, array_to_save=array_to_save,
|
|
697
|
-
enlargeable=False, data_dimension='1D', metadata=tmp_dict)
|
|
698
|
-
|
|
699
|
-
array_to_save = tmp_data_dict.pop('data')
|
|
700
|
-
if 'type' in tmp_data_dict:
|
|
701
|
-
tmp_data_dict.pop('type') # otherwise this metadata would overide mandatory attribute 'type' for a h5 node
|
|
702
|
-
|
|
703
|
-
if 'bkg' in tmp_data_dict:
|
|
704
|
-
bkg = tmp_data_dict.pop('bkg')
|
|
705
|
-
self.add_array(channel_group, 'Bkg', 'bkg', array_type=array_type, array_to_save=bkg,
|
|
706
|
-
data_dimension=dimension)
|
|
707
|
-
tmp_data_dict.update(metadata)
|
|
708
|
-
array_to_save = array_to_save.astype(array_type)
|
|
709
|
-
data_array = self.add_array(channel_group, 'Data', 'data', array_type=array_type,
|
|
710
|
-
title=title, data_shape=shape, enlargeable=enlargeable, data_dimension=dimension,
|
|
711
|
-
scan_type=scan_type, scan_subtype=scan_subtype, scan_shape=scan_shape,
|
|
712
|
-
array_to_save=array_to_save,
|
|
713
|
-
init=init, add_scan_dim=add_scan_dim, metadata=tmp_data_dict)
|
|
714
|
-
|
|
715
|
-
self.flush()
|
|
716
|
-
return data_array
|
|
717
|
-
|
|
718
|
-
def add_array(self, where, name, data_type, data_shape=None, data_dimension=None, scan_type='', scan_subtype='',
|
|
719
|
-
scan_shape=[],
|
|
720
|
-
title='', array_to_save=None, array_type=None, enlargeable=False, metadata=dict([]),
|
|
721
|
-
init=False, add_scan_dim=False):
|
|
722
|
-
"""save data arrays on the hdf5 file together with metadata
|
|
723
|
-
Parameters
|
|
724
|
-
----------
|
|
725
|
-
where: (hdf5 node) node where to save the array
|
|
726
|
-
name: (str) name of the array in the hdf5 file
|
|
727
|
-
data_type: (str) one of ['data', 'axis', 'live_scan', 'navigation_axis', 'external_h5', 'strings', 'bkg'], mandatory
|
|
728
|
-
so that the h5Browsr interpret correctly the array (see add_data)
|
|
729
|
-
data_shape: (iterable) the shape of the array to save, mandatory if array_to_save is None
|
|
730
|
-
data_dimension: (str) one of ['0D', '1D', '2D', 'ND']
|
|
731
|
-
scan_type: (str) either '', 'scan1D' or 'scan2D'
|
|
732
|
-
scan_shape: (iterable): the shape of the scan dimensions
|
|
733
|
-
title: (str) the title attribute of the array node
|
|
734
|
-
array_to_save: (ndarray or None) data to be saved in the array. If None, array_type and data_shape
|
|
735
|
-
should be specified
|
|
736
|
-
array_type: (np.dtype or numpy types), eg np.float, np.int32 ...
|
|
737
|
-
enlargeable: (bool) if False, data are save as a CARRAY, otherwise as a EARRAY (for ragged data, see add_sting_array)
|
|
738
|
-
metadata: (dict) dictionnary whose keys will be saved as the array attributes
|
|
739
|
-
init: (bool) if True, the array saved in the h5 file is initialized with the correct type but all element equal
|
|
740
|
-
to zero. Else, the 'data' key of data_dict is saved as is
|
|
741
|
-
add_scan_dim: if True, the scan axes dimension (scan_shape iterable) is prepended to the array shape on the hdf5
|
|
742
|
-
In that case, the array is usually initialized as zero and further populated
|
|
743
|
-
|
|
744
|
-
Returns
|
|
745
|
-
-------
|
|
746
|
-
array (CARRAY or EARRAY)
|
|
747
|
-
|
|
748
|
-
See Also
|
|
749
|
-
--------
|
|
750
|
-
add_data, add_string_array
|
|
751
|
-
"""
|
|
752
|
-
if array_type is None:
|
|
753
|
-
if array_to_save is None:
|
|
754
|
-
array_type = getattr(np, self.settings.child('dynamic').value())
|
|
755
|
-
else:
|
|
756
|
-
array_type = array_to_save.dtype
|
|
757
|
-
|
|
758
|
-
if data_dimension not in data_dimensions:
|
|
759
|
-
raise InvalidDataDimension('Invalid data dimension')
|
|
760
|
-
if data_type not in data_types:
|
|
761
|
-
raise InvalidDataType('Invalid data type')
|
|
762
|
-
if scan_type != '':
|
|
763
|
-
scan_type = utils.uncapitalize(scan_type)
|
|
764
|
-
if scan_type.lower() not in [s.lower() for s in scan_types]:
|
|
765
|
-
raise InvalidScanType('Invalid scan type')
|
|
766
|
-
if enlargeable:
|
|
767
|
-
if data_shape == (1,):
|
|
768
|
-
data_shape = None
|
|
769
|
-
array = self.create_earray(where, utils.capitalize(name), dtype=np.dtype(array_type),
|
|
770
|
-
data_shape=data_shape, title=title)
|
|
771
|
-
else:
|
|
772
|
-
if add_scan_dim: # means it is an array initialization to zero
|
|
773
|
-
shape = list(scan_shape[:])
|
|
774
|
-
shape.extend(data_shape)
|
|
775
|
-
if init or array_to_save is None:
|
|
776
|
-
array_to_save = np.zeros(shape, dtype=np.dtype(array_type))
|
|
777
|
-
|
|
778
|
-
array = self.create_carray(where, utils.capitalize(name), obj=array_to_save, title=title)
|
|
779
|
-
self.set_attr(array, 'type', data_type)
|
|
780
|
-
self.set_attr(array, 'data_dimension', data_dimension)
|
|
781
|
-
self.set_attr(array, 'scan_type', scan_type)
|
|
782
|
-
self.set_attr(array, 'scan_subtype', scan_subtype)
|
|
783
|
-
|
|
784
|
-
for metadat in metadata:
|
|
785
|
-
self.set_attr(array, metadat, metadata[metadat])
|
|
786
|
-
return array
|
|
787
|
-
|
|
788
|
-
def add_string_array(self, where, name, title='', metadata=dict([])):
|
|
789
|
-
array = self.create_vlarray(where, name, dtype='string', title=title)
|
|
790
|
-
array.attrs['shape'] = (0,)
|
|
791
|
-
array.attrs['data_dimension'] = '0D'
|
|
792
|
-
array.attrs['scan_type'] = 'scan1D'
|
|
793
|
-
|
|
794
|
-
for metadat in metadata:
|
|
795
|
-
array.attrs[metadat] = metadata[metadat]
|
|
796
|
-
return array
|
|
797
|
-
|
|
798
|
-
def get_set_group(self, where, name, title=''):
|
|
799
|
-
self.current_group = super().get_set_group(where, name, title)
|
|
800
|
-
return self.current_group
|
|
801
|
-
|
|
802
|
-
def add_incremental_group(self, group_type, where, title='', settings_as_xml='', metadata=dict([])):
|
|
803
|
-
"""
|
|
804
|
-
Add a node in the h5 file tree of the group type with an increment in the given name
|
|
805
|
-
Parameters
|
|
806
|
-
----------
|
|
807
|
-
group_type: (str) one of the possible values of **group_types**
|
|
808
|
-
where: (str or node) parent node where to create the new group
|
|
809
|
-
title: (str) node title
|
|
810
|
-
settings_as_xml: (str) XML string containing Parameters representation (see custom_Tree)
|
|
811
|
-
metadata: (dict) extra metadata to be saved with this new group node
|
|
812
|
-
|
|
813
|
-
Returns
|
|
814
|
-
-------
|
|
815
|
-
(node): newly created group node
|
|
816
|
-
"""
|
|
817
|
-
if group_type not in group_types:
|
|
818
|
-
raise InvalidGroupType('Invalid group type')
|
|
819
|
-
nodes = [name for name in self.get_children(self.get_node(where))]
|
|
820
|
-
nodes_tmp = []
|
|
821
|
-
for node in nodes:
|
|
822
|
-
if utils.capitalize(group_type) in node:
|
|
823
|
-
nodes_tmp.append(node)
|
|
824
|
-
nodes_tmp.sort()
|
|
825
|
-
if len(nodes_tmp) == 0:
|
|
826
|
-
ind_group = -1
|
|
827
|
-
else:
|
|
828
|
-
ind_group = int(nodes_tmp[-1][-3:])
|
|
829
|
-
group = self.get_set_group(where, utils.capitalize(group_type) + '{:03d}'.format(ind_group + 1), title)
|
|
830
|
-
self.set_attr(group, 'settings', settings_as_xml)
|
|
831
|
-
if group_type.lower() != 'ch':
|
|
832
|
-
self.set_attr(group, 'type', group_type.lower())
|
|
833
|
-
else:
|
|
834
|
-
self.set_attr(group, 'type', '')
|
|
835
|
-
for metadat in metadata:
|
|
836
|
-
self.set_attr(group, metadat, metadata[metadat])
|
|
837
|
-
return group
|
|
838
|
-
|
|
839
|
-
def add_det_group(self, where, title='', settings_as_xml='', metadata=dict([])):
|
|
840
|
-
"""
|
|
841
|
-
Add a new group of type detector
|
|
842
|
-
See Also
|
|
843
|
-
-------
|
|
844
|
-
add_incremental_group
|
|
845
|
-
"""
|
|
846
|
-
group = self.add_incremental_group('detector', where, title, settings_as_xml, metadata)
|
|
847
|
-
return group
|
|
848
|
-
|
|
849
|
-
def add_CH_group(self, where, title='', settings_as_xml='', metadata=dict([])):
|
|
850
|
-
"""
|
|
851
|
-
Add a new group of type channel
|
|
852
|
-
See Also
|
|
853
|
-
-------
|
|
854
|
-
add_incremental_group
|
|
855
|
-
"""
|
|
856
|
-
group = self.add_incremental_group('ch', where, title, settings_as_xml, metadata)
|
|
857
|
-
return group
|
|
858
|
-
|
|
859
|
-
def add_live_scan_group(self, where, dimensionality, title='', settings_as_xml='', metadata=dict([])):
|
|
860
|
-
"""
|
|
861
|
-
Add a new group of type live scan
|
|
862
|
-
See Also
|
|
863
|
-
-------
|
|
864
|
-
add_incremental_group
|
|
865
|
-
"""
|
|
866
|
-
metadata.update(settings=settings_as_xml)
|
|
867
|
-
group = self.add_group(utils.capitalize('Live_scan_{:s}'.format(dimensionality)), '', where, title=title,
|
|
868
|
-
metadata=metadata)
|
|
869
|
-
return group
|
|
870
|
-
|
|
871
|
-
def add_move_group(self, where, title='', settings_as_xml='', metadata=dict([])):
|
|
872
|
-
"""
|
|
873
|
-
Add a new group of type move
|
|
874
|
-
See Also
|
|
875
|
-
-------
|
|
876
|
-
add_incremental_group
|
|
877
|
-
"""
|
|
878
|
-
group = self.add_incremental_group('move', where, title, settings_as_xml, metadata)
|
|
879
|
-
return group
|
|
880
|
-
|
|
881
|
-
def parameter_tree_changed(self, param, changes):
|
|
882
|
-
for param, change, data in changes:
|
|
883
|
-
path = self.settings.childPath(param)
|
|
884
|
-
|
|
885
|
-
if change == 'childAdded':
|
|
886
|
-
pass
|
|
887
|
-
|
|
888
|
-
elif change == 'value':
|
|
889
|
-
if param.name() == 'show_file':
|
|
890
|
-
param.setValue(False)
|
|
891
|
-
self.show_file_content()
|
|
892
|
-
|
|
893
|
-
elif param.name() == 'base_path':
|
|
894
|
-
try:
|
|
895
|
-
if not os.path.isdir(param.value()):
|
|
896
|
-
os.mkdir(param.value())
|
|
897
|
-
except Exception as e:
|
|
898
|
-
logger.warning(f"The base path couldn't be set, please check your options: {str(e)}")
|
|
899
|
-
self.update_status("The base path couldn't be set, please check your options")
|
|
900
|
-
|
|
901
|
-
elif param.name() in putils.iter_children(self.settings.child('compression_options'), []):
|
|
902
|
-
compression = self.settings.child('compression_options', 'h5comp_library').value()
|
|
903
|
-
compression_opts = self.settings.child('compression_options', 'h5comp_level').value()
|
|
904
|
-
self.define_compression(compression, compression_opts)
|
|
905
|
-
|
|
906
|
-
elif change == 'parent':
|
|
907
|
-
pass
|
|
908
|
-
|
|
909
|
-
def update_status(self, status):
|
|
910
|
-
# self.status_sig.emit(utils.ThreadCommand("Update_Status", [status, 'log']))
|
|
911
|
-
# logger.info(status)
|
|
912
|
-
pass
|
|
913
|
-
|
|
914
|
-
def show_file_content(self):
|
|
915
|
-
form = QtWidgets.QWidget()
|
|
916
|
-
if not self.isopen():
|
|
917
|
-
if self.h5_file_path is not None:
|
|
918
|
-
if self.h5_file_path.exists():
|
|
919
|
-
self.analysis_prog = H5Browser(form, h5file_path=self.h5_file_path)
|
|
920
|
-
else:
|
|
921
|
-
logger.warning('The h5 file path has not been defined yet')
|
|
922
|
-
else:
|
|
923
|
-
logger.warning('The h5 file path has not been defined yet')
|
|
924
|
-
else:
|
|
925
|
-
self.flush()
|
|
926
|
-
self.analysis_prog = H5Browser(form, h5file=self.h5file)
|
|
927
|
-
form.show()
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
class H5Saver(H5SaverBase, QObject):
|
|
931
|
-
"""
|
|
932
|
-
status_sig: Signal
|
|
933
|
-
emits a signal of type Threadcommand in order to senf log information to a main UI
|
|
934
|
-
new_file_sig: Signal
|
|
935
|
-
emits a boolean signal to let the program know when the user pressed the new file button on the UI
|
|
936
|
-
"""
|
|
937
|
-
|
|
938
|
-
status_sig = Signal(utils.ThreadCommand)
|
|
939
|
-
new_file_sig = Signal(bool)
|
|
940
|
-
|
|
941
|
-
def __init__(self, *args, **kwargs):
|
|
942
|
-
QObject.__init__(self)
|
|
943
|
-
H5SaverBase.__init__(self, *args, **kwargs)
|
|
944
|
-
|
|
945
|
-
self.settings_tree = ParameterTree()
|
|
946
|
-
self.settings_tree.setMinimumHeight(310)
|
|
947
|
-
self.settings_tree.setParameters(self.settings, showTop=False)
|
|
948
|
-
self.settings.child(('new_file')).sigActivated.connect(lambda: self.emit_new_file(True))
|
|
949
|
-
|
|
950
|
-
def close(self):
|
|
951
|
-
# to display the correct interface to other parts of the library
|
|
952
|
-
# TODO create the related interface/abstract class
|
|
953
|
-
|
|
954
|
-
self.close_file()
|
|
955
|
-
|
|
956
|
-
def emit_new_file(self, status):
|
|
957
|
-
"""Emits the new_file_sig
|
|
958
|
-
|
|
959
|
-
Parameters
|
|
960
|
-
----------
|
|
961
|
-
status: bool
|
|
962
|
-
emits True if a new file has been asked by the user pressing the new file button on the UI
|
|
963
|
-
"""
|
|
964
|
-
self.new_file_sig.emit(status)
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
class H5Logger(AbstractLogger):
|
|
968
|
-
def __init__(self, *args, **kwargs):
|
|
969
|
-
self.h5saver = H5Saver(*args, save_type='logger', **kwargs)
|
|
970
|
-
|
|
971
|
-
def close(self):
|
|
972
|
-
self.h5saver.close_file()
|
|
973
|
-
|
|
974
|
-
@property
|
|
975
|
-
def settings_tree(self):
|
|
976
|
-
return self.h5saver.settings_tree
|
|
977
|
-
|
|
978
|
-
@property
|
|
979
|
-
def settings(self):
|
|
980
|
-
return self.h5saver.settings
|
|
981
|
-
|
|
982
|
-
def init_logger(self, settings):
|
|
983
|
-
self.h5saver.init_file(update_h5=True, metadata=dict(settings=settings))
|
|
984
|
-
self.h5saver.flush()
|
|
985
|
-
return True
|
|
986
|
-
|
|
987
|
-
def get_handler(self):
|
|
988
|
-
return H5LogHandler(self.h5saver)
|
|
989
|
-
|
|
990
|
-
def add_detector(self, det_name, settings):
|
|
991
|
-
if det_name not in self.h5saver.raw_group.children_name():
|
|
992
|
-
det_group = self.h5saver.add_det_group(self.h5saver.raw_group, det_name, settings)
|
|
993
|
-
self.h5saver.add_navigation_axis(np.array([0.0, ]),
|
|
994
|
-
det_group, 'time_axis', enlargeable=True,
|
|
995
|
-
title='Time axis',
|
|
996
|
-
metadata=dict(label='Time axis', units='s', nav_index=0))
|
|
997
|
-
|
|
998
|
-
def add_datas(self, datas):
|
|
999
|
-
det_name = datas['name']
|
|
1000
|
-
det_group = self.h5saver.get_group_by_title(self.h5saver.raw_group, det_name)
|
|
1001
|
-
time_array = self.h5saver.get_node(det_group, 'Logger_time_axis')
|
|
1002
|
-
time_array.append(np.array([datas['acq_time_s']]))
|
|
1003
|
-
|
|
1004
|
-
data_types = ['data0D', 'data1D']
|
|
1005
|
-
if self.settings.child(('save_2D')).value():
|
|
1006
|
-
data_types.extend(['data2D', 'dataND'])
|
|
1007
|
-
|
|
1008
|
-
for data_type in data_types:
|
|
1009
|
-
if data_type in datas.keys() and len(datas[data_type]) != 0:
|
|
1010
|
-
if not self.h5saver.is_node_in_group(det_group, data_type):
|
|
1011
|
-
data_group = self.h5saver.add_data_group(det_group, data_type, metadata=dict(type='scan'))
|
|
1012
|
-
else:
|
|
1013
|
-
data_group = self.h5saver.get_node(det_group, utils.capitalize(data_type))
|
|
1014
|
-
for ind_channel, channel in enumerate(datas[data_type]):
|
|
1015
|
-
channel_group = self.h5saver.get_group_by_title(data_group, channel)
|
|
1016
|
-
if channel_group is None:
|
|
1017
|
-
channel_group = self.h5saver.add_CH_group(data_group, title=channel)
|
|
1018
|
-
data_array = self.h5saver.add_data(channel_group, datas[data_type][channel],
|
|
1019
|
-
scan_type='scan1D', enlargeable=True)
|
|
1020
|
-
else:
|
|
1021
|
-
data_array = self.h5saver.get_node(channel_group, 'Data')
|
|
1022
|
-
if data_type == 'data0D':
|
|
1023
|
-
data_array.append(np.array([datas[data_type][channel]['data']]))
|
|
1024
|
-
else:
|
|
1025
|
-
data_array.append(datas[data_type][channel]['data'])
|
|
1026
|
-
self.h5saver.flush()
|
|
1027
|
-
self.settings.child(('N_saved')).setValue(
|
|
1028
|
-
self.settings.child(('N_saved')).value() + 1)
|
|
1029
|
-
|
|
1030
|
-
def stop_logger(self):
|
|
1031
|
-
self.h5saver.flush()
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
def find_scan_node(scan_node):
|
|
1035
|
-
"""
|
|
1036
|
-
utility function to find the parent node of "scan" type, meaning some of its children (DAQ_scan case)
|
|
1037
|
-
or co-nodes (daq_logger case) are navigation axes
|
|
1038
|
-
Parameters
|
|
1039
|
-
----------
|
|
1040
|
-
scan_node: (pytables node)
|
|
1041
|
-
data node from where this function look for its navigation axes if any
|
|
1042
|
-
Returns
|
|
1043
|
-
-------
|
|
1044
|
-
node: the parent node of 'scan' type
|
|
1045
|
-
list: the data nodes of type 'navigation_axis' corresponding to the initial data node
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
"""
|
|
1049
|
-
try:
|
|
1050
|
-
while True:
|
|
1051
|
-
if scan_node.attrs['type'] == 'scan':
|
|
1052
|
-
break
|
|
1053
|
-
else:
|
|
1054
|
-
scan_node = scan_node.parent_node
|
|
1055
|
-
children = list(scan_node.children().values()) # for data saved using daq_scan
|
|
1056
|
-
children.extend([scan_node.parent_node.children()[child] for child in
|
|
1057
|
-
scan_node.parent_node.children_name()]) # for data saved using the daq_logger
|
|
1058
|
-
nav_children = []
|
|
1059
|
-
for child in children:
|
|
1060
|
-
if 'type' in child.attrs.attrs_name:
|
|
1061
|
-
if child.attrs['type'] == 'navigation_axis':
|
|
1062
|
-
nav_children.append(child)
|
|
1063
|
-
return scan_node, nav_children
|
|
1064
|
-
except Exception:
|
|
1065
|
-
return None, []
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
class H5BrowserUtil(H5Backend):
|
|
1069
|
-
"""Class to handle manipulation and export of h5 nodes"""
|
|
1070
|
-
|
|
1071
|
-
def __init__(self, backend='tables'):
|
|
1072
|
-
super().__init__(backend=backend)
|
|
1073
|
-
|
|
1074
|
-
def export_data(self, node_path='/', filesavename: str = 'datafile.h5'):
|
|
1075
|
-
"""Initialize the correct exporter and export the node"""
|
|
1076
|
-
|
|
1077
|
-
# Format the node and file type
|
|
1078
|
-
filepath = Path(filesavename)
|
|
1079
|
-
node = self.get_node(node_path)
|
|
1080
|
-
# Separate dot from extension
|
|
1081
|
-
extension = filepath.suffix[1:]
|
|
1082
|
-
# Obtain the suitable exporter object
|
|
1083
|
-
exporter = ExporterFactory.create_exporter(extension)
|
|
1084
|
-
# Export the data
|
|
1085
|
-
exporter._export_data(node, filepath)
|
|
1086
|
-
|
|
1087
|
-
def get_h5file_scans(self, where='/'):
|
|
1088
|
-
# TODO add a test for this method
|
|
1089
|
-
scan_list = []
|
|
1090
|
-
where = self.get_node(where)
|
|
1091
|
-
for node in self.walk_nodes(where):
|
|
1092
|
-
if 'pixmap2D' in node.attrs.attrs_name:
|
|
1093
|
-
scan_list.append(
|
|
1094
|
-
dict(scan_name='{:s}_{:s}'.format(node.parent_node.name, node.name), path=node.path,
|
|
1095
|
-
data=node.attrs['pixmap2D']))
|
|
1096
|
-
|
|
1097
|
-
return scan_list
|
|
1098
|
-
|
|
1099
|
-
def get_h5_attributes(self, node_path):
|
|
1100
|
-
"""
|
|
1101
|
-
|
|
1102
|
-
"""
|
|
1103
|
-
|
|
1104
|
-
node = self.get_node(node_path)
|
|
1105
|
-
attrs_names = node.attrs.attrs_name
|
|
1106
|
-
attr_dict = OrderedDict([])
|
|
1107
|
-
for attr in attrs_names:
|
|
1108
|
-
# if attr!='settings':
|
|
1109
|
-
attr_dict[attr] = node.attrs[attr]
|
|
1110
|
-
|
|
1111
|
-
settings = None
|
|
1112
|
-
scan_settings = None
|
|
1113
|
-
if 'settings' in attrs_names:
|
|
1114
|
-
if node.attrs['settings'] != '':
|
|
1115
|
-
settings = node.attrs['settings']
|
|
1116
|
-
|
|
1117
|
-
if 'scan_settings' in attrs_names:
|
|
1118
|
-
if node.attrs['scan_settings'] != '':
|
|
1119
|
-
scan_settings = node.attrs['scan_settings']
|
|
1120
|
-
pixmaps = []
|
|
1121
|
-
for attr in attrs_names:
|
|
1122
|
-
if 'pixmap' in attr:
|
|
1123
|
-
pixmaps.append(node.attrs[attr])
|
|
1124
|
-
|
|
1125
|
-
return attr_dict, settings, scan_settings, pixmaps
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
class H5Browser(QObject):
|
|
1129
|
-
"""UI used to explore h5 files, plot and export subdatas"""
|
|
1130
|
-
data_node_signal = Signal(
|
|
1131
|
-
str) # the path of a node where data should be monitored, displayed...whatever use from the caller
|
|
1132
|
-
status_signal = Signal(str)
|
|
1133
|
-
|
|
1134
|
-
def __init__(self, parent, h5file=None, h5file_path=None, backend='tables'):
|
|
1135
|
-
"""
|
|
1136
|
-
|
|
1137
|
-
Parameters
|
|
1138
|
-
----------
|
|
1139
|
-
parent: QtWidgets container, either a QWidget or a QMainWindow
|
|
1140
|
-
h5file: h5file instance (exact type depends on the backend)
|
|
1141
|
-
h5file_path: (str or Path) if specified load the corresponding file, otherwise open a select file dialog
|
|
1142
|
-
backend: (str) eitre 'tables, 'h5py' or 'h5pyd'
|
|
1143
|
-
|
|
1144
|
-
See Also
|
|
1145
|
-
--------
|
|
1146
|
-
H5Backend, H5Backend
|
|
1147
|
-
"""
|
|
1148
|
-
|
|
1149
|
-
super(H5Browser, self).__init__()
|
|
1150
|
-
if not (isinstance(parent, QtWidgets.QWidget) or isinstance(parent, QtWidgets.QMainWindow)):
|
|
1151
|
-
raise Exception('no valid parent container, expected a QWidget or a QMainWindow')
|
|
1152
|
-
|
|
1153
|
-
if isinstance(parent, QtWidgets.QMainWindow):
|
|
1154
|
-
self.main_window = parent
|
|
1155
|
-
self.parent = QtWidgets.QWidget()
|
|
1156
|
-
self.main_window.setCentralWidget(self.parent)
|
|
1157
|
-
else:
|
|
1158
|
-
self.main_window = None
|
|
1159
|
-
self.parent = parent
|
|
1160
|
-
|
|
1161
|
-
self.backend = backend
|
|
1162
|
-
self.current_node_path = None
|
|
1163
|
-
|
|
1164
|
-
# construct the UI interface
|
|
1165
|
-
self.ui = QObject() # the user interface
|
|
1166
|
-
self.set_GUI()
|
|
1167
|
-
|
|
1168
|
-
# construct the h5 interface and load the file (or open a select file message)
|
|
1169
|
-
self.h5utils = H5BrowserUtil(backend=self.backend)
|
|
1170
|
-
if h5file is None:
|
|
1171
|
-
if h5file_path is None:
|
|
1172
|
-
h5file_path = select_file(save=False, ext=['h5', 'hdf5'])
|
|
1173
|
-
if h5file_path != '':
|
|
1174
|
-
self.h5utils.open_file(h5file_path, 'r+')
|
|
1175
|
-
else:
|
|
1176
|
-
return
|
|
1177
|
-
else:
|
|
1178
|
-
self.h5utils.h5file = h5file
|
|
1179
|
-
|
|
1180
|
-
self.check_version()
|
|
1181
|
-
self.populate_tree()
|
|
1182
|
-
self.ui.h5file_tree.ui.Open_Tree.click()
|
|
1183
|
-
|
|
1184
|
-
def check_version(self):
|
|
1185
|
-
if 'pymodaq_version' in self.h5utils.root().attrs.attrs_name:
|
|
1186
|
-
if version_mod.parse(self.h5utils.root().attrs['pymodaq_version']) < version_mod.parse('2.0'):
|
|
1187
|
-
msgBox = QtWidgets.QMessageBox(parent=None)
|
|
1188
|
-
msgBox.setWindowTitle("Invalid version")
|
|
1189
|
-
msgBox.setText(f"Your file has been saved using PyMoDAQ "
|
|
1190
|
-
f"version {self.h5utils.root().attrs['pymodaq_version']} "
|
|
1191
|
-
f"while you're using version: {utils.get_version()}\n"
|
|
1192
|
-
f"Please create and use an adapted environment to use this version (up to 1.6.4):\n"
|
|
1193
|
-
f"pip install pymodaq==1.6.4")
|
|
1194
|
-
ret = msgBox.exec()
|
|
1195
|
-
self.quit_fun()
|
|
1196
|
-
if self.main_window is not None:
|
|
1197
|
-
self.main_window.close()
|
|
1198
|
-
else:
|
|
1199
|
-
self.parent.close()
|
|
1200
|
-
|
|
1201
|
-
def add_comments(self, status, comment=''):
|
|
1202
|
-
try:
|
|
1203
|
-
self.current_node_path = self.get_tree_node_path()
|
|
1204
|
-
node = self.h5utils.get_node(self.current_node_path)
|
|
1205
|
-
if 'comments' in node.attrs.attrs_name:
|
|
1206
|
-
tmp = node.attrs['comments']
|
|
1207
|
-
else:
|
|
1208
|
-
tmp = ''
|
|
1209
|
-
if comment == '':
|
|
1210
|
-
text, res = QtWidgets.QInputDialog.getMultiLineText(None, 'Enter comments', 'Enter comments here:', tmp)
|
|
1211
|
-
if res and text != '':
|
|
1212
|
-
comment = text
|
|
1213
|
-
node.attrs['comments'] = comment
|
|
1214
|
-
else:
|
|
1215
|
-
node.attrs['comments'] = tmp + comment
|
|
1216
|
-
|
|
1217
|
-
self.h5utils.flush()
|
|
1218
|
-
|
|
1219
|
-
except Exception as e:
|
|
1220
|
-
logger.exception(str(e))
|
|
1221
|
-
|
|
1222
|
-
def get_tree_node_path(self):
|
|
1223
|
-
return self.ui.h5file_tree.ui.Tree.currentItem().text(2)
|
|
1224
|
-
|
|
1225
|
-
def export_data(self):
|
|
1226
|
-
try:
|
|
1227
|
-
# get file filters automatically
|
|
1228
|
-
file_filter = ExporterFactory.get_file_filters()
|
|
1229
|
-
file = select_file(save=True, filter=file_filter)
|
|
1230
|
-
self.current_node_path = self.get_tree_node_path()
|
|
1231
|
-
# Here select and initialize exporter from extension
|
|
1232
|
-
if file != '':
|
|
1233
|
-
self.h5utils.export_data(self.current_node_path, str(file))
|
|
1234
|
-
|
|
1235
|
-
except Exception as e:
|
|
1236
|
-
logger.exception(str(e))
|
|
1237
|
-
|
|
1238
|
-
def save_file(self, filename=None):
|
|
1239
|
-
if filename is None:
|
|
1240
|
-
filename = select_file(save=True, ext='txt')
|
|
1241
|
-
if filename != '':
|
|
1242
|
-
self.h5utils.save_file(filename)
|
|
1243
|
-
|
|
1244
|
-
def quit_fun(self):
|
|
1245
|
-
"""
|
|
1246
|
-
"""
|
|
1247
|
-
try:
|
|
1248
|
-
self.h5utils.close_file()
|
|
1249
|
-
if self.main_window is None:
|
|
1250
|
-
self.parent.close()
|
|
1251
|
-
else:
|
|
1252
|
-
self.main_window.close()
|
|
1253
|
-
except Exception as e:
|
|
1254
|
-
logger.exception(str(e))
|
|
1255
|
-
|
|
1256
|
-
def create_menu(self):
|
|
1257
|
-
"""
|
|
1258
|
-
|
|
1259
|
-
"""
|
|
1260
|
-
self.menubar = self.main_window.menuBar()
|
|
1261
|
-
|
|
1262
|
-
# %% create Settings menu
|
|
1263
|
-
self.file_menu = self.menubar.addMenu('File')
|
|
1264
|
-
load_action = self.file_menu.addAction('Load file')
|
|
1265
|
-
load_action.triggered.connect(lambda: self.load_file(None))
|
|
1266
|
-
save_action = self.file_menu.addAction('Save file')
|
|
1267
|
-
save_action.triggered.connect(self.save_file)
|
|
1268
|
-
self.file_menu.addSeparator()
|
|
1269
|
-
quit_action = self.file_menu.addAction('Quit')
|
|
1270
|
-
quit_action.triggered.connect(self.quit_fun)
|
|
1271
|
-
|
|
1272
|
-
# help menu
|
|
1273
|
-
help_menu = self.menubar.addMenu('?')
|
|
1274
|
-
action_about = help_menu.addAction('About')
|
|
1275
|
-
action_about.triggered.connect(self.show_about)
|
|
1276
|
-
action_help = help_menu.addAction('Help')
|
|
1277
|
-
action_help.triggered.connect(self.show_help)
|
|
1278
|
-
action_help.setShortcut(QtCore.Qt.Key_F1)
|
|
1279
|
-
log_action = help_menu.addAction('Show log')
|
|
1280
|
-
log_action.triggered.connect(self.show_log)
|
|
1281
|
-
|
|
1282
|
-
def show_about(self):
|
|
1283
|
-
splash_path = os.path.join(os.path.split(os.path.split(__file__)[0])[0], 'splash.png')
|
|
1284
|
-
splash = QtGui.QPixmap(splash_path)
|
|
1285
|
-
self.splash_sc = QtWidgets.QSplashScreen(splash, QtCore.Qt.WindowStaysOnTopHint)
|
|
1286
|
-
self.splash_sc.setVisible(True)
|
|
1287
|
-
self.splash_sc.showMessage(f"PyMoDAQ version {utils.get_version()}\n"
|
|
1288
|
-
f"Modular Acquisition with Python\nWritten by Sébastien Weber",
|
|
1289
|
-
QtCore.Qt.AlignRight, QtCore.Qt.white)
|
|
1290
|
-
|
|
1291
|
-
def show_log(self):
|
|
1292
|
-
import webbrowser
|
|
1293
|
-
webbrowser.open(logger.handlers[0].baseFilename)
|
|
1294
|
-
|
|
1295
|
-
def show_help(self):
|
|
1296
|
-
QtGui.QDesktopServices.openUrl(QtCore.QUrl("http://pymodaq.cnrs.fr"))
|
|
1297
|
-
|
|
1298
|
-
def set_GUI(self):
|
|
1299
|
-
|
|
1300
|
-
if self.main_window is not None:
|
|
1301
|
-
self.create_menu()
|
|
1302
|
-
|
|
1303
|
-
layout = QtWidgets.QGridLayout()
|
|
1304
|
-
|
|
1305
|
-
V_splitter = QtWidgets.QSplitter(Qt.Vertical)
|
|
1306
|
-
V_splitter2 = QtWidgets.QSplitter(Qt.Vertical)
|
|
1307
|
-
H_splitter = QtWidgets.QSplitter(Qt.Horizontal)
|
|
1308
|
-
|
|
1309
|
-
Form = QtWidgets.QWidget()
|
|
1310
|
-
# self.ui.h5file_tree = Tree_layout(Form,col_counts=2,labels=["Node",'Pixmap'])
|
|
1311
|
-
self.ui.h5file_tree = Tree_layout(Form, col_counts=1, labels=["Node"])
|
|
1312
|
-
self.ui.h5file_tree.ui.Tree.setMinimumWidth(300)
|
|
1313
|
-
self.ui.h5file_tree.ui.Tree.itemClicked.connect(self.show_h5_attributes)
|
|
1314
|
-
self.ui.h5file_tree.ui.Tree.itemDoubleClicked.connect(self.show_h5_data)
|
|
1315
|
-
|
|
1316
|
-
self.export_action = QtWidgets.QAction("Export data", None)
|
|
1317
|
-
self.export_action.triggered.connect(self.export_data)
|
|
1318
|
-
|
|
1319
|
-
self.add_comments_action = QtWidgets.QAction("Add comments to this node", None)
|
|
1320
|
-
self.add_comments_action.triggered.connect(self.add_comments)
|
|
1321
|
-
self.ui.h5file_tree.ui.Tree.addAction(self.export_action)
|
|
1322
|
-
self.ui.h5file_tree.ui.Tree.addAction(self.add_comments_action)
|
|
1323
|
-
|
|
1324
|
-
V_splitter.addWidget(Form)
|
|
1325
|
-
self.ui.attributes_tree = ParameterTree()
|
|
1326
|
-
self.ui.attributes_tree.setMinimumWidth(300)
|
|
1327
|
-
V_splitter.addWidget(self.ui.attributes_tree)
|
|
1328
|
-
|
|
1329
|
-
self.settings_raw = Parameter.create(name='Param_raw', type='group')
|
|
1330
|
-
self.ui.attributes_tree.setParameters(self.settings_raw, showTop=False)
|
|
1331
|
-
|
|
1332
|
-
H_splitter.addWidget(V_splitter)
|
|
1333
|
-
self.pixmap_widget = QtWidgets.QWidget()
|
|
1334
|
-
self.pixmap_widget.setMaximumHeight(100)
|
|
1335
|
-
V_splitter2.addWidget(self.pixmap_widget)
|
|
1336
|
-
self.ui.settings_tree = ParameterTree()
|
|
1337
|
-
self.ui.settings_tree.setMinimumWidth(300)
|
|
1338
|
-
V_splitter2.addWidget(self.ui.settings_tree)
|
|
1339
|
-
self.ui.text_list = QtWidgets.QListWidget()
|
|
1340
|
-
|
|
1341
|
-
V_splitter2.addWidget(self.ui.text_list)
|
|
1342
|
-
|
|
1343
|
-
H_splitter.addWidget(V_splitter2)
|
|
1344
|
-
|
|
1345
|
-
form_viewer = QtWidgets.QWidget()
|
|
1346
|
-
self.viewer_area = DockArea()
|
|
1347
|
-
self.hyperviewer = ViewerND(self.viewer_area)
|
|
1348
|
-
H_splitter.addWidget(self.viewer_area)
|
|
1349
|
-
|
|
1350
|
-
layout.addWidget(H_splitter)
|
|
1351
|
-
self.parent.setLayout(layout)
|
|
1352
|
-
|
|
1353
|
-
self.settings = Parameter.create(name='Param', type='group')
|
|
1354
|
-
self.ui.settings_tree.setParameters(self.settings, showTop=False)
|
|
1355
|
-
|
|
1356
|
-
self.status_signal.connect(self.add_log)
|
|
1357
|
-
|
|
1358
|
-
def add_log(self, txt):
|
|
1359
|
-
logger.info(txt)
|
|
1360
|
-
|
|
1361
|
-
def show_h5_attributes(self, item):
|
|
1362
|
-
"""
|
|
1363
|
-
|
|
1364
|
-
"""
|
|
1365
|
-
try:
|
|
1366
|
-
self.current_node_path = self.get_tree_node_path()
|
|
1367
|
-
|
|
1368
|
-
attr_dict, settings, scan_settings, pixmaps = self.h5utils.get_h5_attributes(self.current_node_path)
|
|
1369
|
-
|
|
1370
|
-
for child in self.settings_raw.children():
|
|
1371
|
-
child.remove()
|
|
1372
|
-
params = []
|
|
1373
|
-
for attr in attr_dict:
|
|
1374
|
-
params.append({'title': attr, 'name': attr, 'type': 'str', 'value': attr_dict[attr], 'readonly': True})
|
|
1375
|
-
self.settings_raw.addChildren(params)
|
|
1376
|
-
|
|
1377
|
-
if settings is not None:
|
|
1378
|
-
for child in self.settings.children():
|
|
1379
|
-
child.remove()
|
|
1380
|
-
QtWidgets.QApplication.processEvents() # so that the tree associated with settings updates
|
|
1381
|
-
params = pymodaq.daq_utils.parameter.ioxml.XML_string_to_parameter(settings)
|
|
1382
|
-
self.settings.addChildren(params)
|
|
1383
|
-
|
|
1384
|
-
if scan_settings is not None:
|
|
1385
|
-
params = pymodaq.daq_utils.parameter.ioxml.XML_string_to_parameter(scan_settings)
|
|
1386
|
-
self.settings.addChildren(params)
|
|
1387
|
-
|
|
1388
|
-
if pixmaps == []:
|
|
1389
|
-
self.pixmap_widget.setVisible(False)
|
|
1390
|
-
else:
|
|
1391
|
-
self.pixmap_widget.setVisible(True)
|
|
1392
|
-
self.show_pixmaps(pixmaps)
|
|
1393
|
-
|
|
1394
|
-
except Exception as e:
|
|
1395
|
-
logger.exception(str(e))
|
|
1396
|
-
|
|
1397
|
-
def show_pixmaps(self, pixmaps=[]):
|
|
1398
|
-
if self.pixmap_widget.layout() is None:
|
|
1399
|
-
layout = QtWidgets.QHBoxLayout()
|
|
1400
|
-
self.pixmap_widget.setLayout(layout)
|
|
1401
|
-
while 1:
|
|
1402
|
-
child = self.pixmap_widget.layout().takeAt(0)
|
|
1403
|
-
if not child:
|
|
1404
|
-
break
|
|
1405
|
-
child.widget().deleteLater()
|
|
1406
|
-
QtWidgets.QApplication.processEvents()
|
|
1407
|
-
labs = []
|
|
1408
|
-
for pix in pixmaps:
|
|
1409
|
-
labs.append(pngbinary2Qlabel(pix))
|
|
1410
|
-
self.pixmap_widget.layout().addWidget(labs[-1])
|
|
1411
|
-
|
|
1412
|
-
def show_h5_data(self, item):
|
|
1413
|
-
"""
|
|
1414
|
-
"""
|
|
1415
|
-
try:
|
|
1416
|
-
self.current_node_path = item.text(2)
|
|
1417
|
-
self.show_h5_attributes(item)
|
|
1418
|
-
node = self.h5utils.get_node(self.current_node_path)
|
|
1419
|
-
self.data_node_signal.emit(self.current_node_path)
|
|
1420
|
-
if 'ARRAY' in node.attrs['CLASS']:
|
|
1421
|
-
data, axes, nav_axes, is_spread = get_h5_data_from_node(node)
|
|
1422
|
-
# data, axes, nav_axes, is_spread = self.h5utils.get_h5_data(self.current_node_path)
|
|
1423
|
-
if isinstance(data, np.ndarray):
|
|
1424
|
-
if 'scan_type' in node.attrs.attrs_name:
|
|
1425
|
-
scan_type = node.attrs['scan_type']
|
|
1426
|
-
else:
|
|
1427
|
-
scan_type = ''
|
|
1428
|
-
self.hyperviewer.show_data(copy.deepcopy(data), nav_axes=nav_axes, is_spread=is_spread,
|
|
1429
|
-
scan_type=scan_type, **copy.deepcopy(axes))
|
|
1430
|
-
self.hyperviewer.init_ROI()
|
|
1431
|
-
elif isinstance(data, list):
|
|
1432
|
-
if not (not data):
|
|
1433
|
-
if isinstance(data[0], str):
|
|
1434
|
-
self.ui.text_list.clear()
|
|
1435
|
-
for txt in data:
|
|
1436
|
-
self.ui.text_list.addItem(txt)
|
|
1437
|
-
except Exception as e:
|
|
1438
|
-
logger.exception(str(e))
|
|
1439
|
-
|
|
1440
|
-
def populate_tree(self):
|
|
1441
|
-
"""
|
|
1442
|
-
| Init the ui-tree and store data into calling the h5_tree_to_Qtree convertor method
|
|
1443
|
-
|
|
1444
|
-
See Also
|
|
1445
|
-
--------
|
|
1446
|
-
h5tree_to_QTree, update_status
|
|
1447
|
-
"""
|
|
1448
|
-
try:
|
|
1449
|
-
if self.h5utils.h5file is not None:
|
|
1450
|
-
self.ui.h5file_tree.ui.Tree.clear()
|
|
1451
|
-
base_node = self.h5utils.root()
|
|
1452
|
-
base_tree_item, pixmap_items = h5tree_to_QTree(base_node)
|
|
1453
|
-
self.ui.h5file_tree.ui.Tree.addTopLevelItem(base_tree_item)
|
|
1454
|
-
self.add_widget_totree(pixmap_items)
|
|
1455
|
-
|
|
1456
|
-
except Exception as e:
|
|
1457
|
-
logger.exception(str(e))
|
|
1458
|
-
|
|
1459
|
-
def add_widget_totree(self, pixmap_items):
|
|
1460
|
-
|
|
1461
|
-
for item in pixmap_items:
|
|
1462
|
-
widget = QtWidgets.QWidget()
|
|
1463
|
-
|
|
1464
|
-
vLayout = QtWidgets.QVBoxLayout()
|
|
1465
|
-
label1D = QtWidgets.QLabel()
|
|
1466
|
-
bytes = QByteArray(item['node'].attrs['pixmap1D'])
|
|
1467
|
-
im1 = QtGui.QImage.fromData(bytes)
|
|
1468
|
-
a = QtGui.QPixmap.fromImage(im1)
|
|
1469
|
-
label1D.setPixmap(a)
|
|
1470
|
-
|
|
1471
|
-
label2D = QtWidgets.QLabel()
|
|
1472
|
-
bytes = QByteArray(item['node'].attrs['pixmap2D'])
|
|
1473
|
-
im2 = QtGui.QImage.fromData(bytes)
|
|
1474
|
-
b = QtGui.QPixmap.fromImage(im2)
|
|
1475
|
-
label2D.setPixmap(b)
|
|
1476
|
-
|
|
1477
|
-
vLayout.addWidget(label1D)
|
|
1478
|
-
vLayout.addwidget(label2D)
|
|
1479
|
-
widget.setLayout(vLayout)
|
|
1480
|
-
self.ui.h5file_tree.ui.Tree.setItemWidget(item['item'], 1, widget)
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
def browse_data(fname=None, ret_all=False, message=None):
|
|
1484
|
-
"""
|
|
1485
|
-
| Browse data present in any h5 file, when user has selected the one,
|
|
1486
|
-
"""
|
|
1487
|
-
if fname is None:
|
|
1488
|
-
fname = str(select_file(start_path=config('data_saving', 'h5file', 'save_path'), save=False, ext='h5'))
|
|
1489
|
-
|
|
1490
|
-
if type(fname) != str:
|
|
1491
|
-
try:
|
|
1492
|
-
fname = str(fname)
|
|
1493
|
-
except Exception:
|
|
1494
|
-
raise Exception('filename in browse data is not valid')
|
|
1495
|
-
if fname != '':
|
|
1496
|
-
(root, ext) = os.path.splitext(fname)
|
|
1497
|
-
if not ('h5' in ext or 'hdf5' in ext):
|
|
1498
|
-
warnings.warn('This is not a PyMODAQ h5 file, there could be issues', Warning)
|
|
1499
|
-
|
|
1500
|
-
form = QtWidgets.QWidget()
|
|
1501
|
-
browser = H5Browser(form, h5file_path=fname)
|
|
1502
|
-
|
|
1503
|
-
dialog = QtWidgets.QDialog()
|
|
1504
|
-
vlayout = QtWidgets.QVBoxLayout()
|
|
1505
|
-
|
|
1506
|
-
vlayout.addWidget(form)
|
|
1507
|
-
dialog.setLayout(vlayout)
|
|
1508
|
-
buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
|
|
1509
|
-
|
|
1510
|
-
buttonBox.addButton('OK', buttonBox.AcceptRole)
|
|
1511
|
-
buttonBox.accepted.connect(dialog.accept)
|
|
1512
|
-
buttonBox.addButton('Cancel', buttonBox.RejectRole)
|
|
1513
|
-
buttonBox.rejected.connect(dialog.reject)
|
|
1514
|
-
vlayout.addWidget(buttonBox)
|
|
1515
|
-
|
|
1516
|
-
dialog.setWindowTitle('Select a data node in the tree')
|
|
1517
|
-
if message is None or not isinstance(message, str):
|
|
1518
|
-
dialog.setWindowTitle('Select a data node in the tree')
|
|
1519
|
-
else:
|
|
1520
|
-
dialog.setWindowTitle(message)
|
|
1521
|
-
res = dialog.exec()
|
|
1522
|
-
|
|
1523
|
-
if res == dialog.Accepted:
|
|
1524
|
-
node_path = browser.current_node_path
|
|
1525
|
-
data = browser.h5utils.get_node(node_path).read()
|
|
1526
|
-
else:
|
|
1527
|
-
data = None
|
|
1528
|
-
node_path = None
|
|
1529
|
-
|
|
1530
|
-
browser.h5utils.close_file()
|
|
1531
|
-
|
|
1532
|
-
if ret_all:
|
|
1533
|
-
return data, fname, node_path
|
|
1534
|
-
else:
|
|
1535
|
-
return data
|
|
1536
|
-
return None, '', ''
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
### Additional imports. This is needed to register additional formats in the Exporterfactory
|
|
1540
|
-
|
|
1541
|
-
# This Exporter depends on the availability of the hyperspy package.
|
|
1542
|
-
# In the future, adding an optional dependency
|
|
1543
|
-
found = importlib.util.find_spec("hyperspy")
|
|
1544
|
-
if not found:
|
|
1545
|
-
logger.warning('Hyperspy module not found. To save data in the .hspy format, install hyperspy 1.7 or more recent.')
|
|
1546
|
-
else:
|
|
1547
|
-
import pymodaq.daq_utils.h5exporter_hyperspy
|
|
1548
|
-
|
|
1549
|
-
if __name__ == '__main__':
|
|
1550
|
-
app = QtWidgets.QApplication(sys.argv)
|
|
1551
|
-
win = QtWidgets.QMainWindow()
|
|
1552
|
-
area = DockArea()
|
|
1553
|
-
win.setCentralWidget(area)
|
|
1554
|
-
win.resize(1000, 500)
|
|
1555
|
-
win.setWindowTitle('PyMoDAQ H5Browser')
|
|
1556
|
-
prog = H5Browser(win)
|
|
1557
|
-
win.show()
|
|
1558
|
-
QtWidgets.QApplication.processEvents()
|
|
1559
|
-
sys.exit(app.exec_())
|