pymodaq_data 5.2.12__tar.gz → 5.3.0__tar.gz
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.
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/PKG-INFO +12 -12
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/README.rst +11 -11
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/data.py +100 -45
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/backends.py +96 -41
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/data_saving.py +87 -19
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/saving.py +28 -9
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/plotter/plotters/matplotlib_plotters.py +14 -3
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/utils.py +2 -1
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/resources/config_template.toml +1 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/.gitignore +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/LICENSE +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/pyproject.toml +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/config.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/browsing.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/exporter.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/exporters/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/exporters/base.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/exporters/flimj.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/exporters/hyperspy.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/swmr.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/h5modules/utils.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/icon.ico +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/numpy_func.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/plotter/plotter.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/plotter/plotters/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/post_treatment/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/post_treatment/process_to_scalar.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/resources/__init__.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/slicing.py +0 -0
- {pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/splash.png +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pymodaq_data
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.3.0
|
|
4
4
|
Summary: Modular Data Acquisition with Python
|
|
5
5
|
Project-URL: Homepage, http://pymodaq.cnrs.fr
|
|
6
6
|
Project-URL: Source, https://github.com/PyMoDAQ/PyMoDAQ
|
|
@@ -71,11 +71,11 @@ PyMoDAQ Data
|
|
|
71
71
|
:target: https://pypi.org/project/pymodaq_data/
|
|
72
72
|
:alt: Latest Version
|
|
73
73
|
|
|
74
|
-
.. image:: https://readthedocs.org/projects/pymodaq/badge/?version=
|
|
75
|
-
:target: https://pymodaq.readthedocs.io/en/stable/?badge=
|
|
74
|
+
.. image:: https://readthedocs.org/projects/pymodaq/badge/?version=patch-3
|
|
75
|
+
:target: https://pymodaq.readthedocs.io/en/stable/?badge=patch-3
|
|
76
76
|
:alt: Documentation Status
|
|
77
77
|
|
|
78
|
-
.. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/
|
|
78
|
+
.. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/patch-3/graph/badge.svg?token=IQNJRCQDM2
|
|
79
79
|
:target: https://codecov.io/gh/PyMoDAQ/PyMoDAQ
|
|
80
80
|
|
|
81
81
|
+-------------+-------------+---------------+
|
|
@@ -94,28 +94,28 @@ PyMoDAQ Data
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
.. |310-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
97
|
+
.. |310-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.10.svg
|
|
98
98
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
99
99
|
|
|
100
|
-
.. |311-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
100
|
+
.. |311-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.11.svg
|
|
101
101
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
102
102
|
|
|
103
|
-
.. |312-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
103
|
+
.. |312-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.12.svg
|
|
104
104
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
105
105
|
|
|
106
|
-
.. |313-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
106
|
+
.. |313-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.13.svg
|
|
107
107
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
108
108
|
|
|
109
|
-
.. |310-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
109
|
+
.. |310-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.10.svg
|
|
110
110
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
111
111
|
|
|
112
|
-
.. |311-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
112
|
+
.. |311-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.11.svg
|
|
113
113
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
114
114
|
|
|
115
|
-
.. |312-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
115
|
+
.. |312-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.12.svg
|
|
116
116
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
117
117
|
|
|
118
|
-
.. |313-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
118
|
+
.. |313-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.13.svg
|
|
119
119
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
120
120
|
|
|
121
121
|
|
|
@@ -11,11 +11,11 @@ PyMoDAQ Data
|
|
|
11
11
|
:target: https://pypi.org/project/pymodaq_data/
|
|
12
12
|
:alt: Latest Version
|
|
13
13
|
|
|
14
|
-
.. image:: https://readthedocs.org/projects/pymodaq/badge/?version=
|
|
15
|
-
:target: https://pymodaq.readthedocs.io/en/stable/?badge=
|
|
14
|
+
.. image:: https://readthedocs.org/projects/pymodaq/badge/?version=patch-3
|
|
15
|
+
:target: https://pymodaq.readthedocs.io/en/stable/?badge=patch-3
|
|
16
16
|
:alt: Documentation Status
|
|
17
17
|
|
|
18
|
-
.. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/
|
|
18
|
+
.. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/patch-3/graph/badge.svg?token=IQNJRCQDM2
|
|
19
19
|
:target: https://codecov.io/gh/PyMoDAQ/PyMoDAQ
|
|
20
20
|
|
|
21
21
|
+-------------+-------------+---------------+
|
|
@@ -34,28 +34,28 @@ PyMoDAQ Data
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
.. |310-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
37
|
+
.. |310-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.10.svg
|
|
38
38
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
39
39
|
|
|
40
|
-
.. |311-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
40
|
+
.. |311-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.11.svg
|
|
41
41
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
42
42
|
|
|
43
|
-
.. |312-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
43
|
+
.. |312-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.12.svg
|
|
44
44
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
45
45
|
|
|
46
|
-
.. |313-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
46
|
+
.. |313-linux| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Linux_3.13.svg
|
|
47
47
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
48
48
|
|
|
49
|
-
.. |310-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
49
|
+
.. |310-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.10.svg
|
|
50
50
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
51
51
|
|
|
52
|
-
.. |311-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
52
|
+
.. |311-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.11.svg
|
|
53
53
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
54
54
|
|
|
55
|
-
.. |312-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
55
|
+
.. |312-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.12.svg
|
|
56
56
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
57
57
|
|
|
58
|
-
.. |313-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/
|
|
58
|
+
.. |313-windows| image:: https://raw.githubusercontent.com/PyMoDAQ/PyMoDAQ/badges/pymodaq_data/patch-3/tests_Windows_3.13.svg
|
|
59
59
|
:target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/tests-data.yml
|
|
60
60
|
|
|
61
61
|
|
|
@@ -86,7 +86,9 @@ def dimensionless_aware_reduce_units(q: Type[Q_]) -> Type[Q_]:
|
|
|
86
86
|
-------
|
|
87
87
|
The reduced quantity
|
|
88
88
|
"""
|
|
89
|
-
|
|
89
|
+
# this may fire a pint.errors.UndefinedBehavior warning when the magnitude is a numpy array
|
|
90
|
+
# but pint https://github.com/hgrecco/pint/issues/2274 fixed this in July 2026
|
|
91
|
+
# at the moment, it just returns the same quantity layout
|
|
90
92
|
return q.to_compact() if q.dimensionless else q.to_reduced_units()
|
|
91
93
|
|
|
92
94
|
def check_units(units: str):
|
|
@@ -529,6 +531,16 @@ class Axis(SerializableBase):
|
|
|
529
531
|
if self._data is None:
|
|
530
532
|
self._size = _size
|
|
531
533
|
|
|
534
|
+
@property
|
|
535
|
+
def axis_width(self) -> Q_:
|
|
536
|
+
"""get the width of the axis in axis unit
|
|
537
|
+
|
|
538
|
+
That is the difference between the max value and the min value of the axis
|
|
539
|
+
"""
|
|
540
|
+
if self.scaling is not None:
|
|
541
|
+
return Q_(self.size * self.scaling, self.units)
|
|
542
|
+
else:
|
|
543
|
+
return Q_(self.get_data().max() - self.get_data().min(), self.units)
|
|
532
544
|
@staticmethod
|
|
533
545
|
def _check_index_valid(index: int):
|
|
534
546
|
if not isinstance(index, int):
|
|
@@ -653,10 +665,10 @@ class Axis(SerializableBase):
|
|
|
653
665
|
"""find the index of the threshold value within the axis"""
|
|
654
666
|
if isinstance(threshold, Q_):
|
|
655
667
|
threshold = threshold.m_as(self.units)
|
|
656
|
-
if threshold
|
|
668
|
+
if threshold <= self.min():
|
|
657
669
|
return 0
|
|
658
|
-
elif threshold
|
|
659
|
-
return len(self)
|
|
670
|
+
elif threshold >= self.max():
|
|
671
|
+
return len(self)
|
|
660
672
|
elif self._data is not None:
|
|
661
673
|
return mutils.find_index(self._data, threshold)[0][0]
|
|
662
674
|
else:
|
|
@@ -702,7 +714,7 @@ class DataLowLevel:
|
|
|
702
714
|
|
|
703
715
|
@name.setter
|
|
704
716
|
def name(self, other_name: str):
|
|
705
|
-
self._name = other_name
|
|
717
|
+
self._name = str(other_name)
|
|
706
718
|
|
|
707
719
|
@property
|
|
708
720
|
def timestamp(self):
|
|
@@ -2141,6 +2153,10 @@ class DataWithAxes(DataBase, SerializableBase):
|
|
|
2141
2153
|
dwa.timestamp = timestamp
|
|
2142
2154
|
return dwa, remaining_bytes
|
|
2143
2155
|
|
|
2156
|
+
def get_axes_sizes(self) -> Iterable[Q_]:
|
|
2157
|
+
self.create_missing_axes()
|
|
2158
|
+
return [self.get_axis_from_index(index)[0].axis_width for index in range(len(self.axes))]
|
|
2159
|
+
|
|
2144
2160
|
def check_axes_linear(self, axes: List[Axis] = None) -> bool:
|
|
2145
2161
|
""" Check if any axis may be non linear
|
|
2146
2162
|
|
|
@@ -3390,19 +3406,67 @@ class DataToExport(DataLowLevel, SerializableBase):
|
|
|
3390
3406
|
if dat.origin is None or dat.origin == '':
|
|
3391
3407
|
dat.origin = self.name
|
|
3392
3408
|
|
|
3409
|
+
def _combine(self, other: object, op: Callable, reflected: bool = False) -> 'DataToExport':
|
|
3410
|
+
"""Combine self and other elementwise, applying ``op`` to every element.
|
|
3411
|
+
|
|
3412
|
+
*other* may be:
|
|
3413
|
+
|
|
3414
|
+
- a plain number: broadcast to every element. Whether this is
|
|
3415
|
+
actually valid is decided by DataWithAxes/pint arithmetic itself,
|
|
3416
|
+
not pre-judged here — e.g. adding a bare number only makes sense
|
|
3417
|
+
for dimensionless data (pint raises otherwise), while multiplying
|
|
3418
|
+
by one is always valid regardless of units.
|
|
3419
|
+
- another DataToExport of the same length: paired by list position
|
|
3420
|
+
(index i of self with index i of other), not by name.
|
|
3421
|
+
DataToExport is used to combine structurally-parallel containers
|
|
3422
|
+
(e.g. successive snapshots from the same DAQ_Scan/ModulesManager
|
|
3423
|
+
run, always iterated in the same order), and existing callers rely
|
|
3424
|
+
on being able to combine elements whose *names* legitimately
|
|
3425
|
+
differ between the two operands (e.g. a signal container and a
|
|
3426
|
+
differently-named background container). Only the count and,
|
|
3427
|
+
per-pair, the array shapes/units need to match — the latter
|
|
3428
|
+
enforced by DataWithAxes arithmetic itself.
|
|
3429
|
+
|
|
3430
|
+
Parameters
|
|
3431
|
+
----------
|
|
3432
|
+
other: DataToExport or numbers.Number
|
|
3433
|
+
op: Callable
|
|
3434
|
+
Binary operation applied to each pair, e.g. ``np.subtract``. Its
|
|
3435
|
+
``__name__`` is used in error messages.
|
|
3436
|
+
reflected: bool
|
|
3437
|
+
If True, apply ``op(other_i, self_i)`` instead of
|
|
3438
|
+
``op(self_i, other_i)``, for the non-commutative ``__r*__`` methods.
|
|
3439
|
+
"""
|
|
3440
|
+
if isinstance(other, numbers.Number):
|
|
3441
|
+
others = [other] * len(self)
|
|
3442
|
+
elif not isinstance(other, DataToExport):
|
|
3443
|
+
raise TypeError(f'Could not {op.__name__} a {self.__class__.__name__} and a '
|
|
3444
|
+
f'{other.__class__.__name__}: only a number or another '
|
|
3445
|
+
f'{self.__class__.__name__} is supported')
|
|
3446
|
+
elif len(other) != len(self):
|
|
3447
|
+
raise TypeError(f'Could not {op.__name__} a {other.__class__.__name__} with a '
|
|
3448
|
+
f'{self.__class__.__name__} of a different length')
|
|
3449
|
+
else:
|
|
3450
|
+
others = other
|
|
3451
|
+
new_data = copy.deepcopy(self)
|
|
3452
|
+
for ind_dfp, (dwa, other_dwa) in enumerate(zip(self, others)):
|
|
3453
|
+
new_data[ind_dfp] = op(other_dwa, dwa) if reflected else op(dwa, other_dwa)
|
|
3454
|
+
return new_data
|
|
3455
|
+
|
|
3393
3456
|
def __sub__(self, other: object):
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
return new_data
|
|
3399
|
-
else:
|
|
3400
|
-
raise TypeError(f'Could not substract a {other.__class__.__name__} or a {self.__class__.__name__} '
|
|
3401
|
-
f'of a different length')
|
|
3457
|
+
return self._combine(other, np.subtract)
|
|
3458
|
+
|
|
3459
|
+
def __rsub__(self, other: object):
|
|
3460
|
+
return self._combine(other, np.subtract, reflected=True)
|
|
3402
3461
|
|
|
3403
3462
|
def __eq__(self, other):
|
|
3404
3463
|
if not isinstance(other, DataToExport):
|
|
3405
3464
|
return False
|
|
3465
|
+
if len(self) != len(other):
|
|
3466
|
+
# zip() below would otherwise silently truncate to the shorter of
|
|
3467
|
+
# the two, letting a DataToExport compare equal to a strict
|
|
3468
|
+
# superset of itself.
|
|
3469
|
+
return False
|
|
3406
3470
|
|
|
3407
3471
|
for dwa, other_dwa in zip(self, other):
|
|
3408
3472
|
if dwa != other_dwa:
|
|
@@ -3417,31 +3481,20 @@ class DataToExport(DataLowLevel, SerializableBase):
|
|
|
3417
3481
|
|
|
3418
3482
|
|
|
3419
3483
|
def __add__(self, other: object):
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
new_data[ind_dfp] = self[ind_dfp] + other[ind_dfp]
|
|
3424
|
-
return new_data
|
|
3425
|
-
else:
|
|
3426
|
-
raise TypeError(f'Could not add a {other.__class__.__name__} or a {self.__class__.__name__} '
|
|
3427
|
-
f'of a different length')
|
|
3484
|
+
return self._combine(other, np.add)
|
|
3485
|
+
|
|
3486
|
+
__radd__ = __add__ # addition is commutative
|
|
3428
3487
|
|
|
3429
3488
|
def __mul__(self, other: object):
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
new_data[ind_dfp] = self[ind_dfp] * other
|
|
3434
|
-
return new_data
|
|
3435
|
-
else:
|
|
3436
|
-
raise TypeError(f'Could not multiply a {other.__class__.__name__} with a {self.__class__.__name__} '
|
|
3437
|
-
f'of a different length')
|
|
3489
|
+
return self._combine(other, np.multiply)
|
|
3490
|
+
|
|
3491
|
+
__rmul__ = __mul__ # multiplication is commutative
|
|
3438
3492
|
|
|
3439
3493
|
def __truediv__(self, other: object):
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
f'of a different length')
|
|
3494
|
+
return self._combine(other, np.true_divide)
|
|
3495
|
+
|
|
3496
|
+
def __rtruediv__(self, other: object):
|
|
3497
|
+
return self._combine(other, np.true_divide, reflected=True)
|
|
3445
3498
|
|
|
3446
3499
|
def average(self, other: DataToExport, weight: int) -> DataToExport:
|
|
3447
3500
|
""" Compute the weighted average between self and other DataToExport and attributes it to self
|
|
@@ -3453,14 +3506,10 @@ class DataToExport(DataLowLevel, SerializableBase):
|
|
|
3453
3506
|
The weight the 'other_data' holds with respect to self
|
|
3454
3507
|
|
|
3455
3508
|
"""
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
return new_data
|
|
3461
|
-
else:
|
|
3462
|
-
raise TypeError(f'Could not average a {other.__class__.__name__} with a {self.__class__.__name__} '
|
|
3463
|
-
f'of a different length')
|
|
3509
|
+
def average(dwa: DataWithAxes, other_dwa: DataWithAxes) -> DataWithAxes:
|
|
3510
|
+
return dwa.average(other_dwa, weight)
|
|
3511
|
+
|
|
3512
|
+
return self._combine(other, average)
|
|
3464
3513
|
|
|
3465
3514
|
def merge_as_dwa(self, dim: Union[str, DataDim], name: str = None) -> DataRaw:
|
|
3466
3515
|
""" attempt to merge filtered dwa into one
|
|
@@ -3747,9 +3796,16 @@ class DataToExport(DataLowLevel, SerializableBase):
|
|
|
3747
3796
|
data, _ = find_objects_in_list_from_attr_name_val(self.data, 'name', name, return_first=True)
|
|
3748
3797
|
return data
|
|
3749
3798
|
|
|
3750
|
-
def get_data_from_names(self, names: List[str]) -> DataToExport:
|
|
3799
|
+
def get_data_from_names(self, names: List[str] | str) -> DataToExport:
|
|
3800
|
+
if isinstance(names, str):
|
|
3801
|
+
names = [names]
|
|
3751
3802
|
return DataToExport(self.name, data=[dwa for dwa in self if dwa.name in names])
|
|
3752
3803
|
|
|
3804
|
+
def get_data_from_origins(self, origins: list[str] | str) -> DataToExport:
|
|
3805
|
+
if isinstance(origins, str):
|
|
3806
|
+
origins = [origins]
|
|
3807
|
+
return DataToExport(self.name, data=[dwa for dwa in self if dwa.origin in origins])
|
|
3808
|
+
|
|
3753
3809
|
def get_data_from_name_origin(self, name: str, origin: str = '') -> DataWithAxes:
|
|
3754
3810
|
"""Get the data matching the given name and the given origin"""
|
|
3755
3811
|
if origin == '':
|
|
@@ -3771,9 +3827,8 @@ class DataToExport(DataLowLevel, SerializableBase):
|
|
|
3771
3827
|
return ind
|
|
3772
3828
|
raise ValueError
|
|
3773
3829
|
|
|
3774
|
-
def index_from_name_origin(self, name: str, origin: str = '') ->
|
|
3830
|
+
def index_from_name_origin(self, name: str, origin: str = '') -> int:
|
|
3775
3831
|
"""Get the index of a given DataWithAxes within the list of data"""
|
|
3776
|
-
"""Get the data matching the given name and the given origin"""
|
|
3777
3832
|
if origin == '':
|
|
3778
3833
|
_, index = find_objects_in_list_from_attr_name_val(self.data, 'name', name, return_first=True)
|
|
3779
3834
|
else:
|
|
@@ -242,15 +242,13 @@ class Node(object):
|
|
|
242
242
|
return self.attrs['TITLE']
|
|
243
243
|
|
|
244
244
|
@property
|
|
245
|
-
def path(self):
|
|
245
|
+
def path(self) -> str:
|
|
246
246
|
"""return node path
|
|
247
|
-
Parameters
|
|
248
|
-
----------
|
|
249
|
-
node (str or node instance), see h5py and pytables documentation on nodes
|
|
250
247
|
|
|
251
248
|
Returns
|
|
252
249
|
-------
|
|
253
|
-
str
|
|
250
|
+
str
|
|
251
|
+
full path of the node
|
|
254
252
|
"""
|
|
255
253
|
if self.backend == 'tables':
|
|
256
254
|
return self._node._v_pathname
|
|
@@ -288,7 +286,8 @@ class GROUP(Node):
|
|
|
288
286
|
|
|
289
287
|
Returns
|
|
290
288
|
-------
|
|
291
|
-
dict
|
|
289
|
+
dict
|
|
290
|
+
keys are children node names, values are the children nodes
|
|
292
291
|
|
|
293
292
|
See Also
|
|
294
293
|
--------
|
|
@@ -387,8 +386,8 @@ class EARRAY(CARRAY):
|
|
|
387
386
|
The data to append could be:
|
|
388
387
|
|
|
389
388
|
* a single element (without the enlargeable shape index that is always the first
|
|
390
|
-
|
|
391
|
-
|
|
389
|
+
index, that is of shape length n). In that case the first index of the enlargeable array
|
|
390
|
+
is increased by one.
|
|
392
391
|
* an ensemble of elements (a ndarray) of shape length of (n+1).
|
|
393
392
|
|
|
394
393
|
Parameters
|
|
@@ -575,6 +574,11 @@ class H5Backend:
|
|
|
575
574
|
self._swmr_enabled = False
|
|
576
575
|
self.set_backend(backend)
|
|
577
576
|
|
|
577
|
+
@property
|
|
578
|
+
def swmr_mode(self) -> bool:
|
|
579
|
+
return self._swmr_mode
|
|
580
|
+
|
|
581
|
+
|
|
578
582
|
def set_backend(self, backend: str):
|
|
579
583
|
"""Switch the active backend, closing any open file first.
|
|
580
584
|
|
|
@@ -658,7 +662,7 @@ class H5Backend:
|
|
|
658
662
|
return self._h5file
|
|
659
663
|
else:
|
|
660
664
|
if swmr_mode and self.backend == 'h5py':
|
|
661
|
-
if mode
|
|
665
|
+
if mode in ('w', 'a'):
|
|
662
666
|
kwargs['libver'] = 'latest'
|
|
663
667
|
elif mode == 'r':
|
|
664
668
|
kwargs['swmr'] = True
|
|
@@ -804,22 +808,24 @@ class H5Backend:
|
|
|
804
808
|
|
|
805
809
|
def get_set_group(self, where, name, title='', **kwargs):
|
|
806
810
|
"""Retrieve or create (if absent) a node group
|
|
811
|
+
|
|
807
812
|
Get attributed to the class attribute ``current_group``
|
|
808
813
|
|
|
809
814
|
Parameters
|
|
810
815
|
----------
|
|
811
|
-
where: str or
|
|
812
|
-
|
|
816
|
+
where: str or Node
|
|
817
|
+
path or parent node instance
|
|
813
818
|
name: str
|
|
814
|
-
|
|
819
|
+
group node name
|
|
815
820
|
title: str
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
Keyword Arguments:
|
|
821
|
+
node title
|
|
822
|
+
kwargs: dict
|
|
819
823
|
any other metadata related to this node (for example: origin)
|
|
824
|
+
|
|
820
825
|
Returns
|
|
821
826
|
-------
|
|
822
|
-
|
|
827
|
+
GROUP
|
|
828
|
+
the group node
|
|
823
829
|
"""
|
|
824
830
|
|
|
825
831
|
if isinstance(where, Node):
|
|
@@ -858,12 +864,13 @@ class H5Backend:
|
|
|
858
864
|
def is_node_in_group(self, where, name):
|
|
859
865
|
"""
|
|
860
866
|
Check if a given node with name is in the group defined by where (comparison on lower case strings)
|
|
867
|
+
|
|
861
868
|
Parameters
|
|
862
869
|
----------
|
|
863
|
-
where:
|
|
864
|
-
|
|
865
|
-
name:
|
|
866
|
-
|
|
870
|
+
where: str or Node
|
|
871
|
+
path or parent node instance
|
|
872
|
+
name: str
|
|
873
|
+
group node name
|
|
867
874
|
|
|
868
875
|
Returns
|
|
869
876
|
-------
|
|
@@ -940,13 +947,16 @@ class H5Backend:
|
|
|
940
947
|
|
|
941
948
|
def get_node_name(self, node):
|
|
942
949
|
"""return node name
|
|
950
|
+
|
|
943
951
|
Parameters
|
|
944
952
|
----------
|
|
945
|
-
node
|
|
953
|
+
node: str or Node
|
|
954
|
+
see h5py and pytables documentation on nodes
|
|
946
955
|
|
|
947
956
|
Returns
|
|
948
957
|
-------
|
|
949
|
-
str
|
|
958
|
+
str
|
|
959
|
+
name of the node
|
|
950
960
|
"""
|
|
951
961
|
if isinstance(node, Node):
|
|
952
962
|
node = node.node
|
|
@@ -954,13 +964,16 @@ class H5Backend:
|
|
|
954
964
|
|
|
955
965
|
def get_node_path(self, node):
|
|
956
966
|
"""return node path
|
|
967
|
+
|
|
957
968
|
Parameters
|
|
958
969
|
----------
|
|
959
|
-
node
|
|
970
|
+
node: str or Node
|
|
971
|
+
see h5py and pytables documentation on nodes
|
|
960
972
|
|
|
961
973
|
Returns
|
|
962
974
|
-------
|
|
963
|
-
str
|
|
975
|
+
str
|
|
976
|
+
full path of the node
|
|
964
977
|
"""
|
|
965
978
|
if isinstance(node, Node):
|
|
966
979
|
node = node.node
|
|
@@ -977,18 +990,19 @@ class H5Backend:
|
|
|
977
990
|
else:
|
|
978
991
|
return self.get_node(node.parent)
|
|
979
992
|
|
|
980
|
-
def get_children(self, where):
|
|
993
|
+
def get_children(self, where) -> dict[str, Node]:
|
|
981
994
|
"""Get a dict containing all children node hanging from where with their name as keys and types among Node,
|
|
982
995
|
CARRAY, EARRAY, VLARRAY or StringARRAY
|
|
983
996
|
|
|
984
997
|
Parameters
|
|
985
998
|
----------
|
|
986
|
-
where
|
|
999
|
+
where: str or Node
|
|
987
1000
|
see h5py and pytables documentation on nodes, and Node objects of this module
|
|
988
1001
|
|
|
989
1002
|
Returns
|
|
990
1003
|
-------
|
|
991
|
-
dict
|
|
1004
|
+
dict
|
|
1005
|
+
keys are children node names, values are the children nodes
|
|
992
1006
|
|
|
993
1007
|
See Also
|
|
994
1008
|
--------
|
|
@@ -1023,12 +1037,45 @@ class H5Backend:
|
|
|
1023
1037
|
children[child_name] = _cls(child, self.backend)
|
|
1024
1038
|
return children
|
|
1025
1039
|
|
|
1026
|
-
def walk_nodes(self, where):
|
|
1040
|
+
def walk_nodes(self, where: str| Node, depth: int = None, only_groups=False):
|
|
1041
|
+
""" Node Generator recursively iterating in the tree starting from where down to the specified depth
|
|
1042
|
+
(counted from where).
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
Parameters
|
|
1046
|
+
----------
|
|
1047
|
+
where: str | Node
|
|
1048
|
+
The starting node of the iteration
|
|
1049
|
+
depth: int
|
|
1050
|
+
The depth of the iteration in the h5 tree
|
|
1051
|
+
only_groups: bool
|
|
1052
|
+
if False (default) return all nodes otherwise retruns only GROUP instances
|
|
1053
|
+
|
|
1054
|
+
Yields
|
|
1055
|
+
------
|
|
1056
|
+
Node
|
|
1057
|
+
"""
|
|
1027
1058
|
where = self.get_node(where) # return a node object in case where is a string
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1059
|
+
if only_groups and where.attrs['CLASS'] != 'GROUP':
|
|
1060
|
+
pass
|
|
1061
|
+
else:
|
|
1062
|
+
yield where
|
|
1063
|
+
|
|
1064
|
+
if depth == 0:
|
|
1065
|
+
return
|
|
1066
|
+
else:
|
|
1067
|
+
for gr in self.walk_groups(where):
|
|
1068
|
+
for child in self.get_children(gr).values():
|
|
1069
|
+
node_path = child.path[len(where.path):]
|
|
1070
|
+
if node_path[0] == '/':
|
|
1071
|
+
node_path = node_path[1:]
|
|
1072
|
+
if depth is not None and node_path.count('/') == depth:
|
|
1073
|
+
return
|
|
1074
|
+
else:
|
|
1075
|
+
if only_groups and child.attrs['CLASS'] != 'GROUP':
|
|
1076
|
+
pass
|
|
1077
|
+
else:
|
|
1078
|
+
yield child
|
|
1032
1079
|
|
|
1033
1080
|
def walk_groups(self, where):
|
|
1034
1081
|
where = self.get_node(where) # return a node object in case where is a string
|
|
@@ -1126,14 +1173,18 @@ class H5Backend:
|
|
|
1126
1173
|
|
|
1127
1174
|
Parameters
|
|
1128
1175
|
----------
|
|
1129
|
-
where:
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1176
|
+
where: str
|
|
1177
|
+
group location in the file where to create the array node
|
|
1178
|
+
name: str
|
|
1179
|
+
name of the array
|
|
1180
|
+
dtype: dtype
|
|
1181
|
+
numpy dtype style, for particular case of strings, use dtype='string'
|
|
1182
|
+
title: str
|
|
1183
|
+
node title attribute (written in capitals)
|
|
1133
1184
|
|
|
1134
1185
|
Returns
|
|
1135
1186
|
-------
|
|
1136
|
-
|
|
1187
|
+
VLARRAY
|
|
1137
1188
|
|
|
1138
1189
|
"""
|
|
1139
1190
|
if isinstance(where, Node):
|
|
@@ -1189,15 +1240,19 @@ class H5Backend:
|
|
|
1189
1240
|
Add a node in the h5 file tree of the group type
|
|
1190
1241
|
Parameters
|
|
1191
1242
|
----------
|
|
1192
|
-
group_name:
|
|
1243
|
+
group_name: str
|
|
1244
|
+
a custom name for this group
|
|
1193
1245
|
group_type: str or GroupType enum
|
|
1194
1246
|
one of the possible values of GroupType, should be enforced by higher level modules not here
|
|
1195
|
-
where:
|
|
1196
|
-
|
|
1247
|
+
where: str or Node
|
|
1248
|
+
parent node where to create the new group
|
|
1249
|
+
metadata: dict
|
|
1250
|
+
extra metadata to be saved with this new group node
|
|
1197
1251
|
|
|
1198
1252
|
Returns
|
|
1199
1253
|
-------
|
|
1200
|
-
|
|
1254
|
+
GROUP
|
|
1255
|
+
newly created group node
|
|
1201
1256
|
"""
|
|
1202
1257
|
if metadata is None:
|
|
1203
1258
|
metadata = {}
|
|
@@ -4,6 +4,10 @@ Created the 21/11/2022
|
|
|
4
4
|
|
|
5
5
|
@author: Sebastien Weber
|
|
6
6
|
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
|
|
7
11
|
from time import time
|
|
8
12
|
from typing import Union, List, Tuple, Iterable
|
|
9
13
|
from pathlib import Path
|
|
@@ -18,7 +22,7 @@ from .saving import DataType, H5SaverLowLevel
|
|
|
18
22
|
from .backends import GROUP, CARRAY, Node, EARRAY, NodeError, GroupType # noqa: F401
|
|
19
23
|
from pymodaq_utils.utils import capitalize
|
|
20
24
|
from pymodaq_data.h5modules.saving import SaveType
|
|
21
|
-
|
|
25
|
+
from .. import DataToExport, DataDistribution
|
|
22
26
|
|
|
23
27
|
SPECIAL_GROUP_NAMES = dict(nav_axes='NavAxes')
|
|
24
28
|
|
|
@@ -27,6 +31,21 @@ class AxisError(Exception):
|
|
|
27
31
|
pass
|
|
28
32
|
|
|
29
33
|
|
|
34
|
+
@dataclass
|
|
35
|
+
class DataBundle:
|
|
36
|
+
"""Convenience class to hold data to be saved or plotted"""
|
|
37
|
+
dte: DataToExport
|
|
38
|
+
node: str = None
|
|
39
|
+
settings: str = ''
|
|
40
|
+
indexes: list[int] = None # indexes within an eventual Extended array (see DAQ_Scan)
|
|
41
|
+
axis_values: list[float | np.ndarray] = None # axis values within an eventual Enlargeable array (see Optimizers)
|
|
42
|
+
distribution: DataDistribution = field(
|
|
43
|
+
default_factory=lambda: DataDistribution.uniform
|
|
44
|
+
) # type of data to be saved
|
|
45
|
+
save_index: int = 0 # an index to know what step in the saving process we're in (see DAQ_Scan)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
30
49
|
class DataManagement(metaclass=ABCMeta):
|
|
31
50
|
"""Base abstract class to be used for all specialized object saving and loading data to/from a h5file
|
|
32
51
|
|
|
@@ -297,12 +316,17 @@ class DataSaverLoader(DataManagement):
|
|
|
297
316
|
data_type = DataType['data']
|
|
298
317
|
|
|
299
318
|
def __init__(self, h5saver: Union[H5SaverLowLevel, Path],
|
|
300
|
-
new_file: bool = False,
|
|
319
|
+
new_file: bool = False,
|
|
320
|
+
metadata: dict = None,
|
|
321
|
+
save_type=SaveType.custom,
|
|
322
|
+
swmr_mode=False):
|
|
301
323
|
self.data_type = enum_checker(DataType, self.data_type)
|
|
302
324
|
|
|
303
325
|
if isinstance(h5saver, (Path, str)):
|
|
304
326
|
h5saver = H5SaverLowLevel.from_file(h5saver, save_type,
|
|
305
|
-
new_file=new_file,
|
|
327
|
+
new_file=new_file,
|
|
328
|
+
metadata=metadata,
|
|
329
|
+
swmr_mode=swmr_mode)
|
|
306
330
|
|
|
307
331
|
self._h5saver = h5saver
|
|
308
332
|
self._axis_saver = AxisSaverLoader(self._h5saver)
|
|
@@ -708,7 +732,7 @@ class DataExtendedSaver(DataSaverLoader):
|
|
|
708
732
|
Parameters
|
|
709
733
|
----------
|
|
710
734
|
h5saver: H5SaverLowLevel
|
|
711
|
-
extended_shape:
|
|
735
|
+
extended_shape: Iterable[int]
|
|
712
736
|
the extra shape compared to the data the h5array will have
|
|
713
737
|
|
|
714
738
|
Attributes
|
|
@@ -718,7 +742,9 @@ class DataExtendedSaver(DataSaverLoader):
|
|
|
718
742
|
"""
|
|
719
743
|
data_type = DataType['data']
|
|
720
744
|
|
|
721
|
-
def __init__(self, h5saver: H5SaverLowLevel,
|
|
745
|
+
def __init__(self, h5saver: H5SaverLowLevel,
|
|
746
|
+
extended_shape: Iterable[int],
|
|
747
|
+
fill_value=None):
|
|
722
748
|
super().__init__(h5saver)
|
|
723
749
|
self.extended_shape = extended_shape
|
|
724
750
|
self.fill_value = fill_value
|
|
@@ -784,7 +810,9 @@ class DataExtendedSaver(DataSaverLoader):
|
|
|
784
810
|
# because there will be len(self.extended_shape) extra navigation axes
|
|
785
811
|
self._axis_saver.add_axis(where, axis)
|
|
786
812
|
|
|
787
|
-
def add_data(self, where: Union[Node, str],
|
|
813
|
+
def add_data(self, where: Union[Node, str],
|
|
814
|
+
data: DataWithAxes,
|
|
815
|
+
indexes: Iterable[int],
|
|
788
816
|
distribution=DataDistribution['uniform']):
|
|
789
817
|
"""Adds given DataWithAxes at a location within the initialized h5 array
|
|
790
818
|
|
|
@@ -835,6 +863,14 @@ class DataToExportSaver:
|
|
|
835
863
|
self._data_saver = DataSaverLoader(self._h5saver)
|
|
836
864
|
self._bkg_saver = BkgSaver(self._h5saver)
|
|
837
865
|
|
|
866
|
+
@property
|
|
867
|
+
def h5saver(self) -> H5SaverLowLevel:
|
|
868
|
+
return self._h5saver
|
|
869
|
+
|
|
870
|
+
@h5saver.setter
|
|
871
|
+
def h5saver(self, h5saver: H5SaverLowLevel):
|
|
872
|
+
self._h5saver = h5saver
|
|
873
|
+
|
|
838
874
|
def __getattr__(self, item):
|
|
839
875
|
""" Allows to call attributes of the underlying H5Saver object"""
|
|
840
876
|
|
|
@@ -868,6 +904,9 @@ class DataToExportSaver:
|
|
|
868
904
|
and formatted as below"""
|
|
869
905
|
return f'CH{ind:02d}'
|
|
870
906
|
|
|
907
|
+
def add_data_bundle(self, data: DataBundle):
|
|
908
|
+
self.add_data(data.node, data.dte, data.settings)
|
|
909
|
+
|
|
871
910
|
def add_data(self, where: Union[Node, str], data: DataToExport, settings_as_xml='', **kwargs):
|
|
872
911
|
"""
|
|
873
912
|
|
|
@@ -959,6 +998,9 @@ class DataToExportEnlargeableSaver(DataToExportSaver):
|
|
|
959
998
|
self._data_saver = DataEnlargeableSaver(self._h5saver)
|
|
960
999
|
self._nav_axis_saver = AxisSaverLoader(self._h5saver)
|
|
961
1000
|
|
|
1001
|
+
def add_data_bundle(self, data: DataBundle):
|
|
1002
|
+
self.add_data(data.node, data.dte, axis_values=data.axis_values, settings_as_xml=data.settings)
|
|
1003
|
+
|
|
962
1004
|
def add_data(self, where: Union[Node, str], data: DataToExport,
|
|
963
1005
|
axis_values: List[Union[float, np.ndarray]] = None,
|
|
964
1006
|
axis_value: Union[float, np.ndarray] = None,
|
|
@@ -1022,6 +1064,9 @@ class DataToExportTimedSaver(DataToExportEnlargeableSaver):
|
|
|
1022
1064
|
def add_data(self, where: Union[Node, str], data: DataToExport, settings_as_xml='', **kwargs):
|
|
1023
1065
|
super().add_data(where, data, axis_values=[data.timestamp], settings_as_xml=settings_as_xml, **kwargs)
|
|
1024
1066
|
|
|
1067
|
+
def add_data_bundle(self, data: DataBundle):
|
|
1068
|
+
self.add_data(data.node, data.dte, settings_as_xml=data.settings)
|
|
1069
|
+
|
|
1025
1070
|
|
|
1026
1071
|
class DataToExportExtendedSaver(DataToExportSaver):
|
|
1027
1072
|
"""Object to save DataToExport at given indexes within arrays including extended shape
|
|
@@ -1035,7 +1080,10 @@ class DataToExportExtendedSaver(DataToExportSaver):
|
|
|
1035
1080
|
the extra shape compared to the data the h5array will have
|
|
1036
1081
|
"""
|
|
1037
1082
|
|
|
1038
|
-
def __init__(self,
|
|
1083
|
+
def __init__(self,
|
|
1084
|
+
h5saver: H5SaverLowLevel,
|
|
1085
|
+
extended_shape: Iterable[int],
|
|
1086
|
+
fill_value=None):
|
|
1039
1087
|
super().__init__(h5saver)
|
|
1040
1088
|
self._data_saver = DataExtendedSaver(h5saver, extended_shape, fill_value=fill_value)
|
|
1041
1089
|
self._nav_axis_saver = AxisSaverLoader(h5saver)
|
|
@@ -1054,7 +1102,12 @@ class DataToExportExtendedSaver(DataToExportSaver):
|
|
|
1054
1102
|
for axis in axes:
|
|
1055
1103
|
self._nav_axis_saver.add_axis(nav_group, axis)
|
|
1056
1104
|
|
|
1057
|
-
def
|
|
1105
|
+
def add_data_bundle(self, data: DataBundle):
|
|
1106
|
+
self.add_data(data.node, data.dte, data.indexes, data.distribution, data.settings)
|
|
1107
|
+
|
|
1108
|
+
def add_data(self, where: Union[Node, str],
|
|
1109
|
+
data: DataToExport,
|
|
1110
|
+
indexes: Iterable[int],
|
|
1058
1111
|
distribution=DataDistribution.uniform,
|
|
1059
1112
|
settings_as_xml='', **kwargs):
|
|
1060
1113
|
|
|
@@ -1095,7 +1148,7 @@ class DataToExportExtendedSaver(DataToExportSaver):
|
|
|
1095
1148
|
|
|
1096
1149
|
|
|
1097
1150
|
class DataLoader:
|
|
1098
|
-
"""Specialized Object to load DataWithAxes
|
|
1151
|
+
"""Specialized Object to load DataWithAxes / DataToExport objects from a h5file
|
|
1099
1152
|
|
|
1100
1153
|
On the contrary to DataSaverLoader, does include navigation axes stored elsewhere in the h5file
|
|
1101
1154
|
(for instance if saved from the DAQ_Scan)
|
|
@@ -1105,12 +1158,15 @@ class DataLoader:
|
|
|
1105
1158
|
h5saver: H5SaverLowLevel or Path
|
|
1106
1159
|
"""
|
|
1107
1160
|
|
|
1108
|
-
def __init__(self, h5saver: Union[H5SaverLowLevel, Path]
|
|
1161
|
+
def __init__(self, h5saver: Union[H5SaverLowLevel, Path],
|
|
1162
|
+
swmr_mode = False):
|
|
1109
1163
|
self._axis_loader: AxisSaverLoader = None
|
|
1110
1164
|
self._data_loader: DataSaverLoader = None
|
|
1111
1165
|
|
|
1112
1166
|
if isinstance(h5saver, (Path, str)):
|
|
1113
|
-
h5saver = H5SaverLowLevel.from_file(h5saver
|
|
1167
|
+
h5saver = H5SaverLowLevel.from_file(h5saver,
|
|
1168
|
+
swmr_mode=swmr_mode,
|
|
1169
|
+
mode='r')
|
|
1114
1170
|
|
|
1115
1171
|
self.h5saver = h5saver
|
|
1116
1172
|
|
|
@@ -1129,8 +1185,8 @@ class DataLoader:
|
|
|
1129
1185
|
|
|
1130
1186
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
1131
1187
|
self.close_file()
|
|
1132
|
-
@property
|
|
1133
1188
|
|
|
1189
|
+
@property
|
|
1134
1190
|
def raw_group(self) -> Node:
|
|
1135
1191
|
""" Get the base RawGroup where raw data should be saved
|
|
1136
1192
|
|
|
@@ -1141,9 +1197,11 @@ class DataLoader:
|
|
|
1141
1197
|
def close_file(self):
|
|
1142
1198
|
self._h5saver.close_file()
|
|
1143
1199
|
|
|
1144
|
-
def walk_nodes(self, where: Union[str, Node] = '/'
|
|
1200
|
+
def walk_nodes(self, where: Union[str, Node] = '/', depth: int = None,
|
|
1201
|
+
only_groups=False) -> Iterable[Node]:
|
|
1145
1202
|
"""Return a Node generator iterating over the h5file content"""
|
|
1146
|
-
return self.h5saver.walk_nodes(where
|
|
1203
|
+
return self.h5saver.walk_nodes(where, depth=depth,
|
|
1204
|
+
only_groups=only_groups)
|
|
1147
1205
|
|
|
1148
1206
|
def get_node(self, where: Union[Node, str], name: str = None) -> Node:
|
|
1149
1207
|
""" Convenience method to get node"""
|
|
@@ -1159,8 +1217,8 @@ class DataLoader:
|
|
|
1159
1217
|
|
|
1160
1218
|
Returns
|
|
1161
1219
|
-------
|
|
1162
|
-
GROUP
|
|
1163
|
-
|
|
1220
|
+
GROUP
|
|
1221
|
+
the group named ``SPECIAL_GROUP_NAMES['nav_axes']`` holding all NavAxis for those data
|
|
1164
1222
|
|
|
1165
1223
|
See Also
|
|
1166
1224
|
--------
|
|
@@ -1198,17 +1256,25 @@ class DataLoader:
|
|
|
1198
1256
|
if nav_group is not None:
|
|
1199
1257
|
nav_axes = self._axis_loader.get_axes(nav_group)
|
|
1200
1258
|
axes = data.axes[:]
|
|
1259
|
+
# in swmr mode, if at the time where the axis is read, a writer added stuff
|
|
1260
|
+
# then the axis and the data may not be consistent. Below is an attempt to correct this
|
|
1261
|
+
for ind_nav, nav_axe in enumerate(nav_axes):
|
|
1262
|
+
if nav_axe.size > data.shape[data.nav_indexes[nav_axe.index]]:
|
|
1263
|
+
nav_axe.data = nav_axe.get_data()[:data.shape[data.nav_indexes[nav_axe.index]]]
|
|
1201
1264
|
axes.extend(nav_axes)
|
|
1202
1265
|
data.axes = axes
|
|
1203
1266
|
data.get_dim_from_data_axes()
|
|
1204
1267
|
data.create_missing_axes()
|
|
1205
1268
|
return data
|
|
1206
1269
|
|
|
1207
|
-
def load_all(self, where: GROUP, data: DataToExport = None, with_bkg=False) -> DataToExport:
|
|
1270
|
+
def load_all(self, where: GROUP | str, data: DataToExport = None, with_bkg=False) -> DataToExport:
|
|
1208
1271
|
if data is None:
|
|
1209
1272
|
data = DataToExport('Loaded data')
|
|
1210
1273
|
where = self._h5saver.get_node(where)
|
|
1211
|
-
|
|
1274
|
+
try:
|
|
1275
|
+
children_dict = where.children()
|
|
1276
|
+
except AttributeError:
|
|
1277
|
+
children_dict = {where.name: where}
|
|
1212
1278
|
data_list = []
|
|
1213
1279
|
for child in children_dict:
|
|
1214
1280
|
if isinstance(children_dict[child], GROUP):
|
|
@@ -1263,4 +1329,6 @@ class DataLoader:
|
|
|
1263
1329
|
# if not returned so far raise a NameError
|
|
1264
1330
|
raise NameError(f'No dwa matching this name: {name} and '
|
|
1265
1331
|
f'origin: {origin} hanging from '
|
|
1266
|
-
f'{where}')
|
|
1332
|
+
f'{where}')
|
|
1333
|
+
|
|
1334
|
+
|
|
@@ -92,11 +92,14 @@ class H5SaverLowLevel(H5Backend):
|
|
|
92
92
|
|
|
93
93
|
@classmethod
|
|
94
94
|
def from_file(cls, path: Union[Path, str], save_type: SaveType = 'scan',
|
|
95
|
-
new_file: bool = False, metadata: dict = None
|
|
95
|
+
new_file: bool = False, metadata: dict = None,
|
|
96
|
+
swmr_mode: bool = False,
|
|
97
|
+
mode='a') -> 'H5SaverLowLevel':
|
|
96
98
|
"""Create and initialise an H5SaverLowLevel from a file path.
|
|
97
99
|
|
|
98
100
|
Convenience factory that combines the constructor and :meth:`init_file`
|
|
99
101
|
call into a single expression.
|
|
102
|
+
|
|
100
103
|
Parameters
|
|
101
104
|
----------
|
|
102
105
|
path: Path or str
|
|
@@ -108,6 +111,11 @@ class H5SaverLowLevel(H5Backend):
|
|
|
108
111
|
opened for appending.
|
|
109
112
|
metadata: dict or None
|
|
110
113
|
Extra attributes written to the raw-data group on creation.
|
|
114
|
+
swmr_mode: bool
|
|
115
|
+
If True, prepare the file for SWMR (h5py backend only)
|
|
116
|
+
mode: str
|
|
117
|
+
valid if new_file is False. Then could be 'r' for readonly or 'a' to append data
|
|
118
|
+
|
|
111
119
|
|
|
112
120
|
Returns
|
|
113
121
|
-------
|
|
@@ -115,7 +123,11 @@ class H5SaverLowLevel(H5Backend):
|
|
|
115
123
|
A fully initialised instance ready for reading/writing.
|
|
116
124
|
"""
|
|
117
125
|
h5saver = cls(save_type)
|
|
118
|
-
h5saver.init_file(file_name=Path(path),
|
|
126
|
+
h5saver.init_file(file_name=Path(path),
|
|
127
|
+
new_file=new_file,
|
|
128
|
+
metadata=metadata,
|
|
129
|
+
swmr_mode=swmr_mode,
|
|
130
|
+
mode=mode)
|
|
119
131
|
return h5saver
|
|
120
132
|
|
|
121
133
|
def set_swmr_flush_interval(self, interval: int):
|
|
@@ -149,8 +161,11 @@ class H5SaverLowLevel(H5Backend):
|
|
|
149
161
|
def h5_file(self):
|
|
150
162
|
return self._h5file
|
|
151
163
|
|
|
152
|
-
def init_file(self, file_name: Path, raw_group_name='RawData',
|
|
153
|
-
|
|
164
|
+
def init_file(self, file_name: Path, raw_group_name='RawData',
|
|
165
|
+
new_file=False,
|
|
166
|
+
metadata: dict = None,
|
|
167
|
+
swmr_mode: bool = False,
|
|
168
|
+
mode: str = 'a'):
|
|
154
169
|
"""Initializes a new h5 file,
|
|
155
170
|
|
|
156
171
|
Should have an extension with h5 in it.
|
|
@@ -167,6 +182,8 @@ class H5SaverLowLevel(H5Backend):
|
|
|
167
182
|
A dictionary to be saved as attributes
|
|
168
183
|
swmr_mode: bool
|
|
169
184
|
If True, prepare the file for SWMR (h5py backend only)
|
|
185
|
+
mode: str
|
|
186
|
+
valid if new_file is False. Then could be 'r' for readonly or 'a' to append data
|
|
170
187
|
|
|
171
188
|
Returns
|
|
172
189
|
-------
|
|
@@ -200,7 +217,7 @@ class H5SaverLowLevel(H5Backend):
|
|
|
200
217
|
return
|
|
201
218
|
|
|
202
219
|
self.close_file()
|
|
203
|
-
self.open_file(self.h5_file_path.joinpath(self.h5_file_name), 'w' if new_file else
|
|
220
|
+
self.open_file(self.h5_file_path.joinpath(self.h5_file_name), 'w' if new_file else mode,
|
|
204
221
|
title='PyMoDAQ file', swmr_mode=swmr_mode)
|
|
205
222
|
|
|
206
223
|
self._raw_group = self.get_set_group(self.root(), raw_group_name, title='Data from PyMoDAQ modules')
|
|
@@ -267,6 +284,7 @@ class H5SaverLowLevel(H5Backend):
|
|
|
267
284
|
title: str = '', metadata=dict([])):
|
|
268
285
|
|
|
269
286
|
"""save data arrays on the hdf5 file together with metadata
|
|
287
|
+
|
|
270
288
|
Parameters
|
|
271
289
|
----------
|
|
272
290
|
where: GROUP
|
|
@@ -278,7 +296,7 @@ class H5SaverLowLevel(H5Backend):
|
|
|
278
296
|
data_shape: Iterable
|
|
279
297
|
the shape of the array to save, mandatory if array_to_save is None
|
|
280
298
|
data_dimension: DataDim
|
|
281
|
-
|
|
299
|
+
The data's dimension
|
|
282
300
|
scan_shape: Iterable
|
|
283
301
|
the shape of the scan dimensions
|
|
284
302
|
title: str
|
|
@@ -294,12 +312,13 @@ class H5SaverLowLevel(H5Backend):
|
|
|
294
312
|
if False, data are saved as a CARRAY, otherwise as a EARRAY (for ragged data, see add_string_array)
|
|
295
313
|
metadata: dict
|
|
296
314
|
dictionnary whose keys will be saved as the array attributes
|
|
297
|
-
add_scan_dim:
|
|
298
|
-
|
|
315
|
+
add_scan_dim: bool
|
|
316
|
+
if True, the scan axes dimension (scan_shape iterable) is prepended to the array shape on the hdf5
|
|
317
|
+
In that case, the array is usually initialized as zero and further populated
|
|
299
318
|
|
|
300
319
|
Returns
|
|
301
320
|
-------
|
|
302
|
-
|
|
321
|
+
CARRAY or EARRAY
|
|
303
322
|
|
|
304
323
|
See Also
|
|
305
324
|
--------
|
|
@@ -20,7 +20,10 @@ logger = set_logger(get_module_name(__file__))
|
|
|
20
20
|
config = configmod.GlobalConfig()
|
|
21
21
|
|
|
22
22
|
PLOT_COLORS = PlotColors()
|
|
23
|
-
|
|
23
|
+
try:
|
|
24
|
+
PLOT_COLORS.remove((255, 255, 255))# remove white color as plotted on white background
|
|
25
|
+
except ValueError:
|
|
26
|
+
pass # this color is not in PLOT_COLORS (maybe because light style is used)
|
|
24
27
|
PLOT_COLORS = PlotColors([(np.array(color) / 255).tolist() for color in PLOT_COLORS]) # translation to matplotlib
|
|
25
28
|
|
|
26
29
|
|
|
@@ -38,18 +41,26 @@ class Plotter(PlotterBase):
|
|
|
38
41
|
def plot(self, data: Union[DataWithAxes, DataToExport], *args, viewer=None,
|
|
39
42
|
**kwargs) -> plt.Figure:
|
|
40
43
|
fig = plt.figure()
|
|
44
|
+
if 'linewidth' in kwargs:
|
|
45
|
+
linewidth = kwargs['linewidth']
|
|
46
|
+
else:
|
|
47
|
+
linewidth = self.linewidth
|
|
41
48
|
|
|
42
49
|
if isinstance(data, DataWithAxes):
|
|
43
50
|
self.n_columns = len(data) if data.dim.name == 'Data2D' else 1
|
|
44
|
-
self.plot_dwa(data, *args, **kwargs)
|
|
51
|
+
self.plot_dwa(data, *args, linewidth=linewidth, **kwargs)
|
|
45
52
|
elif isinstance(data, DataToExport):
|
|
46
53
|
self.n_columns = max([len(dwa) if dwa.dim.name == 'Data2D' else 1 for dwa in data])
|
|
47
54
|
self.n_lines = len(data)
|
|
48
|
-
self.plot_dte(data, *args, **kwargs)
|
|
55
|
+
self.plot_dte(data, *args, linewidth=linewidth, **kwargs)
|
|
49
56
|
plt.tight_layout()
|
|
50
57
|
fig.suptitle(f'{data.name} taken the {datetime.datetime.fromtimestamp(data.timestamp)}')
|
|
51
58
|
return fig
|
|
52
59
|
|
|
60
|
+
@property
|
|
61
|
+
def linewidth(self) -> int:
|
|
62
|
+
return config('data', 'plotting', 'linewidth')
|
|
63
|
+
|
|
53
64
|
def plot_dwa(self, dwa: DataWithAxes, *args, **kwargs):
|
|
54
65
|
if dwa.dim.name == 'Data1D':
|
|
55
66
|
if len(dwa.axes) == 0:
|
|
@@ -26,3 +26,4 @@ backend = ['h5py', 'tables', 'h5pyd'] # first element is the default, mostly te
|
|
|
26
26
|
backend = ['matplotlib'] # either 'matplotlib' or 'qt' or any other custom backend
|
|
27
27
|
plot_colors = [[255, 255, 255], [255, 0, 0], [0, 255, 0], [0, 0, 255], [14, 207, 189], [207, 14, 166], [207, 204, 14]]
|
|
28
28
|
process_roi = true # if True subdata is extracted from the ROI in the data viewers
|
|
29
|
+
linewidth = 1 # default linewidth of the plots
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/plotting/plotter/plotters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.2.12 → pymodaq_data-5.3.0}/src/pymodaq_data/post_treatment/process_to_scalar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|