pymodaq 4.4.7__py3-none-any.whl → 4.4.11__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.

@@ -616,10 +616,12 @@ class DAQ_Move(ParameterControlModule):
616
616
  if ('°' in unit or 'degree' in unit) and not '°C' in unit:
617
617
  # special cas as pint base unit for angles are radians
618
618
  return '°'
619
- elif 'W' in unit or 'watt' in unit:
619
+ elif 'W' in unit or 'watt' in unit.lower():
620
620
  return 'W'
621
621
  elif '°C' in unit or 'Celsius' in unit:
622
622
  return '°C'
623
+ elif 'V' in unit or 'volt' in unit.lower():
624
+ return 'V'
623
625
  else:
624
626
  return str(Q_(1, unit).to_base_units().units)
625
627
 
@@ -711,7 +713,8 @@ class DAQ_Move_Hardware(QObject):
711
713
  Uninitialize the stage closing the hardware.
712
714
 
713
715
  """
714
- self.hardware.close()
716
+ if self.hardware is not None:
717
+ self.hardware.close()
715
718
 
716
719
  return "Stage uninitialized"
717
720
 
@@ -786,7 +789,7 @@ class DAQ_Move_Hardware(QObject):
786
789
  self.hardware.move_is_done = False
787
790
  self.hardware.ispolling = polling
788
791
  if self.hardware.data_actuator_type.name == 'float':
789
- self.hardware.move_abs(position.value())
792
+ self.hardware.move_abs(position.units_as(self.hardware.axis_unit).value()) # convert to plugin controller current axis units
790
793
  else:
791
794
  position.units = self.hardware.axis_unit # convert to plugin controller current axis units
792
795
  self.hardware.move_abs(position)
@@ -15,7 +15,7 @@ from qtpy.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout, QWidget, QTool
15
15
 
16
16
  from pymodaq.utils.daq_utils import ThreadCommand
17
17
  from pymodaq.utils.gui_utils.custom_app import CustomApp
18
- from pymodaq.utils.gui_utils.widgets import PushButtonIcon, LabelWithFont, SpinBox, QSpinBox_ro, QLED
18
+ from pymodaq.utils.gui_utils.widgets import PushButtonIcon, LabelWithFont, SpinBox, QSpinBox_ro, QLED, QSpinBoxWithShortcut
19
19
  from pymodaq.control_modules.utils import ControlModuleUI
20
20
  from pymodaq.utils.gui_utils import DockArea
21
21
  from pymodaq.utils.plotting.data_viewers.viewer import ViewerDispatcher
@@ -199,9 +199,9 @@ class DAQ_Move_UI(ControlModuleUI):
199
199
  self.main_ui.layout().addWidget(self.toolbar, 0, 0, 1, 2)
200
200
  self.main_ui.layout().addWidget(self.move_toolbar, 1, 0, 1, 2)
201
201
 
202
- self.abs_value_sb = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
202
+ self.abs_value_sb = QSpinBoxWithShortcut(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
203
203
  self.abs_value_sb.setStyleSheet("background-color : lightgreen; color: black")
204
- self.abs_value_sb_2 = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
204
+ self.abs_value_sb_2 = QSpinBoxWithShortcut(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
205
205
  self.abs_value_sb_2.setStyleSheet("background-color : lightcoral; color: black")
206
206
  self.move_toolbar.addWidget(self.abs_value_sb)
207
207
  self.move_toolbar.addWidget(self.abs_value_sb_2)
@@ -227,7 +227,7 @@ class DAQ_Move_UI(ControlModuleUI):
227
227
  self.control_ui.layout().addWidget(LabelWithFont('Abs. Value'), 0, 0)
228
228
  self.find_home_pb = PushButtonIcon('home2', 'Find Home')
229
229
  self.control_ui.layout().addWidget(self.find_home_pb, 0, 1)
230
- self.abs_value_sb_bis = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
230
+ self.abs_value_sb_bis = QSpinBoxWithShortcut(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
231
231
  self.control_ui.layout().addWidget(self.abs_value_sb_bis, 1, 0)
232
232
  self.move_abs_pb = PushButtonIcon('Move', 'Set Abs.',
233
233
  tip='Set the value of the actuator to the set absolute value')
@@ -236,7 +236,7 @@ class DAQ_Move_UI(ControlModuleUI):
236
236
  self.move_rel_plus_pb = PushButtonIcon('MoveUp', 'Set Rel. (+)')
237
237
  self.control_ui.layout().addWidget(self.move_rel_plus_pb, 2, 1)
238
238
 
239
- self.rel_value_sb = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
239
+ self.rel_value_sb = QSpinBoxWithShortcut(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'), key_sequences=("Ctrl+Enter","Ctrl+Shift+Enter"),)
240
240
  self.control_ui.layout().addWidget(self.rel_value_sb, 3, 0)
241
241
  self.move_rel_minus_pb = PushButtonIcon('MoveDown', 'Set Rel. (-)')
242
242
  self.control_ui.layout().addWidget(self.move_rel_minus_pb, 3, 1)
@@ -302,9 +302,16 @@ class DAQ_Move_UI(ControlModuleUI):
302
302
  self.connect_action('show_config', lambda: self.command_sig.emit(ThreadCommand('show_config', )))
303
303
 
304
304
  self.move_abs_pb.clicked.connect(lambda: self.emit_move_abs(self.abs_value_sb_bis))
305
+ self.abs_value_sb.shortcut["Ctrl+Enter"].activated.connect(lambda: self.emit_move_abs(self.abs_value_sb))
306
+ self.abs_value_sb_2.shortcut["Ctrl+Enter"].activated.connect(lambda: self.emit_move_abs(self.abs_value_sb_2))
307
+ self.abs_value_sb_bis.shortcut["Ctrl+Enter"].activated.connect(lambda: self.emit_move_abs(self.abs_value_sb_bis))
305
308
 
306
309
  self.rel_value_sb.valueChanged.connect(lambda: self.command_sig.emit(
307
310
  ThreadCommand('rel_value', self.rel_value_sb.value())))
311
+
312
+ self.rel_value_sb.shortcut["Ctrl+Enter"].activated.connect(lambda: self.emit_move_rel('+'))
313
+ self.rel_value_sb.shortcut["Ctrl+Shift+Enter"].activated.connect(lambda: self.emit_move_rel('-'))
314
+
308
315
  self.move_rel_plus_pb.clicked.connect(lambda: self.emit_move_rel('+'))
309
316
  self.move_rel_minus_pb.clicked.connect(lambda: self.emit_move_rel('-'))
310
317
 
@@ -67,11 +67,11 @@ class DataActuatorType(BaseEnum):
67
67
  def comon_parameters(epsilon=config('actuator', 'epsilon_default'),
68
68
  epsilons=None):
69
69
  if epsilons is not None:
70
- epsilon=epsilons
70
+ epsilon = epsilons
71
71
  if isinstance(epsilon, list):
72
- epsilon=epsilon[0]
72
+ epsilon = epsilon[0]
73
73
  elif isinstance(epsilon, dict):
74
- epsilon=epsilon[list[epsilon.keys()][0]]
74
+ epsilon = epsilon[list[epsilon.keys()][0]]
75
75
 
76
76
  return [{'title': 'Units:', 'name': 'units', 'type': 'str', 'value': '', 'readonly': True},
77
77
  {'title': 'Epsilon:', 'name': 'epsilon', 'type': 'float',
@@ -124,7 +124,7 @@ def comon_parameters_fun(is_multiaxes=False, axes_names=None,
124
124
 
125
125
  Parameters
126
126
  ----------
127
- is_multiaxes: bool # deprecated not need anymore
127
+ is_multiaxes: bool
128
128
  If True, display the particular settings to define which axis the controller is driving
129
129
  axes_names: deprecated, use axis_names
130
130
  axis_names: list of str or dictionnary of string as key and integer as value
@@ -140,7 +140,7 @@ def comon_parameters_fun(is_multiaxes=False, axes_names=None,
140
140
  axes_names = ['']
141
141
  axis_names = axes_names
142
142
 
143
- is_multiaxes = len(axis_names) > 1
143
+ is_multiaxes = len(axis_names) > 1 or is_multiaxes
144
144
  if isinstance(axis_names, list):
145
145
  if len(axis_names) > 0:
146
146
  axis_name = axis_names[0]
@@ -617,6 +617,10 @@ class DAQ_Move_base(QObject):
617
617
  else:
618
618
  raise NotImplementedError
619
619
 
620
+
621
+ def close(self):
622
+ raise NotImplementedError
623
+
620
624
  def move_abs(self, value: Union[float, DataActuator]):
621
625
  if hasattr(self, 'move_Abs'):
622
626
  deprecation_msg('move_Abs method in plugins is deprecated, use move_abs', 3)
@@ -358,7 +358,6 @@ class ParameterControlModule(ParameterManager, ControlModule):
358
358
  listener_class: Type[ActorListener] = ActorListener
359
359
 
360
360
  def __init__(self, **kwargs):
361
- QObject.__init__(self)
362
361
  ParameterManager.__init__(self, action_list=('save', 'update'))
363
362
  ControlModule.__init__(self)
364
363
 
@@ -82,7 +82,12 @@ class DAQScan(QObject, ParameterManager):
82
82
  {'title': 'Timeout (ms)', 'name': 'timeout', 'type': 'int', 'value': 10000},
83
83
  ]},
84
84
  {'title': 'Scan options', 'name': 'scan_options', 'type': 'group', 'children': [
85
- {'title': 'Naverage:', 'name': 'scan_average', 'type': 'int', 'value': 1, 'min': 1},
85
+ {'title': 'Naverage:', 'name': 'scan_average', 'type': 'int',
86
+ 'value': config('scan', 'Naverage'), 'min': 1},
87
+ {'title': 'Plot on top:', 'name': 'average_on_top', 'type': 'bool',
88
+ 'value': config('scan', 'average_on_top'),
89
+ 'tip': 'At the second iteration will plot the averaged scan on top (True) of the current one'
90
+ 'or in a second panel (False)'},
86
91
  ]},
87
92
 
88
93
  {'title': 'Plotting options', 'name': 'plot_options', 'type': 'group', 'children': [
@@ -629,6 +634,10 @@ class DAQScan(QObject, ParameterManager):
629
634
  viewers_enum.extend([ViewersEnum('Data1D').increase_dim(self.scanner.n_axes)
630
635
  for _ in range(len(self.settings['plot_options', 'plot_1d']['selected']))])
631
636
  data_names.extend(self.settings['plot_options', 'plot_1d']['selected'][:])
637
+ if not self.settings['scan_options', 'average_on_top']:
638
+
639
+ viewers_enum = viewers_enum + viewers_enum
640
+ data_names = data_names + [f'{data_name}_averaged' for data_name in data_names]
632
641
  self.live_plotter.prepare_viewers(viewers_enum, viewers_name=data_names)
633
642
 
634
643
  def update_status(self, txt: str, wait_time=0):
@@ -668,6 +677,7 @@ class DAQScan(QObject, ParameterManager):
668
677
  self.ui.set_scan_step_average(status.attribute[1] + 1)
669
678
 
670
679
  elif status.command == "Scan_done":
680
+
671
681
  self.modules_manager.reset_signals()
672
682
  self.live_timer.stop()
673
683
  self.ui.set_scan_done()
@@ -732,6 +742,7 @@ class DAQScan(QObject, ParameterManager):
732
742
  self.live_plotter.load_plot_data(group_0D=self.settings['plot_options', 'group0D'],
733
743
  average_axis=average_axis,
734
744
  average_index=self.ind_average,
745
+ separate_average= not self.settings['scan_options', 'average_on_top'],
735
746
  target_at=self.scanner.positions[self.ind_scan],
736
747
  last_step=(self.ind_scan ==
737
748
  self.scanner.positions.size - 1 and
@@ -765,6 +776,8 @@ class DAQScan(QObject, ParameterManager):
765
776
  messagebox(text="There are not enough or too much selected move modules for this scan")
766
777
  return False
767
778
 
779
+ ## TODO the stuff about adaptive scans have to be moved into a dedicated extension. M
780
+ ## Most similat to the Bayesian one!
768
781
  if self.scanner.scan_sub_type == 'Adaptive':
769
782
  #todo include this in scanners objects for the adaptive scanners
770
783
  if len(self.modules_manager.get_selected_probed_data('0D')) == 0:
@@ -852,9 +865,15 @@ class DAQScan(QObject, ParameterManager):
852
865
  self.save_metadata(scan_node, 'scan_info')
853
866
 
854
867
  self._init_live()
868
+ Naverage = self.settings['scan_options', 'scan_average']
869
+ if Naverage > 1:
870
+ scan_shape = [Naverage]
871
+ scan_shape.extend(self.scanner.get_scan_shape())
872
+ else:
873
+ scan_shape = self.scanner.get_scan_shape()
855
874
  for det in self.modules_manager.detectors:
856
875
  det.module_and_data_saver = (
857
- module_saving.DetectorExtendedSaver(det, self.scanner.get_scan_shape()))
876
+ module_saving.DetectorExtendedSaver(det, scan_shape))
858
877
  self.module_and_data_saver.h5saver = self.h5saver # force the update as the h5saver ill also be set on each detectors
859
878
 
860
879
  # mandatory to deal with multithreads
@@ -1034,7 +1053,7 @@ class DAQScanAcquisition(QObject):
1034
1053
 
1035
1054
  def start_acquisition(self):
1036
1055
  try:
1037
- #todo hoaw to apply newlayout to adaptive mode?
1056
+ #todo hoaw to apply newlayout to adaptive mode? => cannot has to be a new extension
1038
1057
 
1039
1058
  self.modules_manager.connect_actuators()
1040
1059
  self.modules_manager.connect_detectors()
@@ -1106,6 +1125,7 @@ class DAQScanAcquisition(QObject):
1106
1125
  # daq_scan wait time
1107
1126
  QThread.msleep(self.scan_settings.child('time_flow', 'wait_time').value())
1108
1127
 
1128
+ self.modules_manager.timeout_signal.disconnect()
1109
1129
  self.modules_manager.connect_actuators(False)
1110
1130
  self.modules_manager.connect_detectors(False)
1111
1131
 
@@ -1113,6 +1133,7 @@ class DAQScanAcquisition(QObject):
1113
1133
  attribute="Acquisition has finished"))
1114
1134
  self.status_sig.emit(utils.ThreadCommand("Scan_done"))
1115
1135
 
1136
+
1116
1137
  except Exception as e:
1117
1138
  logger.exception(str(e))
1118
1139
 
@@ -1148,7 +1169,8 @@ class DAQScanAcquisition(QObject):
1148
1169
  full_names: list = self.scan_settings['plot_options', 'plot_0d']['selected'][:]
1149
1170
  full_names.extend(self.scan_settings['plot_options', 'plot_1d']['selected'][:])
1150
1171
  data_temp = det_done_datas.get_data_from_full_names(full_names, deepcopy=False)
1151
- data_temp = data_temp.get_data_with_naxes_lower_than(2-len(indexes)) # maximum Data2D included nav indexes
1172
+ n_nav_axis_selection = 2-len(indexes) + 1 if self.Naverage > 1 else 2-len(indexes)
1173
+ data_temp = data_temp.get_data_with_naxes_lower_than(n_nav_axis_selection) # maximum Data2D included nav indexes
1152
1174
 
1153
1175
  self.scan_data_tmp.emit(ScanDataTemp(self.ind_scan, indexes, data_temp))
1154
1176
 
@@ -114,7 +114,7 @@ class DAQScanUI(CustomApp, ViewerDispatcher):
114
114
 
115
115
  settings_widget = QtWidgets.QWidget()
116
116
  settings_widget.setLayout(QtWidgets.QVBoxLayout())
117
- settings_widget.setMinimumWidth(220)
117
+ #settings_widget.setMinimumWidth(220)
118
118
 
119
119
  splitter_v_widget.addWidget(self.module_widget)
120
120
  splitter_v_widget.addWidget(self.plotting_widget)
@@ -1,4 +1,4 @@
1
- from pymodaq_utils.utils import ThreadCommand
1
+ from pymodaq.utils.daq_utils import ThreadCommand
2
2
 
3
3
  from pymodaq.control_modules.move_utility_classes import (DAQ_Move_base, comon_parameters_fun,
4
4
  DataActuatorType, DataActuator)
@@ -67,7 +67,7 @@ class LoaderPlotter:
67
67
  def load_data(self, filter_dims: List[Union[DataDim, str]] = None,
68
68
  filter_full_names: List[str] = None, remove_navigation: bool = True,
69
69
  group_0D=False, average_axis: int=None, average_index: int = 0,
70
- last_step=False):
70
+ last_step=False, separate_average=False):
71
71
  """Load Data from the h5 node of the dataloader and apply some filtering/manipulation before
