HDF5DataModel 0.2.3__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.
- HDF5DataModel/Model/__init__.py +0 -0
- HDF5DataModel/Model/attrsclasses.py +96 -0
- HDF5DataModel/Model/h5model.py +39 -0
- HDF5DataModel/Model/strict_parameter.py +28 -0
- HDF5DataModel/Model/subclasses.py +131 -0
- HDF5DataModel/View/__init__.py +0 -0
- HDF5DataModel/View/widget.py +853 -0
- HDF5DataModel/View/widget.ui +50 -0
- HDF5DataModel/__init__.py +1 -0
- HDF5DataModel/utils/__init__.py +0 -0
- HDF5DataModel/utils/customparametertree.py +31 -0
- HDF5DataModel/utils/customtreeview.py +205 -0
- HDF5DataModel/version.py +1 -0
- hdf5datamodel-0.2.3.dist-info/METADATA +43 -0
- hdf5datamodel-0.2.3.dist-info/RECORD +18 -0
- hdf5datamodel-0.2.3.dist-info/WHEEL +5 -0
- hdf5datamodel-0.2.3.dist-info/licenses/LICENSE +165 -0
- hdf5datamodel-0.2.3.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AttrBase:
|
|
9
|
+
def to_params(self):
|
|
10
|
+
params = []
|
|
11
|
+
for key, value in self.__dict__.items():
|
|
12
|
+
if key == 'start_time':
|
|
13
|
+
dt = datetime.fromtimestamp(value)
|
|
14
|
+
params.append({'name': key, 'type': 'str', 'value': dt.strftime('%d/%m/%y %H:%M:%S.%f')})
|
|
15
|
+
else:
|
|
16
|
+
typ, value = self.get_type_and_cast(value)
|
|
17
|
+
# params.append({'name': key, 'type': 'strict', 'value': value, 'opts': {'type': typ}}) # TODO : comprendre le fonctionnement de ParameterItem et parameterTypes
|
|
18
|
+
params.append({'name': key, 'type': typ, 'value': value})
|
|
19
|
+
return params
|
|
20
|
+
|
|
21
|
+
def get_type_and_cast(self, variable):
|
|
22
|
+
if isinstance(variable, str):
|
|
23
|
+
return 'str', variable
|
|
24
|
+
elif isinstance(variable, (int, np.int32, np.int64)):
|
|
25
|
+
return 'int', int(variable)
|
|
26
|
+
elif isinstance(variable, (float, np.float32, np.float64)):
|
|
27
|
+
return 'float', float(variable)
|
|
28
|
+
elif isinstance(variable, list):
|
|
29
|
+
return 'list', variable
|
|
30
|
+
elif isinstance(variable, bool):
|
|
31
|
+
return 'bool', bool(variable)
|
|
32
|
+
else:
|
|
33
|
+
return 'str', str(variable)
|
|
34
|
+
|
|
35
|
+
def set_attrs_from_dict(self, attrs_dict):
|
|
36
|
+
for key, value in attrs_dict.items():
|
|
37
|
+
setattr(self, key, value)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class DatasetAttrs(AttrBase):
|
|
42
|
+
version: str = 'HDF5DataModel 1.0'
|
|
43
|
+
start_time: float = 0.
|
|
44
|
+
experimenter: str = ''
|
|
45
|
+
description: str = ''
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AcquisitionAtrrs(AttrBase):
|
|
50
|
+
start_time: float = 0.
|
|
51
|
+
sample: str = ''
|
|
52
|
+
environment: str = ''
|
|
53
|
+
description: str = ''
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class AcqSigAttrs(AttrBase):
|
|
58
|
+
type: str = 'Acquisition attributes'
|
|
59
|
+
start_time: float = 0.
|
|
60
|
+
traces_number: int = 0
|
|
61
|
+
position_interval: float = 0
|
|
62
|
+
position_offset: float = 0
|
|
63
|
+
hardware: str = '' # hardware used to acquire the trace
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class GenSigAttrs(AttrBase):
|
|
68
|
+
type: str = 'Generation attributes'
|
|
69
|
+
points_number: int = 0
|
|
70
|
+
central_freq: int = 0
|
|
71
|
+
sampling_freq: int = 0
|
|
72
|
+
duration: float = 0
|
|
73
|
+
transducer: str = ''
|
|
74
|
+
hardware: str = ''
|
|
75
|
+
amplitude: float = 0
|
|
76
|
+
position_x: float = 0
|
|
77
|
+
position_y: float = 0
|
|
78
|
+
position_z: float = 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class AcqTraceAttrs(AttrBase):
|
|
83
|
+
columns: List[str] = field(default_factory=list) # list of column names
|
|
84
|
+
units: List[str] = field(default_factory=list) # list of units for each column
|
|
85
|
+
start_time: float = 0 # start time of the trace, timestamp in seconds
|
|
86
|
+
duration: float = 0 # duration of the trace, seconds
|
|
87
|
+
position_x: float = 0. # position of the trace, meters
|
|
88
|
+
position_y: float = 0. # position of the trace, meters
|
|
89
|
+
position_z: float = 0. # position of the trace, meters
|
|
90
|
+
points_number: int = 0 # number of points in the trace
|
|
91
|
+
range: float = 0. # range of the trace
|
|
92
|
+
sampling_freq: int = 0 # sampling frequency of the trace, Hz
|
|
93
|
+
channel: int = 0 # acquisition channel of the trace
|
|
94
|
+
index: int = 0 # index of the trace in the acquisition signal
|
|
95
|
+
average_number: int = 0 # number of averaging
|
|
96
|
+
pretrig_duration: float = 0. # time between first sample and trig, second
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from PySide6.QtCore import Signal, QObject
|
|
3
|
+
from h5py import File
|
|
4
|
+
from HDF5DataModel.Model.subclasses import Dataset
|
|
5
|
+
from PySide6.QtWidgets import QWidget
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class H5DataModel(QObject):
|
|
9
|
+
modelUpdated = Signal()
|
|
10
|
+
|
|
11
|
+
def __init__(self, file_path=None):
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.file_path = file_path
|
|
14
|
+
self.file = None # ???
|
|
15
|
+
self.datasets = {}
|
|
16
|
+
|
|
17
|
+
def add_dataset(self, name):
|
|
18
|
+
self.datasets[name] = Dataset(name)
|
|
19
|
+
return self.datasets[name]
|
|
20
|
+
|
|
21
|
+
def to_hdf5_file(self):
|
|
22
|
+
with File(self.file_path, 'w') as f:
|
|
23
|
+
for name, dataset in self.datasets.items():
|
|
24
|
+
dataset.to_hdf5_file(f)
|
|
25
|
+
|
|
26
|
+
def get_datasets(self):
|
|
27
|
+
with File(self.file_path, 'r') as f:
|
|
28
|
+
for key in f.keys():
|
|
29
|
+
if f[key].attrs['version'][:-4] == 'HDF5DataModel':
|
|
30
|
+
dataset = Dataset()
|
|
31
|
+
dataset.from_h5(f, key)
|
|
32
|
+
self.datasets[key] = dataset
|
|
33
|
+
else:
|
|
34
|
+
warnings.warn('version not recognized')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
h5 = H5DataModel(r'C:\Users\devie\Documents\Programmes\HDF5DataModel\test.h5')
|
|
39
|
+
h5.get_datasets()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from pyqtgraph.parametertree import parameterTypes, ParameterItem
|
|
2
|
+
from PySide6 import QtWidgets
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class StrictParameterItem(ParameterItem):
|
|
6
|
+
def __init__(self, param, depth):
|
|
7
|
+
super().__init__(param, depth)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StrictParameter(parameterTypes.SimpleParameter):
|
|
11
|
+
def __init__(self, *args, **kwargs):
|
|
12
|
+
super().__init__(**kwargs)
|
|
13
|
+
|
|
14
|
+
def setValue(self, value, block_signal=None):
|
|
15
|
+
print(self.opts.get('type'), self.opts['type'])
|
|
16
|
+
if self.opts['type'] == int and not isinstance(value, int):
|
|
17
|
+
raise ValueError(f"Invalid value type: expected int, got {type(value).__name__}")
|
|
18
|
+
elif self.opts['type'] == float and not isinstance(value, float):
|
|
19
|
+
raise ValueError(f"Invalid value type: expected float, got {type(value).__name__}")
|
|
20
|
+
elif self.opts['type'] == str and not isinstance(value, str):
|
|
21
|
+
raise ValueError(f"Invalid value type: expected str, got {type(value).__name__}")
|
|
22
|
+
elif self.opts['type'] == list and not isinstance(value, list):
|
|
23
|
+
raise ValueError(f"Invalid value type: expected list, got {type(value).__name__}")
|
|
24
|
+
super().setValue(value, block_signal)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def itemClass(self):
|
|
28
|
+
return StrictParameterItem
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#from typing import Optional
|
|
2
|
+
import os
|
|
3
|
+
import collections
|
|
4
|
+
import numpy as np
|
|
5
|
+
#from HDF5DataModel.Model.h5model import H5DataModel
|
|
6
|
+
from HDF5DataModel.Model.attrsclasses import DatasetAttrs, AcquisitionAtrrs, AcqSigAttrs, GenSigAttrs, AcqTraceAttrs
|
|
7
|
+
|
|
8
|
+
from pyseg2.rawseg2 import write_raw_seg2
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Dataset:
|
|
12
|
+
def __init__(self, name='default', parent: "H5DataModel"=None):
|
|
13
|
+
self.name = name
|
|
14
|
+
self.attrs = DatasetAttrs()
|
|
15
|
+
self.acquisitions = collections.OrderedDict()
|
|
16
|
+
self.parent = parent # the parent H5DataModel of this dataset
|
|
17
|
+
|
|
18
|
+
def add_acquisition(self, name):
|
|
19
|
+
self.acquisitions[name] = Acquisition()
|
|
20
|
+
return self.acquisitions[name]
|
|
21
|
+
|
|
22
|
+
def to_hdf5_file(self, h5file):
|
|
23
|
+
"""Save the experiment to a HDF5 file"""
|
|
24
|
+
experiment_group = h5file.create_group(self.name)
|
|
25
|
+
experiment_group.attrs.update(self.attrs.__dict__)
|
|
26
|
+
for acq_name, acquisition in self.acquisitions.items():
|
|
27
|
+
acquisition_group = experiment_group.create_group(acq_name)
|
|
28
|
+
acquisition_group.attrs.update(acquisition.attrs.__dict__)
|
|
29
|
+
for sig_name, acq_sig in acquisition.acq_sigs.items():
|
|
30
|
+
acq_sig_group = acquisition_group.create_group(sig_name)
|
|
31
|
+
acq_sig_group.attrs.update(acq_sig.attrs.__dict__)
|
|
32
|
+
for trace_name, trace in acq_sig.traces.items():
|
|
33
|
+
trace_group = acq_sig_group.create_group(trace_name)
|
|
34
|
+
trace_group.attrs.update(trace.attrs.__dict__)
|
|
35
|
+
trace_group.create_dataset('signal', data=trace.data)
|
|
36
|
+
for gen_name, gen_sig in acquisition.gen_sigs.items():
|
|
37
|
+
gen_sig_group = acquisition_group.create_group(gen_name)
|
|
38
|
+
gen_sig_group.attrs.update(gen_sig.attrs.__dict__)
|
|
39
|
+
gen_sig_group.create_dataset('signal', data=gen_sig.signal)
|
|
40
|
+
|
|
41
|
+
def from_h5(self, h5file, experiment_name):
|
|
42
|
+
"""Load the experiment from a HDF5 file"""
|
|
43
|
+
self.name = experiment_name
|
|
44
|
+
experiment_group = h5file[experiment_name]
|
|
45
|
+
self.attrs = DatasetAttrs()
|
|
46
|
+
self.attrs.set_attrs_from_dict(dict(experiment_group.attrs))
|
|
47
|
+
for acq_name in experiment_group:
|
|
48
|
+
acquisition_group = experiment_group[acq_name]
|
|
49
|
+
self.add_acquisition(acq_name)
|
|
50
|
+
self.acquisitions[acq_name].attrs = AcquisitionAtrrs()
|
|
51
|
+
self.acquisitions[acq_name].attrs.set_attrs_from_dict(dict(acquisition_group.attrs))
|
|
52
|
+
for sig_name in acquisition_group:
|
|
53
|
+
if acquisition_group[sig_name].attrs['type'] == 'Acquisition attributes':
|
|
54
|
+
self.acquisitions[acq_name].add_acq_sig(sig_name)
|
|
55
|
+
acq_sig_group = acquisition_group[sig_name]
|
|
56
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].attrs = AcqSigAttrs()
|
|
57
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].attrs.set_attrs_from_dict(dict(acq_sig_group.attrs))
|
|
58
|
+
for trace_name in acq_sig_group:
|
|
59
|
+
trace_group = acq_sig_group[trace_name]
|
|
60
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].add_trace(trace_name)
|
|
61
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].traces[trace_name].attrs = AcqTraceAttrs()
|
|
62
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].traces[trace_name].attrs.set_attrs_from_dict(dict(trace_group.attrs))
|
|
63
|
+
self.acquisitions[acq_name].acq_sigs[sig_name].traces[trace_name].data = np.array(trace_group['signal'])
|
|
64
|
+
elif acquisition_group[sig_name].attrs['type'] == 'Generation attributes':
|
|
65
|
+
self.acquisitions[acq_name].add_gen_sig(sig_name)
|
|
66
|
+
gen_sig_group = acquisition_group[sig_name]
|
|
67
|
+
self.acquisitions[acq_name].gen_sigs[sig_name].attrs = GenSigAttrs()
|
|
68
|
+
self.acquisitions[acq_name].gen_sigs[sig_name].attrs.set_attrs_from_dict(dict(gen_sig_group.attrs))
|
|
69
|
+
self.acquisitions[acq_name].gen_sigs[sig_name].signal = np.array(gen_sig_group['signal'])
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Acquisition:
|
|
73
|
+
"""An acquisition is a set of one generation signal and multiple acquisition signals with the sample and config"""
|
|
74
|
+
def __init__(self, parent: "Dataset"=None):
|
|
75
|
+
self.attrs = AcquisitionAtrrs()
|
|
76
|
+
self.acq_sigs = collections.OrderedDict()
|
|
77
|
+
self.gen_sigs = collections.OrderedDict()
|
|
78
|
+
self.parent = parent # the Parent Acquisition of self
|
|
79
|
+
|
|
80
|
+
def add_acq_sig(self, name):
|
|
81
|
+
self.acq_sigs[name] = AcqSig()
|
|
82
|
+
return self.acq_sigs[name]
|
|
83
|
+
|
|
84
|
+
def add_gen_sig(self, name):
|
|
85
|
+
self.gen_sigs[name] = GenSig()
|
|
86
|
+
return self.gen_sigs[name]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AcqSig:
|
|
90
|
+
def __init__(self, parent: "Acquisition"=None):
|
|
91
|
+
self.attrs = AcqSigAttrs()
|
|
92
|
+
self.traces = collections.OrderedDict()
|
|
93
|
+
self.parent = parent
|
|
94
|
+
|
|
95
|
+
def add_trace(self, name):
|
|
96
|
+
self.traces[name] = AcqTrace()
|
|
97
|
+
return self.traces[name]
|
|
98
|
+
|
|
99
|
+
def to_seg2(self, filename: str, allow_overwrite: bool=False, include_type_names: bool=False):
|
|
100
|
+
|
|
101
|
+
trace_header_and_data = []
|
|
102
|
+
for trace in self.traces.values():
|
|
103
|
+
trace: AcqTrace
|
|
104
|
+
|
|
105
|
+
# FILL THE MANDATRY ATTRIBUTES
|
|
106
|
+
trace.attrs.SAMPLE_INTERVAL = 1. / trace.attrs.sampling_freq
|
|
107
|
+
# ...
|
|
108
|
+
|
|
109
|
+
trace_header_and_data.append((trace.attrs.__dict__, trace.data))
|
|
110
|
+
|
|
111
|
+
write_raw_seg2(
|
|
112
|
+
filename=filename,
|
|
113
|
+
file_header=self.attrs.__dict__,
|
|
114
|
+
trace_header_and_data=trace_header_and_data,
|
|
115
|
+
allow_overwrite=allow_overwrite,
|
|
116
|
+
include_type_names=include_type_names,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class GenSig:
|
|
121
|
+
def __init__(self, parent: "Acquisition"=None):
|
|
122
|
+
self.attrs = GenSigAttrs()
|
|
123
|
+
self.signal = np.zeros(1)
|
|
124
|
+
self.parent = parent # the parent Acquisition of self
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AcqTrace:
|
|
128
|
+
def __init__(self, parent: "AcqSig, GenSig"=None):
|
|
129
|
+
self.data = np.zeros(10)
|
|
130
|
+
self.attrs = AcqTraceAttrs()
|
|
131
|
+
self.parent = parent
|
|
File without changes
|