72
72
  plotting
73
73
 
@@ -89,6 +89,8 @@ class LoaderPlotter:
89
89
  which step in the averaging process are we in.
90
90
  last_step: bool
91
91
  tells if this is the very last step of the (averaged) scan
92
+ separate_average: bool
93
+ Tells if the averaged data are to be plotted on the same data viewer panel or another one
92
94
 
93
95
  Returns
94
96
  -------
@@ -99,7 +101,8 @@ class LoaderPlotter:
99
101
  self.dataloader.load_all('/', self._data)
100
102
 
101
103
  if average_axis is not None:
102
- self.average_axis(average_axis, average_index, last_step=last_step)
104
+ self.average_axis(average_axis, average_index, last_step=last_step,
105
+ separate_average=separate_average)
103
106
 
104
107
  if filter_dims is not None:
105
108
  filter_dims[:] = [enum_checker(DataDim, dim) for dim in filter_dims]
@@ -110,14 +113,15 @@ class LoaderPlotter:
110
113
  filter_full_names]
111
114
 
112
115
  if group_0D: # 0D initial data
113
- self.group_0D_data()
116
+ self.group_0D_data(separate_average=separate_average)
114
117
 
115
118
  if remove_navigation:
116
119
  self.remove_navigation_axes()
117
120
 
118
121
  return self._data
119
122
 
120
- def average_axis(self, average_axis, average_index, last_step=False) -> None:
123
+ def average_axis(self, average_axis, average_index, last_step=False,
124
+ separate_average=False) -> None:
121
125
  """ Average the data along their average axis
122
126
 
123
127
  Parameters
@@ -129,7 +133,12 @@ class LoaderPlotter:
129
133
  which step in the averaging process are we in.
130
134
  last_step: bool
131
135
  tells if this is the very last step of the (averaged) scan
136
+ separate_average: bool
137
+ Tells if the averaged data are to be plotted on the same data viewer panel or another one
138
+
132
139
  """
140
+ if separate_average and average_index > 0:
141
+ averaged_data = DataToExport('Averaged')
133
142
  for ind, data in enumerate(self._data):
134
143
  current_data = data.inav[average_index, ...]
135
144
  if average_index > 0:
@@ -140,10 +149,16 @@ class LoaderPlotter:
140
149
  data_to_append = data.inav[0, ...]
141
150
  else:
142
151
  data_to_append = data.inav[0:average_index, ...].mean(axis=average_axis)
143
-
152
+ data_to_append.name = f'{data_to_append.name}_averaged'
144
153
  data_to_append.labels = [f'{label}_averaged' for label in data_to_append.labels]
145
- current_data.append(data_to_append)
154
+ if not (separate_average and average_index > 0):
155
+ current_data.append(data_to_append)
156
+ else:
157
+ averaged_data.append(data_to_append)
146
158
  self._data[ind] = current_data
159
+ if separate_average and average_index > 0:
160
+ self._data.append(averaged_data.data)
161
+
147
162
 
148
163
  def remove_navigation_axes(self):
149
164
  """Make the navigation axes as signal axes
@@ -156,18 +171,27 @@ class LoaderPlotter:
156
171
  data.transpose() # because usual ND data should be plotted here as 2D with the nav axes as the minor
157
172
  # (horizontal)
158
173
 
159
- def group_0D_data(self):
174
+ def group_0D_data(self, separate_average=False):
160
175
  """Group in a single DataFromPlugins all data that are initialy Data0D
161
176
 
162
177
  """
163
178
  data = self._data.get_data_from_sig_axes(0)
164
179
  if len(data) > 0:
165
180
  data0D_arrays = []
181
+ data0D_arrays_averaged = []
166
182
  labels = []
183
+ labels_averaged = []
167
184
  for dwa in data:
168
- data0D_arrays.extend(dwa.data)
169
- labels.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
170
- self._data.remove(dwa)
185
+ if 'averaged' in dwa.name and separate_average:
186
+ data0D_arrays_averaged.extend(dwa.data)
187
+ labels_averaged.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
188
+ self._data.remove(dwa)
189
+
190
+
191
+ else:
192
+ data0D_arrays.extend(dwa.data)
193
+ labels.extend([f'{dwa.get_full_name()}/{label}' for label in dwa.labels])
194
+ self._data.remove(dwa)
171
195
 
172
196
  data0D = DataFromPlugins(self.grouped_data0D_fullname.split('/')[1],
173
197
  data=data0D_arrays, labels=labels,
@@ -176,6 +200,15 @@ class LoaderPlotter:
176
200
  axes=dwa.axes, nav_indexes=dwa.nav_indexes,
177
201
  )
178
202
  self._data.append(data0D)
203
+ if 'averaged' in dwa.name and separate_average:
204
+ data0D_averaged = DataFromPlugins(
205
+ f"{self.grouped_data0D_fullname.split('/')[1]}_averaged",
206
+ data=data0D_arrays_averaged, labels=labels_averaged,
207
+ dim='DataND',
208
+ origin=self.grouped_data0D_fullname.split('/')[0],
209
+ axes=dwa.axes, nav_indexes=dwa.nav_indexes,
210
+ )
211
+ self._data.append(data0D_averaged)
179
212
 
180
213
  def load_plot_data(self, **kwargs):
181
214
  """Load and plot all data from the current H5Saver
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # WARNING! All changes made in this file will be lost!
8
8
 
9
- from PyQt5 import QtCore
9
+ from qtpy import QtCore
10
10
 
11
11
  qt_resource_data = b"\
12
12
  \x00\x00\x07\xbc\
pymodaq/resources/VERSION CHANGED
@@ -1,2 +1,2 @@
1
- version = '4.4.7'
1
+ version = '4.4.11'
2
2
 
@@ -86,6 +86,7 @@ scan_in_thread = true
86
86
  show_popups = true
87
87
  default = "Scan2D"
88
88
  Naverage = 1 # minimum is 1
89
+ average_on_top = true
89
90
  steps_limit = 1000 # the limit of the number of steps you can set in a given scan
90
91
  sort1D = true
91
92
 
pymodaq/utils/data.py CHANGED
@@ -20,7 +20,6 @@ from time import time
20
20
  import copy
21
21
  import pint
22
22
  from multipledispatch import dispatch
23
- import pymodaq
24
23
  from pymodaq.utils.enums import BaseEnum, enum_checker
25
24
  from pymodaq.utils.messenger import deprecation_msg
26
25
  from pymodaq.utils.daq_utils import find_objects_in_list_from_attr_name_val
@@ -157,6 +156,24 @@ class DataDistribution(BaseEnum):
157
156
  spread = 1
158
157
 
159
158
 
159
+ def _compute_slices_from_axis(axis: Axis, _slice, *ignored, is_index=True, **ignored_also):
160
+ if not is_index:
161
+ if isinstance(_slice, numbers.Number):
162
+ if not is_index:
163
+ _slice = axis.find_index(_slice)
164
+ elif _slice is Ellipsis:
165
+ return _slice
166
+ elif isinstance(_slice, slice):
167
+ if not (_slice.start is None and
168
+ _slice.stop is None and _slice.step is None):
169
+ start = axis.find_index(
170
+ _slice.start if _slice.start is not None else axis.get_data()[0])
171
+ stop = axis.find_index(
172
+ _slice.stop if _slice.stop is not None else axis.get_data()[-1])
173
+ _slice = slice(start, stop)
174
+ return _slice
175
+
176
+
160
177
  class Axis:
161
178
  """Object holding info and data about physical axis of some data
162
179
 
@@ -367,8 +384,9 @@ class Axis:
367
384
  def __len__(self):
368
385
  return self.size
369
386
 
370
- def _compute_slices(self, slices, *ignored, **ignored_also):
371
- return slices
387
+ def _compute_slices(self, _slice, *ignored, is_index=True, **ignored_also):
388
+ _slice = _compute_slices_from_axis(self, _slice, is_index=is_index)
389
+ return _slice, _slice
372
390
 
373
391
  def _slicer(self, _slice, *ignored, **ignored_also):
374
392
  ax: Axis = copy.deepcopy(self)
@@ -2188,11 +2206,22 @@ class DataWithAxes(DataBase):
2188
2206
  axes.append(ax)
2189
2207
  self.axes = axes
2190
2208
 
2191
- def _compute_slices(self, slices, is_navigation=True):
2209
+ def _compute_slices(self, slices, is_navigation=True, is_index=True):
2192
2210
  """Compute the total slice to apply to the data
2193
2211
 
2194
2212
  Filling in Ellipsis when no slicing should be done
2213
+ Parameters
2214
+ ----------
2215
+ slices: List of slice
2216
+ is_navigation: bool
2217
+ is_index: bool
2218
+ if False, the slice are on the values of the underlying axes
2219
+ Returns
2220
+ -------
2221
+ list(slice): the computed slices as index (eventually for all axes)
2222
+ list(slice): a version as index of the input argument
2195
2223
  """
2224
+ _slices_as_index = []
2196
2225
  if isinstance(slices, numbers.Number) or isinstance(slices, slice):
2197
2226
  slices = [slices]
2198
2227
  if is_navigation:
@@ -2203,13 +2232,29 @@ class DataWithAxes(DataBase):
2203
2232
  slices = list(slices)
2204
2233
  for ind in range(len(self.shape)):
2205
2234
  if ind in indexes:
2206
- total_slices.append(slices.pop(0))
2235
+ _slice = slices.pop(0)
2236
+ if not is_index:
2237
+ axis = self.get_axis_from_index(ind)[0]
2238
+ _slice = _compute_slices_from_axis(axis, _slice, is_index=is_index)
2239
+ _slices_as_index.append(_slice)
2240
+ total_slices.append(_slice)
2207
2241
  elif len(total_slices) == 0:
2208
2242
  total_slices.append(Ellipsis)
2209
2243
  elif not (Ellipsis in total_slices and total_slices[-1] is Ellipsis):
2210
2244
  total_slices.append(slice(None))
2245
+ if len(slices) == 0 and self.distribution == DataDistribution.uniform and is_navigation:
2246
+ if total_slices[-1] is Ellipsis:
2247
+ for ind in range(len(total_slices), len(indexes)):
2248
+ _slices_as_index.append(slice(None))
2249
+ else:
2250
+ for ind in range(len(total_slices), len(indexes)):
2251
+ _slices_as_index.insert(0, Ellipsis)
2252
+ for ind in range(len(indexes), len(self.shape)):
2253
+ total_slices.append(slice(None))
2254
+
2255
+ break
2211
2256
  total_slices = tuple(total_slices)
2212
- return total_slices
2257
+ return total_slices, _slices_as_index
2213
2258
 
2214
2259
  def check_squeeze(self, total_slices: List[slice], is_navigation: bool):
2215
2260
 
@@ -2221,7 +2266,7 @@ class DataWithAxes(DataBase):
2221
2266
  do_squeeze = False
2222
2267
  return do_squeeze
2223
2268
 
2224
- def _slicer(self, slices, is_navigation=True):
2269
+ def _slicer(self, slices, is_navigation=True, is_index=True):
2225
2270
  """Apply a given slice to the data either navigation or signal dimension
2226
2271
 
2227
2272
  Parameters
@@ -2230,6 +2275,8 @@ class DataWithAxes(DataBase):
2230
2275
  the slices to apply to the data
2231
2276
  is_navigation: bool
2232
2277
  if True apply the slices to the navigation dimension else to the signal ones
2278
+ is_index: bool
2279
+ if True the slices are indexes otherwise the slices are axes values to be indexed first
2233
2280
 
2234
2281
  Returns
2235
2282
  -------
@@ -2237,10 +2284,10 @@ class DataWithAxes(DataBase):
2237
2284
  Object of the same type as the initial data, derived from DataWithAxes. But with lower
2238
2285
  data size due to the slicing and with eventually less axes.
2239
2286
  """
2240
-
2241
2287
  if isinstance(slices, numbers.Number) or isinstance(slices, slice):
2242
2288
  slices = [slices]
2243
- total_slices = self._compute_slices(slices, is_navigation)
2289
+
2290
+ total_slices, slices = self._compute_slices(slices, is_navigation, is_index=is_index)
2244
2291
 
2245
2292
  do_squeeze = self.check_squeeze(total_slices, is_navigation)
2246
2293
  new_arrays_data = [squeeze(dat[total_slices], do_squeeze) for dat in self.data]
@@ -2290,11 +2337,11 @@ class DataWithAxes(DataBase):
2290
2337
  if len(nav_indexes) != 0:
2291
2338
  distribution = self.distribution
2292
2339
  else:
2293
- distribution = DataDistribution['uniform']
2340
+ distribution = DataDistribution.uniform
2294
2341
 
2295
2342
  data = DataWithAxes(self.name, data=new_arrays_data, nav_indexes=tuple(nav_indexes),
2296
2343
  axes=axes,
2297
- source='calculated', origin=self.origin,
2344
+ source=DataSource.calculated, origin=self.origin,
2298
2345
  labels=self.labels[:],
2299
2346
  distribution=distribution)
2300
2347
  return data
@@ -1,5 +1,5 @@
1
1
  from .qled import QLED
2
2
  from .push import PushButtonIcon, EditPush, EditPushRel
3
- from .spinbox import QSpinBox_ro, SpinBox
3
+ from .spinbox import QSpinBox_ro, SpinBox, QSpinBoxWithShortcut
4
4
  from .table import TableView, TableModel
5
5
  from .label import LabelWithFont
@@ -1,4 +1,4 @@
1
- from qtpy import QtWidgets, QtGui
1
+ from qtpy import QtWidgets, QtGui,QtCore
2
2
  from pyqtgraph.widgets.SpinBox import SpinBox
3
3
 
4
4
 
@@ -15,7 +15,20 @@ class SpinBox(SpinBox):
15
15
  self.setFont(font)
16
16
  self.setMinimumHeight(min_height)
17
17
 
18
-
18
+ class QSpinBoxWithShortcut(SpinBox):
19
+ """
20
+ QSpinBox but which accepts key sequences and store them as attribute
21
+ For now, it does not apply to regular input such as text or numerics.
22
+ """
23
+ def __init__(self, *args, key_sequences=("Ctrl+Enter",), **kwargs):
24
+ super().__init__(*args, **kwargs)
25
+
26
+ self.shortcut = dict() #Store shortcuts in a dictionnary
27
+ for key_sequence in key_sequences:
28
+ shortcut = QtWidgets.QShortcut(QtGui.QKeySequence(key_sequence), self)
29
+ shortcut.setContext(QtCore.Qt.ShortcutContext.WidgetWithChildrenShortcut)
30
+ self.shortcut[key_sequence] = shortcut
31
+
19
32
  class QSpinBox_ro(SpinBox):
20
33
  def __init__(self, *args, readonly=True, **kwargs):
21
34
  super().__init__(*args, **kwargs)
@@ -58,8 +58,12 @@ class DataManagement(metaclass=ABCMeta):
58
58
  self.close_file()
59
59
 
60
60
  def close_file(self):
61
+ self._h5saver.flush()
61
62
  self._h5saver.close_file()
62
63
 
64
+ def close(self):
65
+ self.close_file()
66
+
63
67
  def _get_next_node_name(self, where: Union[str, Node]) -> str:
64
68
  """Get the formatted next node name given the ones already saved
65
69
 
@@ -600,7 +604,7 @@ class DataEnlargeableSaver(DataSaverLoader):
600
604
  self._axis_saver.add_axis(where, axis)
601
605
 
602
606
  def add_data(self, where: Union[Node, str], data: DataWithAxes,
603
- axis_values: Iterable[float] = None):
607
+ axis_values: Iterable[float] = None, **kwargs):
604
608
  """ Append data to an enlargeable array node
605
609
 
606
610
  Data of dim (0, 1 or 2) will be just appended to the enlargeable array.
@@ -624,17 +628,31 @@ class DataEnlargeableSaver(DataSaverLoader):
624
628
  if self.get_last_node_name(where) is None:
625
629
  if len(data.nav_indexes) == 0:
626
630
  data_init = data
631
+ elif len(data.nav_indexes) == 1: # special case of DataND data
632
+ data_init = data.inav[0]
633
+ add_enl_axes = True
634
+ axis = data.get_axis_from_index(data.nav_indexes[0])[0]
635
+ axis_values = (axis.get_data(),)
636
+ self._enl_axis_names = (axis.label,)
637
+ self._enl_axis_units = (axis.units,)
627
638
  else:
628
639
  raise DataDimError('It is not possible to append DataND')
629
640
  self._create_data_arrays(where, data_init, save_axes=True, add_enl_axes=add_enl_axes)
641
+ elif len(data.nav_indexes) == 1: # special case of DataND data
642
+ add_enl_axes = True
643
+ axis = data.get_axis_from_index(data.nav_indexes[0])[0]
644
+ axis_values = (axis.get_data(),)
630
645
 
631
646
  for ind_data in range(len(data)):
632
647
  array: EARRAY = self.get_node_from_index(where, ind_data)
633
648
  array.append(data[ind_data])
634
- if add_enl_axes and axis_values is not None:
649
+ if add_enl_axes:
635
650
  for ind_axis in range(self._n_enl_axes):
636
651
  axis_array: EARRAY = self._axis_saver.get_node_from_index(where, ind_axis)
637
- axis_array.append(np.array([axis_values[ind_axis]]))
652
+ if not isinstance(axis_values[ind_axis], np.ndarray):
653
+ axis_array.append(np.array([axis_values[ind_axis]]))
654
+ else:
655
+ axis_array.append(axis_values[ind_axis], expand=False)
638
656
  axis_array.attrs['size'] += 1
639
657
 
640
658
 
@@ -862,7 +880,7 @@ class DataToExportEnlargeableSaver(DataToExportSaver):
862
880
  def add_data(self, where: Union[Node, str], data: DataToExport,
863
881
  axis_values: List[Union[float, np.ndarray]] = None,
864
882
  axis_value: Union[float, np.ndarray] = None,
865
- settings_as_xml='', metadata=None,
883
+ settings_as_xml='', metadata=None, **kwargs
866
884
  ):
867
885
  """
868
886
 
@@ -885,7 +903,7 @@ class DataToExportEnlargeableSaver(DataToExportSaver):
885
903
  if axis_values is None and axis_value is not None:
886
904
  axis_values = [axis_value]
887
905
 
888
- super().add_data(where, data, settings_as_xml, metadata)
906
+ super().add_data(where, data, settings_as_xml, metadata, **kwargs)
889
907
  # a parent navigation group (same for all data nodes)
890
908
 
891
909
  where = self._get_node(where)
@@ -12,12 +12,14 @@ import xml.etree.ElementTree as ET
12
12
 
13
13
  import numpy as np
14
14
 
15
+ from pymodaq.utils.logger import set_logger, get_module_name
15
16
  from pymodaq.utils.abstract import ABCMeta, abstract_attribute, abstractmethod
16
17
  from pymodaq.utils.daq_utils import capitalize
17
18
  from pymodaq.utils.data import Axis, DataDim, DataWithAxes, DataToExport, DataDistribution
18
- from .saving import H5SaverLowLevel
19
- from .backends import GROUP, CARRAY, Node, GroupType
20
- from .data_saving import DataToExportSaver, AxisSaverLoader, DataToExportTimedSaver, DataToExportExtendedSaver
19
+ from pymodaq.utils.h5modules.saving import H5SaverLowLevel
20
+ from pymodaq.utils.h5modules.backends import GROUP, CARRAY, Node, GroupType
21
+ from pymodaq.utils.h5modules.data_saving import (DataToExportSaver, AxisSaverLoader,
22
+ DataToExportTimedSaver, DataToExportExtendedSaver)
21
23
  from pymodaq.utils.parameter import ioxml
22
24
 
23
25
  if TYPE_CHECKING:
@@ -27,6 +29,9 @@ if TYPE_CHECKING:
27
29
  from pymodaq.utils.h5modules.h5logging import H5Logger
28
30
 
29
31
 
32
+ logger = set_logger(get_module_name(__file__))
33
+
34
+
30
35
  class ModuleSaver(metaclass=ABCMeta):
31
36
  """Abstract base class to save info and data from main modules (DAQScan, DAQViewer, DAQMove, ...)"""
32
37
  group_type: GroupType = abstract_attribute()
@@ -373,7 +378,7 @@ class ScanSaver(ModuleSaver):
373
378
  try:
374
379
  detector.insert_data(indexes, where=self._module_group, distribution=distribution)
375
380
  except Exception as e:
376
- pass
381
+ logger.exception(f'Cannot insert data: {str(e)}')
377
382
 
378
383
 
379
384
  class LoggerSaver(ScanSaver):
@@ -355,12 +355,12 @@ class ModulesManager(QObject, ParameterManager):
355
355
  sig.connect(slot)
356
356
  else:
357
357
 
358
- for sig in [mod.grab_done_signal for mod in self.detectors_all]:
358
+ for sig in [mod.grab_done_signal for mod in self.detectors]:
359
359
  try:
360
360
  sig.disconnect(slot)
361
361
  except TypeError as e:
362
362
  # means the slot was not previously connected
363
- logger.info(str(e))
363
+ logger.info(f'Could not disconnect grab signal from the {slot} slot', stacklevel=2)
364
364
 
365
365
  self.detectors_connected = connect
366
366
 
@@ -7,7 +7,7 @@ import numpy as np
7
7
 
8
8
  class SliderSpinBox(QtWidgets.QWidget):
9
9
 
10
- def __init__(self, *args, subtype='lin', **kwargs):
10
+ def __init__(self, *args, subtype='linear', **kwargs):
11
11
 
12
12
  super().__init__()
13
13
  self.subtype = subtype
@@ -465,6 +465,8 @@ class View2D(ActionManager, QtCore.QObject):
465
465
  tip='Flip the image left/right', checkable=True)
466
466
  self.add_action('rotate', 'Rotate', 'rotation2',
467
467
  tip='Rotate the image', checkable=True)
468
+ self.add_action('opposite', 'Opposite', 'remove',
469
+ tip='Take the opposite of the image', checkable=True)
468
470
  self.add_action('legend', 'Legend', 'RGB',
469
471
  tip='Show the legend', checkable=True)
470
472
 
@@ -877,6 +879,8 @@ class Viewer2D(ViewerBase):
877
879
  data = np.fliplr(data)
878
880
  if self.view.is_action_checked('rotate'):
879
881
  data = np.flipud(np.transpose(data))
882
+ if self.view.is_action_checked('opposite'):
883
+ data = -data
880
884
  return data
881
885
 
882
886
  def set_visible_items(self):
@@ -917,6 +921,7 @@ class Viewer2D(ViewerBase):
917
921
  self.view.connect_action('flip_ud', slot=self.update_data)
918
922
  self.view.connect_action('flip_lr', slot=self.update_data)
919
923
  self.view.connect_action('rotate', slot=self.update_data)
924
+ self.view.connect_action('opposite', slot=self.update_data)
920
925
  self.view.connect_action('autolevels', slot=self.update_data)
921
926
  self.view.connect_action('isocurve', slot=self.update_data)
922
927
  self.view.histogrammer.gradient_changed.connect(lambda: setattr(self, '_is_gradient_manually_set', True))
pymodaq/utils/slicing.py CHANGED
@@ -26,7 +26,7 @@ class SpecialSlicersData(SpecialSlicers):
26
26
  def __setitem__(self, slices, data: Union[np.ndarray, 'DataWithAxes', 'Axis']):
27
27
  """x.__setitem__(slices, data) <==> x[slices]=data
28
28
  """
29
- slices = self.obj._compute_slices(slices, self.is_navigation)
29
+ total_slices, slices_index = self.obj._compute_slices(slices, self.is_navigation)
30
30
 
31
31
  if hasattr(self.obj, 'base_type') and self.obj.base_type == 'Axis':
32
32
  if isinstance(data, np.ndarray):
@@ -35,14 +35,14 @@ class SpecialSlicersData(SpecialSlicers):
35
35
  data_to_replace = data.get_data()
36
36
  if hasattr(self.obj, 'units') and self.obj.data is None:
37
37
  self.obj.create_linear_data(len(self.obj))
38
- self.obj.data[slices] = data_to_replace
38
+ self.obj.data[total_slices] = data_to_replace
39
39
  else:
40
40
  for ind in range(len(self.obj)):
41
41
  if isinstance(data, np.ndarray):
42
42
  data_to_replace = data
43
43
  else: # means it's a DataWithAxes
44
44
  data_to_replace = data[ind]
45
- self.obj[ind][slices] = data_to_replace
45
+ self.obj[ind][total_slices] = data_to_replace
46
46
 
47
47
  def __len__(self):
48
48
  return self.obj.axes_manager.sig_shape[0]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pymodaq
3
- Version: 4.4.7
3
+ Version: 4.4.11
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
@@ -27,6 +27,8 @@ License: The MIT License (MIT)
27
27
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
28
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
29
  THE SOFTWARE.
30
+ License-File: AUTHORS.md
31
+ License-File: LICENSE
30
32
  Classifier: Development Status :: 5 - Production/Stable
31
33
  Classifier: Environment :: Other Environment
32
34
  Classifier: Intended Audience :: Science/Research
@@ -61,7 +63,8 @@ Requires-Dist: qtpy
61
63
  Requires-Dist: scipy
62
64
  Requires-Dist: setuptools>=60
63
65
  Requires-Dist: simple-pid
64
- Requires-Dist: tables<3.9
66
+ Requires-Dist: tables<3.9; python_version < '3.10'
67
+ Requires-Dist: tables>=3.10; python_version >= '3.10'
65
68
  Requires-Dist: toml
66
69
  Description-Content-Type: text/x-rst
67
70
 
@@ -3,13 +3,13 @@ pymodaq/dashboard.py,sha256=4fbV92erom0yWwqPMtx3KW1q-d6QYflV-EhOZMg24a4,64476
3
3
  pymodaq/icon.ico,sha256=hOHHfNDENKphQvG1WDleSEYcHukneR2eRFJu8isIlD4,74359
4
4
  pymodaq/splash.png,sha256=ow8IECF3tPRUMA4tf2tMu1aRiMaxx91_Y2ckVxkrmF0,53114
5
5
  pymodaq/control_modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- pymodaq/control_modules/daq_move.py,sha256=0zit9shAo6HAVxkaYdhhuSN-yu8j8T_TLSF6YOaM85Y,37842
7
- pymodaq/control_modules/daq_move_ui.py,sha256=IbqNAErwXGjKUbYEptvZUz3J8MapNBFIbQnUf9nQrMw,15753
6
+ pymodaq/control_modules/daq_move.py,sha256=wT95Mnsnc6uOC0DpzavxI6ks4k-apowRB3DkOexX9w4,38051
7
+ pymodaq/control_modules/daq_move_ui.py,sha256=qHlZW_FQUCIENBVlZ1HciPzmXTcLf1kyXi3YHWqJyV8,16447
8
8
  pymodaq/control_modules/daq_viewer.py,sha256=5CYmdWHGE7sQApeMfdWNV3zbPyoxxYtzFlQ1PaEw4fI,57883
9
9
  pymodaq/control_modules/daq_viewer_ui.py,sha256=FWP3jdIOR9vTgYqNaaodteGZ3dwgQ1GdWKrOpOAuSrs,15693
10
10
  pymodaq/control_modules/mocks.py,sha256=hh_xSWp9g1UV3NAQVD9Ft9tNWfTsSvKU0OU0trgzP2w,1956
11
- pymodaq/control_modules/move_utility_classes.py,sha256=PDiPVAfOEAhtTc6MS0oVCy3AL-6KLSxOgYg3c5jctoA,43417
12
- pymodaq/control_modules/utils.py,sha256=5YdSwq_lFJm7IalYWe_Hn1U4LUoUmo1gedvV9UguU0Y,20016
11
+ pymodaq/control_modules/move_utility_classes.py,sha256=y0gzF1DnKdh8i3b34FasJHRlFHVaYQn2rp93ntLfjf0,43465
12
+ pymodaq/control_modules/utils.py,sha256=3uJWizLRKmHvM_9sUOhChrCW31r2Q8GtyYpFw1diBKE,19985
13
13
  pymodaq/control_modules/viewer_utility_classes.py,sha256=OHxwue1t3z2AXyeqNjnwPT2pMc8yXhnqyiWc9IdCI2c,26841
14
14
  pymodaq/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  pymodaq/examples/custom_app.py,sha256=2wQR0hlPWjZrWK0abNF6ASv8iQyJqRn2CKnBa_nAgN4,10452
@@ -38,8 +38,8 @@ pymodaq/examples/Labview_TCP_Client/cmd_types.ctl,sha256=gwuDyGcte11Zqx0C-U8ljRy
38
38
  pymodaq/extensions/__init__.py,sha256=MGZb_QDwL5nlV7XLvBGW9x0GpUwPSpECyD8lCf9EN38,459
39
39
  pymodaq/extensions/console.py,sha256=_hlHupD7DeoOcvEvN_OiGDB6_no50x2J8R6pE2eACgo,2303
40
40
  pymodaq/extensions/daq_logger.py,sha256=hEAb98fv1qJxcH9NPvaiLp1SPWkBVBwpujJhuyAiVmQ,18820
41
- pymodaq/extensions/daq_scan.py,sha256=CGBPrhLRHPmFJunKQ1aK5RgToXemzNEPdYSHn7e36DE,49414
42
- pymodaq/extensions/daq_scan_ui.py,sha256=bYJaWBLQMl2Yu1PAxc-bCldh4keWXd6l12ttrHqExE0,10181
41
+ pymodaq/extensions/daq_scan.py,sha256=gZwol7p4puiLGAiUexCGYh5iWFJXHvVDgK8menFCgZk,50684
42
+ pymodaq/extensions/daq_scan_ui.py,sha256=EHIpTZIG8wTtPI9zVzUTYBgSvvYYjc4KGSNXKGR18RI,10182
43
43
  pymodaq/extensions/h5browser.py,sha256=eQZ8YhPHdiBCyHIn1JJJLn7OgZsc1XlcETgqdafofZs,1140
44
44
  pymodaq/extensions/utils.py,sha256=sh4SxJx5_lGJ6aJE8p8VJ2zrptW8pyze73aT_52xOwQ,2255
45
45
  pymodaq/extensions/bayesian/__init__.py,sha256=QMV9tq8Axi6YjoGj4UguBO8Zvuh26r5s91bm6YU7Itk,55
@@ -47,11 +47,11 @@ pymodaq/extensions/bayesian/bayesian_optimisation.py,sha256=zB6DoyHx6nsKHsl3G4T0
47
47
  pymodaq/extensions/bayesian/utils.py,sha256=1m_NpYMr-Swg2ImtMvbh-XOxutkAVwc0M2BWeK_NMIg,15544
48
48
  pymodaq/extensions/pid/__init__.py,sha256=jm4axOgTnYVPsftjcjlvC_07KsdTY1H7CnOCYhOvY-o,473
49
49
  pymodaq/extensions/pid/actuator_controller.py,sha256=SOE2GjevbqxqxXewW0DMgoqNL_0CaPdNLjyKNc6ULKI,377
50
- pymodaq/extensions/pid/daq_move_PID.py,sha256=kqWAnYbhSMtrqu4cK1AtOuBD0UjPlrAFl-uB39ToPts,2151
50
+ pymodaq/extensions/pid/daq_move_PID.py,sha256=oH-Q9QLQgxsa8J3iWPpHMAPUCfbr72NWww1CvtKDsOA,2155
51
51
  pymodaq/extensions/pid/pid_controller.py,sha256=5iTEiv69MlHW7-sUFCzvQlqZzAoXiJ_mRQtFntvFBIE,28511
52
52
  pymodaq/extensions/pid/utils.py,sha256=pxexKSg-3a1CWLQtCFZR6ZtdI8NY1Yl7J87aSBi2q2o,7984
53
53
  pymodaq/post_treatment/__init__.py,sha256=xaaLFZJ7OLqI_7yPurFk89A7m2ywSbYDXAsdE-QQ8Zg,81
54
- pymodaq/post_treatment/load_and_plot.py,sha256=UZaAMrC3b9VXcsZsjc3-7LxxUEuia_5ECUqDB7G_EoM,12140
54
+ pymodaq/post_treatment/load_and_plot.py,sha256=a-qU4TfW7htsa_4CcJy_mB8N8Fw12Je7q7syud3y7tQ,13902
55
55
  pymodaq/post_treatment/process_to_scalar.py,sha256=NHntybqpDhDjQJ224Dhf9Ij_ql-fAEMRT6egA6UEGfA,11568
56
56
  pymodaq/post_treatment/daq_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  pymodaq/post_treatment/daq_measurement/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -59,15 +59,15 @@ pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py,sha256=1u7hWDaiwsZ
59
59
  pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui,sha256=PyzbCWPMkh5oIYYteZczXyWMeHKW9EJmM1QlzXhnyTk,7037
60
60
  pymodaq/post_treatment/daq_measurement/daq_measurement_main.py,sha256=CAKwcWMOD86aXB8mbdxOK7e8nZRos5d59FzDtqK1QoY,17093
61
61
  pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat,sha256=e1tu2A67MS9fk3jhriF6saQgRxWIucIvNW92iWXFP6E,164
62
- pymodaq/resources/VERSION,sha256=ib4rQ9G3YTfdijgNEWBcOoAIZQyUy-mfF0jvGog9fno,19
62
+ pymodaq/resources/VERSION,sha256=w0jMnA4fFPN7V67D511deSQoG74Sk9UOLlGvO9RkZ6M,20
63
63
  pymodaq/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- pymodaq/resources/config_template.toml,sha256=d3pofgIK5FxaRMELAI1qEsRcMD3GlYd87zZjDj9G9m0,3210
64
+ pymodaq/resources/config_template.toml,sha256=twhcKsROU3fFKPQq7CcB5gXI4w6U6ZeAxaUQZcRmxZ4,3232
65
65
  pymodaq/resources/preset_default.xml,sha256=Dt8iWLwPPOPtcG00JCVP-mh-G7KC6B0YN8hd8RQdnNI,27256
66
66
  pymodaq/resources/setup_plugin.py,sha256=jvMuSp4UxGaPUe9uPUvHg9DrdwyFakG6_sFy_zXb1f8,3182
67
67
  pymodaq/resources/triangulation_data.npy,sha256=Dzq6eE8f_i7Woloy1iUn6N1OfVdBZ4WnK4J4SCoqXso,9320
68
68
  pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.bat,sha256=gqBmrc6Cfzn7wIZQtzgcglKRQ8zLXLW9Xt8LWxkJdw0,205
69
69
  pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources.qrc,sha256=S99847o7hvKYOWa9lR3qErMHwcjhjYPEduoq2ylCn4I,10123
70
- pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py,sha256=ctA38bv3tKIJVsDgqujg0uHSnByecXZPZMZSXgxogHA,8332435
70
+ pymodaq/resources/QtDesigner_Ressources/QtDesigner_ressources_rc.py,sha256=1JdB2hKdHSrBH-uIFylPcuTtTN94hhgiP0zlfWRyAfQ,8332434
71
71
  pymodaq/resources/QtDesigner_Ressources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  pymodaq/resources/QtDesigner_Ressources/credit.rst,sha256=F2PpCDeMkzYWb47RwrIrwujefiiiN25gI2d3s5QqjeI,342
73
73
  pymodaq/resources/QtDesigner_Ressources/icons.svg,sha256=7leLubaJv4E_JnmTdQw00HvrmFBZ3koShefBUbZ2UkU,5703
@@ -312,7 +312,7 @@ pymodaq/utils/chrono_timer.py,sha256=rwX8apS8B-IKhA0Cp2H9tLz0BRN7G3Pg5ptozvd3MKM
312
312
  pymodaq/utils/config.py,sha256=0QqoBJC4ECuIeh1UsvUQqhxkKl7Vfgi4iERp-6qNWAc,16202
313
313
  pymodaq/utils/conftests.py,sha256=3Ak8WEpa3EhAp73Yb1LLq8YFONhPqiL7gG9eSDIoTNc,58
314
314
  pymodaq/utils/daq_utils.py,sha256=0jTrbT0aaZr3KaTgeDicmK9FbVnu3iaWBmNHnNJpr3A,28050
315
- pymodaq/utils/data.py,sha256=RdzEDz3ziRmwbaqy875fLr06jrmaG1wzU-0e_5QQKXM,111908
315
+ pymodaq/utils/data.py,sha256=v5ONtOH4E51ZNIIsPEtMTnHlFSrhC7t5gwno_Z3dckw,114198
316
316
  pymodaq/utils/enums.py,sha256=wpRipioUJkKcEfoaY2NrDQ2WhGxZTZiZoJty5f2Ljpc,2236
317
317
  pymodaq/utils/exceptions.py,sha256=wLO6VlofzfwWkOOWMN2B-3NEWMfpgygyeEdakIx_rAs,668
318
318
  pymodaq/utils/factory.py,sha256=QLqAPFnTZ93eUpmAAIr7kESDk2enD57RNSuFUsjxE4E,2311
@@ -320,7 +320,7 @@ pymodaq/utils/logger.py,sha256=VxymWeeS38VjNre3wjY_lBv06ynstPyI5JrFNgo4PXc,2754
320
320
  pymodaq/utils/math_utils.py,sha256=bh8pMXomoAv_Lxfq26izDacK9RByFi3QUbPd-zFVqpA,18532
321
321
  pymodaq/utils/messenger.py,sha256=N5SPc9NomIGtK0TihQ0oq9evlxyWNYELWfpr2s8PoWw,2072
322
322
  pymodaq/utils/qvariant.py,sha256=iIBp-DDk5OVBIEqX5SwqwrJyy5t2cRgFFyfgvxQOHqM,311
323
- pymodaq/utils/slicing.py,sha256=qhi70yX6HltIhPCRdz8tob34p1d0gGTa5_XZvKKl8iM,2207
323
+ pymodaq/utils/slicing.py,sha256=gvxJPe-A7pWoCSZ1U2ukOq3m6VRf981ggSmcETQ5DGI,2239
324
324
  pymodaq/utils/units.py,sha256=Tj4O5qZBMBuUbjTZSH10UzSbU5z_SHVSu2VLw82tceE,3959
325
325
  pymodaq/utils/abstract/__init__.py,sha256=m7GEoQdFlnWuhwp-qlJ5S_Il6pKDyMEX8MAPlfDSxKU,1260
326
326
  pymodaq/utils/abstract/logger.py,sha256=I-t-2nSIVHHUfHJ-3IoqsuULZiIsXRe4saotaEL3-Fk,1095
@@ -336,22 +336,22 @@ pymodaq/utils/gui_utils/layout.py,sha256=6oczLLGwwEN4EQ8yUDnz0-4Ue2wlyCRklKsVD1G
336
336
  pymodaq/utils/gui_utils/list_picker.py,sha256=ddYnRTlRlgwdJSy0Q98IzYWHzIf2GS6ABl8XSS9kVXM,1190
337
337
  pymodaq/utils/gui_utils/loader_utils.py,sha256=Gg0d31fjkqDq3l1WuRMbLzKiKPOIYdbUrcfGOgoXRk0,1298
338
338
  pymodaq/utils/gui_utils/utils.py,sha256=aPxFCnG4skDp-0UuhAudq6BPZnEXoAf6FOEJyrhUjx0,6089
339
- pymodaq/utils/gui_utils/widgets/__init__.py,sha256=LThGzmbFKbp2FtTTF_L7pHjyBzfB7F_bhMF4rPTwrUY,195
339
+ pymodaq/utils/gui_utils/widgets/__init__.py,sha256=vtDpBo8YEW6KR_Kyu85Po1hlTbyudmiRRS00jiQyst0,217
340
340
  pymodaq/utils/gui_utils/widgets/label.py,sha256=C2MU8i_Yy_oVRW7yal_ghB1Y5Bj_a9o8IFZWW3br-KM,600
341
341
  pymodaq/utils/gui_utils/widgets/lcd.py,sha256=jBleZJ-ChpbSt0iJ3T-XFwAhPUTBsofceCXJylzgA9s,3325
342
342
  pymodaq/utils/gui_utils/widgets/push.py,sha256=pS_KTG6JV4EKmaUzpX1pAX5RsXXDeRxBWOEwcY-BZKA,4748
343
343
  pymodaq/utils/gui_utils/widgets/qled.py,sha256=HuVDYApG8rwiiNhVrN-ZWZ2W8OxK7A29MX1zQAKjJqc,2096
344
- pymodaq/utils/gui_utils/widgets/spinbox.py,sha256=cgKPXDSFjy0DFQ0VCaMrGwxFAHsxu6LTSsw8_BcVmhg,742
344
+ pymodaq/utils/gui_utils/widgets/spinbox.py,sha256=0wl1IWgvxYu-qPEu3MicA6Du0wY2SpBeOeC0jMAKWdA,1408
345
345
  pymodaq/utils/gui_utils/widgets/table.py,sha256=zMvjf5-HUx_sHV3Wh99HlH4LO1GLDBnS1ONOTXfKjnc,8770
346
346
  pymodaq/utils/gui_utils/widgets/tree_layout.py,sha256=OCXj59SzVQPi1ksxxvvbr5AIZ5mXN0_JdwcNMMR5wUg,6625
347
347
  pymodaq/utils/gui_utils/widgets/tree_toml.py,sha256=0O2j_F0kzVjyG2dSwYtw4HNtmS0WJo0VWZF1rnil68A,4704
348
348
  pymodaq/utils/h5modules/__init__.py,sha256=x3_4ELvG9onTKEFgIt9xEGg_mA1bB07dvVbU9q0xQKw,104
349
349
  pymodaq/utils/h5modules/backends.py,sha256=i-3x_DtTbnYKqft9Y3V1PuinGRXZ9DVv0Mg9TNTA33Q,33269
350
350
  pymodaq/utils/h5modules/browsing.py,sha256=B39JZFdm3WCaOTnG0tioPnp6A1zFCRWZkbcXfgI_eHE,23542
351
- pymodaq/utils/h5modules/data_saving.py,sha256=ybCDHlzNk9LCkOnRGLGdsSXk-Rjt455FCmgMxdjKM-4,42408
351
+ pymodaq/utils/h5modules/data_saving.py,sha256=rI7uxfhHEWG7Nl6lqvXwBeRpKAJEkKwBqRIT5MsL4j8,43255
352
352
  pymodaq/utils/h5modules/exporter.py,sha256=iCfUjkuGjs3-ijcUkt38NMrjO8tI9wXShvwYHJIUU70,3670
353
353
  pymodaq/utils/h5modules/h5logging.py,sha256=UhRo9YvjU5Ujw_i5aPHXOgOdw_IszxmekOa7gYUY5AQ,2492
354
- pymodaq/utils/h5modules/module_saving.py,sha256=RUhKaKakLyNaWUrXYrhkp22_nunDPegX5EQUwpsSA5Q,13997
354
+ pymodaq/utils/h5modules/module_saving.py,sha256=3x85IrN-YNK2-xySpDA7UJYgvt3fvoGcEz6_OCXxnx0,14271
355
355
  pymodaq/utils/h5modules/saving.py,sha256=Hfw8gGG7AM_ZroxIOhTvfwHr4CDW9VarWw-zMkQS5TM,34036
356
356
  pymodaq/utils/h5modules/utils.py,sha256=0isF661xthXlT3hFJvXTcgGqkZcGQmSanTNAGSInez4,3368
357
357
  pymodaq/utils/h5modules/exporters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -369,7 +369,7 @@ pymodaq/utils/leco/utils.py,sha256=jCHhco89uG9UA-t9LpaEuyZQMQW5XQP_t0URcvqLDNU,2
369
369
  pymodaq/utils/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
370
370
  pymodaq/utils/managers/action_manager.py,sha256=v99NuRq7uT_PNTAsqbUyoWyDGUzhlP6dYtdAO30_cbc,17756
371
371
  pymodaq/utils/managers/batchscan_manager.py,sha256=jcL08YvFafX5kiy03BV1_6obt2Xogiby5pvTKeN8_ho,13905
372
- pymodaq/utils/managers/modules_manager.py,sha256=RKpt1RU0VrELBPmTAlLB_B5k-5KX7deHnequgOFfYKk,20821
372
+ pymodaq/utils/managers/modules_manager.py,sha256=Fdp92kpHKjULAfFnCSftyIzwLL5XTjlLc3kJnFzxANw,20881
373
373
  pymodaq/utils/managers/overshoot_manager.py,sha256=fe_CR1Bkw85BER34MoVFlm-xtKl9Hr9bkf2nyaz9hXg,7158
374
374
  pymodaq/utils/managers/parameter_manager.py,sha256=hO3RXcpkYOtuqjAQaD5H3r71rVEHntYg64VHZwVCQBg,11473
375
375
  pymodaq/utils/managers/preset_manager.py,sha256=m8r_TcfFbUatddBX9SJj7XI_GZ3FhoiwzhFgocw9jZ8,9481
@@ -388,7 +388,7 @@ pymodaq/utils/parameter/pymodaq_ptypes/led.py,sha256=oDKM3k4aPy_CArgQIEqjLx3KG5C
388
388
  pymodaq/utils/parameter/pymodaq_ptypes/list.py,sha256=bXGlUxKVAA5_QY41BmzG2iWWoh1Qq-w-QHgMo-_Kc7Q,5716
389
389
  pymodaq/utils/parameter/pymodaq_ptypes/numeric.py,sha256=lzklQWAnzh9LDC20hdlGbAj2NdVzNDDeJke1KnPi7GU,560
390
390
  pymodaq/utils/parameter/pymodaq_ptypes/pixmap.py,sha256=vIhWscDZRM7Bc3RRf4kBs2g0QNE3-tfp1P4T8EO7AxY,5534
391
- pymodaq/utils/parameter/pymodaq_ptypes/slide.py,sha256=RStaK9SXGEI7c2ESDX5DhZvsDhATl5I8swChsUDwHuM,5748
391
+ pymodaq/utils/parameter/pymodaq_ptypes/slide.py,sha256=1mhGLSei9DRqNKhDF5PFR7WNfBREDZ-TjPHD53ETs7g,5751
392
392
  pymodaq/utils/parameter/pymodaq_ptypes/table.py,sha256=kM6Kb_KZfJ-sA6tSgn76EDBZK_Cbpc4jfw2KEJ5E2Cg,4819
393
393
  pymodaq/utils/parameter/pymodaq_ptypes/tableview.py,sha256=EXMHEGlkL2JC7_fau2ysL3oCvq2XmeCuJNYEu-hp9iY,4866
394
394
  pymodaq/utils/parameter/pymodaq_ptypes/text.py,sha256=iPMS3dEz2evsX_1ThX-sqNzfyIL8w0D0tpM87jF8ODo,4708
@@ -404,7 +404,7 @@ pymodaq/utils/plotting/data_viewers/viewer.py,sha256=DhPa4Dw42HfOdqEGEq2KKmIXg_z
404
404
  pymodaq/utils/plotting/data_viewers/viewer0D.py,sha256=YgCNuqAW5NOURw2pqPWT4WHHaowXjySDWoYgY7lbl-s,11609
405
405
  pymodaq/utils/plotting/data_viewers/viewer1D.py,sha256=7Iz0fDIgP_IpDt2QtB3Do8nONM0eZemcHSrv3XeJn_Q,32239
406
406
  pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py,sha256=u91tVhmi_WIlVa8areapb0xWu0NOr5pVRLMylY_in7g,7432
407
- pymodaq/utils/plotting/data_viewers/viewer2D.py,sha256=vjXuKjDguJLY8XLRq_BbrHWqgqE4KJIS2Cx2Qpxyj_k,46214
407
+ pymodaq/utils/plotting/data_viewers/viewer2D.py,sha256=KKWIQoFeZ21WMhByUcMC5WNQQu1Ahy_s-8vwlfc5HE4,46495
408
408
  pymodaq/utils/plotting/data_viewers/viewer2D_basic.py,sha256=aRLu8JVZZI8PH6Lxl8oITpHwUXaUY3PyLW6eHzkf76o,5588
409
409
  pymodaq/utils/plotting/data_viewers/viewerND.py,sha256=NrEPrFcE-UxuC95l5W258YqrP58-4WvTg2H80wfucaA,38531
410
410
  pymodaq/utils/plotting/items/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -439,8 +439,9 @@ pymodaq/utils/tcp_ip/__init__.py,sha256=1e_EK0AgvdoLAD_CSGGEaITZdy6OWCO7ih9IAIp7
439
439
  pymodaq/utils/tcp_ip/mysocket.py,sha256=StAWj8dzHeMnbLj68Sel81uWFy-YkKVNRnVf7gXrESI,3452
440
440
  pymodaq/utils/tcp_ip/serializer.py,sha256=kXmvFLR5ZedjY2rVGUs83lsJk9Z9l_iAWR6srDxzaYQ,29262
441
441
  pymodaq/utils/tcp_ip/tcp_server_client.py,sha256=xIMTNgVW_rKK0yTi4FDNFLf85-Akb27Jz2LdrvOrP68,30660
442
- pymodaq-4.4.7.dist-info/METADATA,sha256=jkmtri16XiPHC-2It45CeeZp--C6giXVJ0FLp3g13u8,7611
443
- pymodaq-4.4.7.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
444
- pymodaq-4.4.7.dist-info/entry_points.txt,sha256=RAzdYNjvUT28I2eiCKki_g2NzXq0woWxhev6lwzwRv8,348
445
- pymodaq-4.4.7.dist-info/licenses/LICENSE,sha256=VKOejxexXAe3XwfhAhcFGqeXQ12irxVHdeAojZwFEI8,1108
446
- pymodaq-4.4.7.dist-info/RECORD,,
442
+ pymodaq-4.4.11.dist-info/METADATA,sha256=tPXoiwJZQyM7G-8tlExdAMuyJtudR4VJTKtN042Wo58,7738
443
+ pymodaq-4.4.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
444
+ pymodaq-4.4.11.dist-info/entry_points.txt,sha256=RAzdYNjvUT28I2eiCKki_g2NzXq0woWxhev6lwzwRv8,348
445
+ pymodaq-4.4.11.dist-info/licenses/AUTHORS.md,sha256=8f-B6E-I2KPlBSJIcutEZTedoYQHERaXADbx6q_9vjg,1295
446
+ pymodaq-4.4.11.dist-info/licenses/LICENSE,sha256=VKOejxexXAe3XwfhAhcFGqeXQ12irxVHdeAojZwFEI8,1108
447
+ pymodaq-4.4.11.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.1
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,46 @@
1
+ Here is a list of the main contributors:
2
+
3
+ ## Main modules
4
+ ***************
5
+
6
+ Functionalities
7
+ ---------------
8
+
9
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
10
+ * David Bresteau, Research Engineer at Attolab facility, CEA Saclay
11
+ * Nicolas Tappy, Engineer at Attolight (https://attolight.com/)
12
+
13
+ Cleaning
14
+ --------
15
+
16
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
17
+ * David Trémouilles, Researcher at LAAS/CNRS
18
+ * Loïc GUILMARD, Engineer at CETHIL/CNRS
19
+
20
+ ## Plugins
21
+ **********
22
+
23
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
24
+ * Sophie Meuret, Researcher at CEMES/CNRS
25
+ * David Bresteau, Research Engineer at Attolab facility, CEA Saclay
26
+ * and many others...
27
+
28
+ ## Extensions
29
+ *************
30
+
31
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
32
+ * Romain Geneaux, Researcher at CEA Saclay contributed to the PyMoDAQ-Femto extension
33
+
34
+ ## Documentation
35
+ ****************
36
+
37
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
38
+ * Matthieu Cabos helped with this documentation
39
+ * David Bresteau wrote the documentation of the PID extension and the tutorial: :ref:`plugin_development`
40
+ * Loïc GUILMARD, Engineer at CETHIL/CNRS
41
+
42
+ ## Testing
43
+ **********
44
+
45
+ * Sébastien Weber, Research Engineer at CEMES/CNRS
46
+ * Pierre Jannot wrote tests with a total of 5000 lines of code tested during his internship at CEMES in 2021