pymodaq 4.3.7__py3-none-any.whl → 4.4.0__py3-none-any.whl

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

Potentially problematic release.


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

pymodaq/__init__.py CHANGED
@@ -51,6 +51,7 @@ try:
51
51
  ureg.default_format = '~'
52
52
  Q_ = ureg.Quantity
53
53
  Unit = ureg.Unit
54
+ ureg.define('whatever = 1 = wr') # defining whatever as a dimensionless quantity
54
55
  logger.info('')
55
56
  logger.info('')
56
57
 
@@ -23,7 +23,8 @@ from easydict import EasyDict as edict
23
23
  from pymodaq.utils.logger import set_logger, get_module_name
24
24
  from pymodaq.control_modules.utils import ParameterControlModule
25
25
  from pymodaq.control_modules.daq_move_ui import DAQ_Move_UI, ThreadCommand
26
- from pymodaq.control_modules.move_utility_classes import MoveCommand, DAQ_Move_base, DataActuatorType
26
+ from pymodaq.control_modules.move_utility_classes import (MoveCommand, DAQ_Move_base,
27
+ DataActuatorType, check_units)
27
28
  from pymodaq.control_modules.move_utility_classes import params as daq_move_params
28
29
  from pymodaq.utils import daq_utils as utils
29
30
  from pymodaq.utils.parameter import utils as putils
@@ -32,7 +33,8 @@ from pymodaq.utils import config as config_mod
32
33
  from pymodaq.utils.exceptions import ActuatorError
33
34
  from pymodaq.utils.messenger import deprecation_msg
34
35
  from pymodaq.utils.h5modules import module_saving
35
- from pymodaq.utils.data import DataRaw, DataToExport, DataFromPlugins, DataActuator
36
+ from pymodaq.utils.data import (DataRaw, DataToExport, DataFromPlugins, DataActuator, Unit,
37
+ DataUnitError)
36
38
  from pymodaq.utils.h5modules.backends import Node
37
39
  from pymodaq.utils.parameter import ioxml, Parameter
38
40
 
@@ -161,11 +163,13 @@ class DAQ_Move(ParameterControlModule):
161
163
  self.stop_motion()
162
164
  elif cmd.command == 'move_abs':
163
165
  data_act: DataActuator = cmd.attribute
164
- data_act.force_units(self.units)
166
+ if not Unit(data_act.units).is_compatible_with(self.units) and data_act.units != '':
167
+ data_act.force_units(self.units)
165
168
  self.move_abs(data_act)
166
169
  elif cmd.command == 'move_rel':
167
170
  data_act: DataActuator = cmd.attribute
168
- data_act.force_units(self.units)
171
+ if not Unit(data_act.units).is_compatible_with(self.units) and data_act.units != '':
172
+ data_act.force_units(self.units)
169
173
  self.move_rel(data_act)
170
174
  elif cmd.command == 'show_log':
171
175
  self.show_log()
@@ -591,7 +595,14 @@ class DAQ_Move(ParameterControlModule):
591
595
  def units(self, unit: str):
592
596
  self.settings.child('move_settings', 'units').setValue(unit)
593
597
  if self.ui is not None and config('actuator', 'display_units'):
594
- self.ui.set_unit_as_suffix(unit)
598
+ if unit == '°':
599
+ # special cas as pint base unit for angles are radians
600
+ self.ui.set_unit_as_suffix(unit)
601
+ else:
602
+ # if the controller units are in mm the displayed unit will be m
603
+ # because m is the base unit
604
+ # then the user could ask for mm, km, µm...
605
+ self.ui.set_unit_as_suffix(str(Q_(1, unit).to_base_units().units))
595
606
 
596
607
  def update_settings(self):
597
608
 
@@ -668,8 +679,6 @@ class DAQ_Move_Hardware(QObject):
668
679
  self._title = title
669
680
  self.hardware: Optional[DAQ_Move_base] = None
670
681
  self.actuator_type = actuator_type
671
- self.current_position: DataActuator = position
672
- self._target_value: Optional[DataActuator] = None
673
682
  self.hardware_adress = None
674
683
  self.axis_address = None
675
684
  self.motion_stoped = False
@@ -692,9 +701,8 @@ class DAQ_Move_Hardware(QObject):
692
701
  """
693
702
  pos = self.hardware.get_actuator_value()
694
703
  if self.hardware.data_actuator_type == DataActuatorType.float:
695
- return DataActuator(self._title, data=pos, units=self.units)
696
- else:
697
- return pos
704
+ pos = DataActuator(self._title, data=pos, units=self.hardware.axis_unit)
705
+ return pos
698
706
 
699
707
  def check_position(self):
700
708
  """Get the current position checking the hardware position (deprecated)
@@ -743,6 +751,8 @@ class DAQ_Move_Hardware(QObject):
743
751
  status.initialized = infos[1]
744
752
  status.controller = self.hardware.controller
745
753
  self.hardware.move_done_signal.connect(self.move_done)
754
+ if status.initialized:
755
+ self.status_sig.emit(ThreadCommand('get_actuator_value', [self.get_actuator_value()]))
746
756
 
747
757
  return status
748
758
  except Exception as e:
@@ -753,15 +763,13 @@ class DAQ_Move_Hardware(QObject):
753
763
  """
754
764
 
755
765
  """
756
- # if isinstance(position, Number):
757
- # position = float(position) # because it may be a numpy float and could cause issues
758
- # # see https://github.com/pythonnet/pythonnet/issues/1833
759
- self._target_value = position
766
+ position = check_units(position, self.hardware.axis_unit)
760
767
  self.hardware.move_is_done = False
761
768
  self.hardware.ispolling = polling
762
769
  if self.hardware.data_actuator_type.name == 'float':
763
770
  self.hardware.move_abs(position.value())
764
771
  else:
772
+ position.units = self.hardware.axis_unit # convert to plugin controller current axis units
765
773
  self.hardware.move_abs(position)
766
774
  self.hardware.poll_moving()
767
775
 
@@ -769,14 +777,14 @@ class DAQ_Move_Hardware(QObject):
769
777
  """
770
778
 
771
779
  """
772
-
780
+ rel_position = check_units(rel_position, self.hardware.axis_unit)
773
781
  self.hardware.move_is_done = False
774
- self._target_value = self.current_position + rel_position
775
782
  self.hardware.ispolling = polling
776
783
 
777
784
  if self.hardware.data_actuator_type.name == 'float':
778
785
  self.hardware.move_rel(rel_position.value())
779
786
  else:
787
+ rel_position.units = self.hardware.axis_unit # convert to plugin current axis units
780
788
  self.hardware.move_rel(rel_position)
781
789
 
782
790
  self.hardware.poll_moving()
@@ -798,7 +806,6 @@ class DAQ_Move_Hardware(QObject):
798
806
 
799
807
  """
800
808
  self.hardware.move_is_done = False
801
- self._target_value = 0
802
809
  self.hardware.move_home()
803
810
 
804
811
  @Slot(DataActuator)
@@ -7,6 +7,7 @@ Created the 28/07/2022
7
7
 
8
8
  from typing import List
9
9
  import sys
10
+ from pint.errors import DimensionalityError
10
11
 
11
12
  from qtpy import QtWidgets
12
13
  from qtpy.QtCore import Signal, Qt
@@ -65,12 +66,17 @@ class DAQ_Move_UI(ControlModuleUI):
65
66
 
66
67
  super().__init__(parent)
67
68
  self.title = title
69
+ self._unit = ''
68
70
  self.setup_ui()
69
71
 
70
72
  self.enable_move_buttons(False)
71
73
 
72
74
  def display_value(self, value: DataActuator):
73
- self.current_value_sb.setValue(value.value())
75
+ try:
76
+ self.current_value_sb.setValue(value.value(self._unit))
77
+ except DimensionalityError as e:
78
+ value.force_units(self._unit)
79
+ self.current_value_sb.setValue(value.value())
74
80
 
75
81
  @property
76
82
  def actuator_init(self):
@@ -193,9 +199,9 @@ class DAQ_Move_UI(ControlModuleUI):
193
199
  self.main_ui.layout().addWidget(self.toolbar, 0, 0, 1, 2)
194
200
  self.main_ui.layout().addWidget(self.move_toolbar, 1, 0, 1, 2)
195
201
 
196
- self.abs_value_sb = SpinBox(step=0.1, dec=True)
202
+ self.abs_value_sb = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
197
203
  self.abs_value_sb.setStyleSheet("background-color : lightgreen; color: black")
198
- self.abs_value_sb_2 = SpinBox(step=0.1, dec=True)
204
+ self.abs_value_sb_2 = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
199
205
  self.abs_value_sb_2.setStyleSheet("background-color : lightcoral; color: black")
200
206
  self.move_toolbar.addWidget(self.abs_value_sb)
201
207
  self.move_toolbar.addWidget(self.abs_value_sb_2)
@@ -221,7 +227,7 @@ class DAQ_Move_UI(ControlModuleUI):
221
227
  self.control_ui.layout().addWidget(LabelWithFont('Abs. Value'), 0, 0)
222
228
  self.find_home_pb = PushButtonIcon('home2', 'Find Home')
223
229
  self.control_ui.layout().addWidget(self.find_home_pb, 0, 1)
224
- self.abs_value_sb_bis = SpinBox(step=0.1, dec=True)
230
+ self.abs_value_sb_bis = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
225
231
  self.control_ui.layout().addWidget(self.abs_value_sb_bis, 1, 0)
226
232
  self.move_abs_pb = PushButtonIcon('Move', 'Set Abs.',
227
233
  tip='Set the value of the actuator to the set absolute value')
@@ -230,7 +236,7 @@ class DAQ_Move_UI(ControlModuleUI):
230
236
  self.move_rel_plus_pb = PushButtonIcon('MoveUp', 'Set Rel. (+)')
231
237
  self.control_ui.layout().addWidget(self.move_rel_plus_pb, 2, 1)
232
238
 
233
- self.rel_value_sb = SpinBox(step=0.1, dec=True)
239
+ self.rel_value_sb = SpinBox(step=0.1, dec=True, siPrefix=config('actuator', 'siprefix'))
234
240
  self.control_ui.layout().addWidget(self.rel_value_sb, 3, 0)
235
241
  self.move_rel_minus_pb = PushButtonIcon('MoveDown', 'Set Rel. (-)')
236
242
  self.control_ui.layout().addWidget(self.move_rel_minus_pb, 3, 1)
@@ -252,6 +258,7 @@ class DAQ_Move_UI(ControlModuleUI):
252
258
 
253
259
  def set_unit_as_suffix(self, unit: str):
254
260
  """Will append the actuator units in the value display"""
261
+ self._unit = unit
255
262
  self.current_value_sb.setOpts(suffix=unit)
256
263
  self.abs_value_sb_bis.setOpts(suffix=unit)
257
264
  self.abs_value_sb.setOpts(suffix=unit)
@@ -328,12 +335,14 @@ class DAQ_Move_UI(ControlModuleUI):
328
335
 
329
336
  def emit_move_abs(self, spinbox):
330
337
  spinbox.editingFinished.emit()
331
- self.command_sig.emit(ThreadCommand('move_abs', DataActuator(data=spinbox.value())))
338
+ self.command_sig.emit(ThreadCommand('move_abs', DataActuator(data=spinbox.value(),
339
+ units=self._unit)))
332
340
 
333
341
  def emit_move_rel(self, sign):
334
- self.command_sig.emit(ThreadCommand('move_rel',
335
- DataActuator(data=self.rel_value_sb.value() * (1 if sign == '+'
336
- else -1))))
342
+ self.command_sig.emit(ThreadCommand(
343
+ 'move_rel',
344
+ DataActuator(data=self.rel_value_sb.value() * (1 if sign == '+' else -1),
345
+ units=self._unit)))
337
346
 
338
347
  def close(self):
339
348
  self.graph_ui.close()
@@ -1,6 +1,7 @@
1
1
  from time import perf_counter
2
2
  from typing import Union, List, Dict, TYPE_CHECKING, Optional
3
3
  from numbers import Number
4
+ from collections.abc import Iterable
4
5
 
5
6
  from easydict import EasyDict as edict
6
7
  import numpy as np
@@ -30,6 +31,30 @@ logger = set_logger(get_module_name(__file__))
30
31
  config = configmod.Config()
31
32
 
32
33
 
34
+ def check_units(dwa: DataActuator, units: str):
35
+ """ Check if dwa units is compatible with the units argument
36
+
37
+ If it is incompatible and has dimensionless units, brute force change the dwa units to units,
38
+ otherwise raise a DataUnitError
39
+
40
+ Parameters
41
+ ----------
42
+ dwa: DataActuator
43
+ units: str
44
+
45
+ Returns
46
+ -------
47
+ DataActuator
48
+ """
49
+ if Unit(dwa.units).is_compatible_with(units):
50
+ return dwa
51
+ elif Unit(dwa.units).dimensionless: # dimensionless
52
+ dwa.force_units(units)
53
+ return dwa
54
+ else:
55
+ raise DataUnitError(f'Units incompatibility between {dwa} and "{units}" units')
56
+
57
+
33
58
  class DataActuatorType(BaseEnum):
34
59
  """Enum for new or old style holding the value of the actuator"""
35
60
  float = 0
@@ -37,6 +62,8 @@ class DataActuatorType(BaseEnum):
37
62
 
38
63
 
39
64
  def comon_parameters(epsilon=config('actuator', 'epsilon_default')):
65
+ if isinstance(epsilon, list):
66
+ epsilon=epsilon[0]
40
67
  return [{'title': 'Units:', 'name': 'units', 'type': 'str', 'value': '', 'readonly': True},
41
68
  {'title': 'Epsilon:', 'name': 'epsilon', 'type': 'float',
42
69
  'value': epsilon,
@@ -80,10 +107,10 @@ class MoveCommand:
80
107
 
81
108
 
82
109
  def comon_parameters_fun(is_multiaxes = False,
83
- axes_names = [],
110
+ axes_names: Union[List[str], Dict[str, int]] = [],
84
111
  axis_names: Union[List, Dict] = [],
85
- master = True,
86
- epsilon = config('actuator', 'epsilon_default')):
112
+ master=True,
113
+ epsilon=config('actuator', 'epsilon_default')):
87
114
  """Function returning the common and mandatory parameters that should be on the actuator plugin level
88
115
 
89
116
  Parameters
@@ -206,8 +233,8 @@ class DAQ_Move_base(QObject):
206
233
  is_multiaxes = False
207
234
  stage_names = []
208
235
  params = []
209
- _controller_units = ''
210
- _epsilon = 1
236
+ _controller_units: Union[str, List[str]] = ''
237
+ _epsilon = 1.0
211
238
  data_actuator_type = DataActuatorType.float
212
239
  data_shape = (1, ) # expected shape of the underlying actuator's value (in general a float so shape = (1, ))
213
240
 
@@ -237,18 +264,19 @@ class DAQ_Move_base(QObject):
237
264
  else:
238
265
  self._title = "myactuator"
239
266
 
240
- if isinstance(self._controller_units, list):
241
- # for backcompatibility
242
- # in case an actuator has multiple units properly handled in future version of pymodaq
243
- self._controller_units = self._controller_units[self.axis_names.index(self.axis_name)]
267
+ self._axis_units: List[str] = []
268
+ self.axis_units = self._controller_units
269
+ self._epsilons: List[float] = [] # self._epsilon if isinstance(self._epsilon, list) else\
270
+ # [self._epsilon for _ in range(len(self.axis_name))]
271
+ self.epsilons = self._epsilon
272
+ self.axis_name = self.axis_name # to trigger some actions on units and epsilons
244
273
 
245
274
  self._current_value = DataActuator(self._title,
246
275
  data=[np.zeros(self.data_shape, dtype=float)],
247
- units=self.controller_units)
276
+ units=self.axis_unit)
248
277
  self._target_value = DataActuator(self._title,
249
278
  data=[np.zeros(self.data_shape, dtype=float)],
250
- units=self.controller_units)
251
- self.controller_units = self._controller_units
279
+ units=self.axis_unit)
252
280
 
253
281
  self.poll_timer = QTimer()
254
282
  self.poll_timer.setInterval(config('actuator', 'polling_interval_ms'))
@@ -258,7 +286,81 @@ class DAQ_Move_base(QObject):
258
286
  self.ini_attributes()
259
287
 
260
288
  @property
261
- def axis_name(self) -> Union[str, object]:
289
+ def axis_unit(self) -> str:
290
+ """ Get/set the unit of the currently chosen axis
291
+
292
+ Will update the printed controller unit in the UI
293
+
294
+ New in 4.4.0
295
+ """
296
+ return self.axis_units[self.axis_value]
297
+
298
+ @axis_unit.setter
299
+ def axis_unit(self, unit: str):
300
+ self.axis_units[self.axis_value] = unit
301
+ self.settings.child('units').setValue(unit)
302
+ self.emit_status(ThreadCommand('units', unit))
303
+
304
+ @property
305
+ def axis_units(self) -> List[str]:
306
+ """ Get/Set the units for each axis of the controller
307
+
308
+ New in 4.4.0
309
+ """
310
+ return self._axis_units
311
+
312
+ @axis_units.setter
313
+ def axis_units(self, units: Union[str, List[str]]):
314
+ if isinstance(units, str):
315
+ units = [units for _ in range(len(self.axis_names))]
316
+ self._axis_units = units
317
+
318
+ @property
319
+ def epsilon(self) -> float:
320
+ """ Get/Set the epsilon of the currently chosen axis
321
+
322
+ New in 4.4.0
323
+ """
324
+ return self.epsilons[self.axis_value]
325
+
326
+ @epsilon.setter
327
+ def epsilon(self, eps: float):
328
+ self.epsilons[self.axis_value] = eps
329
+
330
+ @property
331
+ def epsilons(self) -> List[float]:
332
+ """ Get/Set the epsilon for each axis of the controller
333
+
334
+ New in 4.4.0
335
+ """
336
+ return self._epsilons
337
+
338
+ @epsilons.setter
339
+ def epsilons(self, epsilons: Union[float, List[float]]):
340
+ if not isinstance(epsilons, Iterable) and isinstance(epsilons, float):
341
+ epsilons = [epsilons for _ in range(len(self.axis_names))]
342
+ self._epsilons = epsilons
343
+
344
+ @property
345
+ def controller_units(self):
346
+ """ Get/Set the units of the currently chosen axis of the controller
347
+
348
+ Deprecated with pymodaq >= 4.4.0
349
+
350
+ The property controller_units is deprecated please use the axis_unit property
351
+ """
352
+ deprecation_msg('The property controller_units is deprecated please use the'
353
+ 'axis_unit property.')
354
+ return self.axis_unit
355
+
356
+ @controller_units.setter
357
+ def controller_units(self, units: str = ''):
358
+ deprecation_msg('The property controller_units is deprecated please use the'
359
+ 'axis_unit property.')
360
+ self._axis_units[self.axis_value] = units
361
+
362
+ @property
363
+ def axis_name(self) -> Union[str]:
262
364
  """Get/Set the current axis using its string identifier"""
263
365
  limits = self.settings.child('multiaxes', 'axis').opts['limits']
264
366
  if isinstance(limits, list):
@@ -275,6 +377,9 @@ class DAQ_Move_base(QObject):
275
377
  elif isinstance(limits, dict):
276
378
  self.settings.child('multiaxes', 'axis').setValue(limits[name])
277
379
  QtWidgets.QApplication.processEvents()
380
+ self.axis_unit = self.axis_unit
381
+ self.settings.child('epsilon').setValue(self.epsilon)
382
+
278
383
 
279
384
  @property
280
385
  def axis_names(self) -> Union[List, Dict]:
@@ -292,9 +397,12 @@ class DAQ_Move_base(QObject):
292
397
  QtWidgets.QApplication.processEvents()
293
398
 
294
399
  @property
295
- def axis_value(self) -> object:
400
+ def axis_value(self) -> int:
296
401
  """Get the current value selected from the current axis"""
297
- return self.settings['multiaxes', 'axis']
402
+ if isinstance(self.axis_names, list):
403
+ return self.axis_names.index(self.axis_name)
404
+ else:
405
+ return self.axis_names[self.axis_name]
298
406
 
299
407
  def ini_attributes(self):
300
408
  """ To be subclassed, in order to init specific attributes needed by the real implementation"""
@@ -343,12 +451,12 @@ class DAQ_Move_base(QObject):
343
451
  def current_value(self, value: Union[float, DataActuator]):
344
452
  if not isinstance(value, DataActuator):
345
453
  self._current_value = DataActuator(self._title, data=value,
346
- units=self.controller_units)
454
+ units=self.axis_unit)
347
455
  else:
348
- if (not Unit(self.controller_units).is_compatible_with(
456
+ if (not Unit(self.axis_unit).is_compatible_with(
349
457
  Unit(value.units)) and
350
458
  value.units == ''):
351
- value.force_units(self.controller_units)
459
+ value.force_units(self.axis_unit)
352
460
  self._current_value = value
353
461
 
354
462
  @property
@@ -362,12 +470,12 @@ class DAQ_Move_base(QObject):
362
470
  def target_value(self, value: Union[float, DataActuator]):
363
471
  if not isinstance(value, DataActuator):
364
472
  self._target_value = DataActuator(self._title, data=value,
365
- units=self.controller_units)
473
+ units=self.axis_unit)
366
474
  else:
367
- if (not Unit(self.controller_units).is_compatible_with(
475
+ if (not Unit(self.axis_unit).is_compatible_with(
368
476
  Unit(value.units)) and
369
477
  value.units == ''):
370
- value.force_units(self.controller_units)
478
+ value.force_units(self.axis_unit)
371
479
  self._target_value = value
372
480
 
373
481
  @property
@@ -396,19 +504,6 @@ class DAQ_Move_base(QObject):
396
504
  """
397
505
  return self.settings['multiaxes', 'multi_status'] == 'Master'
398
506
 
399
- @property
400
- def controller_units(self):
401
- """ Get/Set the units of this plugin"""
402
- return self._controller_units
403
-
404
- @controller_units.setter
405
- def controller_units(self, units: str = ''):
406
- self._controller_units = units
407
- try:
408
- self.settings.child('units').setValue(units)
409
- self.emit_status(ThreadCommand('units', units))
410
- except Exception:
411
- pass
412
507
 
413
508
  @property
414
509
  def ispolling(self):
@@ -428,12 +523,12 @@ class DAQ_Move_base(QObject):
428
523
  if position > self.settings.child('bounds', 'max_bound').value():
429
524
  position = DataActuator(self._title,
430
525
  data=self.settings.child('bounds', 'max_bound').value(),
431
- units = self.controller_units)
526
+ units=self.axis_unit)
432
527
  self.emit_status(ThreadCommand('outofbounds', []))
433
528
  elif position < self.settings.child('bounds', 'min_bound').value():
434
529
  position = DataActuator(self._title,
435
530
  data=self.settings.child('bounds', 'min_bound').value(),
436
- units=self.controller_units
531
+ units=self.axis_unit
437
532
  )
438
533
  self.emit_status(ThreadCommand('outofbounds', []))
439
534
  return position
@@ -502,13 +597,13 @@ class DAQ_Move_base(QObject):
502
597
  if position is None:
503
598
  if self.data_actuator_type.name == 'float':
504
599
  position = DataActuator(self._title, data=self.get_actuator_value(),
505
- units = self.controller_units)
600
+ units=self.axis_unit)
506
601
  else:
507
602
  position = self.get_actuator_value()
508
603
  if position.name != self._title: # make sure the emitted DataActuator has the name of the real implementation
509
604
  #of the plugin
510
605
  position = DataActuator(self._title, data=position.value(),
511
- units = self.controller_units)
606
+ units=self.axis_unit)
512
607
  self.move_done_signal.emit(position)
513
608
  self.move_is_done = True
514
609
 
@@ -526,32 +621,59 @@ class DAQ_Move_base(QObject):
526
621
  else:
527
622
  if self.data_actuator_type == DataActuatorType.float:
528
623
  self._current_value = DataActuator(data=self.get_actuator_value(),
529
- units=self.controller_units)
624
+ units=self.axis_unit)
530
625
  else:
531
626
  self._current_value = self.get_actuator_value()
532
- if (not Unit(self.controller_units).is_compatible_with(
627
+ if (not Unit(self.axis_unit).is_compatible_with(
533
628
  Unit(self._current_value.units)) and
534
629
  self._current_value.units == ''):
535
630
  # this happens if the units have not been specified in
536
631
  # the plugin
537
- self._current_value.force_units(self.controller_units)
632
+ self._current_value.force_units(self.axis_unit)
538
633
 
539
634
  logger.debug(f'Current position: {self._current_value}')
540
635
  self.move_done(self._current_value)
541
636
 
542
- def check_target_reached(self):
543
- logger.debug(f"epsilon value is {self.settings['epsilon']}")
544
- logger.debug(f"current_value value is {self._current_value}")
545
- logger.debug(f"target_value value is {self._target_value}")
637
+ def _condition_to_reach_target(self) -> bool:
638
+ """ Implement the condition for exiting the polling mechanism and specifying that the
639
+ target value has been reached
546
640
 
641
+ Returns
642
+ -------
643
+ bool: if True, PyMoDAQ considers the target value has been reached at epsilon
644
+
645
+ See Also
646
+ --------
647
+ user_condition_to_reach_target
648
+ """
547
649
  try:
548
- epsilon_calculated = (self._current_value - self._target_value).abs()
650
+ epsilon_calculated = (
651
+ self._current_value - self._target_value).abs().value(self.axis_unit)
549
652
  except DataUnitError as e:
550
653
  epsilon_calculated = abs(self._current_value.value() - self._target_value.value())
551
- logger.warning(f'Unit issue when calculating epsilon, units are not the same between'
654
+ logger.warning(f'Unit issue when calculating epsilon, units are not compatible between'
552
655
  f'target and current values')
553
656
 
554
- if not epsilon_calculated < self.settings['epsilon']:
657
+ return (epsilon_calculated < self.epsilon) and self.user_condition_to_reach_target()
658
+
659
+ def user_condition_to_reach_target(self) -> bool:
660
+ """ Implement a user defined condition for exiting the polling mechanism and specifying
661
+ that the target value has been reached (on top of the existing epsilon mechanism)
662
+
663
+ Should be reimplemented in plugins to implement other conditions
664
+
665
+ Returns
666
+ -------
667
+ bool: if True, PyMoDAQ considers the target value has been reached
668
+ """
669
+ return True
670
+
671
+ def check_target_reached(self):
672
+ logger.debug(f"epsilon value is {self.epsilon}")
673
+ logger.debug(f"current_value value is {self._current_value}")
674
+ logger.debug(f"target_value value is {self._target_value}")
675
+
676
+ if not self._condition_to_reach_target():
555
677
 
556
678
  logger.debug(f'Check move_is_done: {self.move_is_done}')
557
679
  if self.move_is_done:
@@ -647,6 +769,11 @@ class DAQ_Move_base(QObject):
647
769
  self.commit_common_settings(param)
648
770
  self.commit_settings(param)
649
771
 
772
+ if param.name() == 'axis':
773
+ self.axis_name = param.value()
774
+ elif param.name() == 'epsilon':
775
+ self.epsilon = param.value()
776
+
650
777
 
651
778
  class DAQ_Move_TCP_server(DAQ_Move_base, TCPServer):
652
779
  """
@@ -22,6 +22,8 @@ class ParameterEx(ParameterManager):
22
22
  {'title': 'Numbers:', 'name': 'numbers', 'type': 'group', 'children': [
23
23
  {'title': 'Standard float', 'name': 'afloat', 'type': 'float', 'value': 20., 'min': 1.,
24
24
  'tip': 'displays this text as a tooltip'},
25
+ {'title': 'Standard float with Si prefix', 'name': 'afloatprefix', 'type': 'float', 'value': 20.,
26
+ 'tip': 'displays this text as a tooltip', 'siPrefix': True, 'suffix': 'V'},
25
27
  {'title': 'Linear Slide float', 'name': 'linearslidefloat', 'type': 'slide', 'value': 50, 'default': 50,
26
28
  'min': 0,
27
29
  'max': 123, 'subtype': 'linear'},
pymodaq/resources/VERSION CHANGED
@@ -1,2 +1,2 @@
1
- version = '4.3.7'
1
+ version = '4.4.0'
2
2
 
pymodaq/utils/config.py CHANGED
@@ -20,7 +20,7 @@ except:
20
20
  USER = 'unknown_user'
21
21
 
22
22
  CONFIG_BASE_PATH = Path(environ['PROGRAMDATA']) if sys.platform == 'win32' else \
23
- Path('/Library/Application Support') if sys.platform == 'darwin' else Path('/etc')
23
+ Path('Library/Application Support') if sys.platform == 'darwin' else Path('/etc')
24
24
 
25
25
 
26
26
  KeyType = TypeVar('KeyType')
pymodaq/utils/data.py CHANGED
@@ -2413,21 +2413,48 @@ class DataActuator(DataRaw):
2413
2413
  else:
2414
2414
  return f'<{self.__class__.__name__} ({self.shape} {self.units})>'
2415
2415
 
2416
- def value(self) -> float:
2416
+ def value(self, units: str = None) -> float:
2417
2417
  """Returns the underlying float value (of the first elt in the data list) if this data
2418
- holds only a float otherwise returns a mean of the underlying data"""
2418
+ holds only a float otherwise returns a mean of the underlying data
2419
+
2420
+ Parameters
2421
+ ----------
2422
+
2423
+ units: str
2424
+ if unit is compatible with self.units, convert the data to these new units before
2425
+ getting the value
2426
+
2427
+
2428
+ """
2419
2429
  if self.length == 1 and self.size == 1:
2420
- return float(self.data[0][0])
2430
+ if units is not None:
2431
+ data = Q_(float(self.data[0][0]), self.units)
2432
+ return data.m_as(units)
2433
+ else:
2434
+ return float(self.data[0][0])
2421
2435
  else:
2422
- return float(np.mean(self.data))
2436
+ if units is not None:
2437
+ data = Q_(float(np.mean(self.data[0])), self.units)
2438
+ return data.m_as(units)
2439
+ else:
2440
+ return float(np.mean(self.data[0]))
2423
2441
 
2424
- def values(self) -> List[float]:
2442
+ def values(self, units: str = None) -> List[float]:
2425
2443
  """Returns the underlying float value (for each data array in the data list) if this data
2426
2444
  holds only a float otherwise returns a mean of the underlying data"""
2427
2445
  if self.length == 1 and self.size == 1:
2428
- return [float(data_array[0]) for data_array in self.data]
2446
+ if units is not None:
2447
+ return [float(Q_(data_array[0], self.units).m_as(units))
2448
+ for data_array in self.data]
2449
+ else:
2450
+ return [float(data_array[0])
2451
+ for data_array in self.data]
2429
2452
  else:
2430
- return [float(np.mean(data_array)) for data_array in self.data]
2453
+ if units is not None:
2454
+ return [float(Q_(np.mean(data_array), self.units).m_as(units))
2455
+ for data_array in self.data]
2456
+ else:
2457
+ return [float(np.mean(data_array)) for data_array in self.data]
2431
2458
 
2432
2459
 
2433
2460
  class DataFromPlugins(DataRaw):
@@ -12,7 +12,7 @@ def load_dashboard_with_preset(preset_name: str, extension_name: str):
12
12
  area = DockArea()
13
13
  win.setCentralWidget(area)
14
14
  win.resize(1000, 500)
15
- win.setWindowTitle('PyMoDAQ Dashboard')
15
+ win.setWindowTitle('extension_name')
16
16
  win.show()
17
17
 
18
18
  # win.setVisible(False)
@@ -420,6 +420,7 @@ class Serializer:
420
420
  * serialize the string type: 'DataWithAxes'
421
421
  * serialize the timestamp: float
422
422
  * serialize the name
423
+ * serialize the units
423
424
  * serialize the source enum as a string
424
425
  * serialize the dim enum as a string
425
426
  * serialize the distribution enum as a string
@@ -439,6 +440,7 @@ class Serializer:
439
440
  bytes_string += self.object_type_serialization(dwa)
440
441
  bytes_string += self.scalar_serialization(dwa.timestamp)
441
442
  bytes_string += self.string_serialization(dwa.name)
443
+ bytes_string += self.string_serialization(dwa.units)
442
444
  bytes_string += self.string_serialization(dwa.source.name)
443
445
  bytes_string += self.string_serialization(dwa.dim.name)
444
446
  bytes_string += self.string_serialization(dwa.distribution.name)
@@ -731,9 +733,11 @@ class DeSerializer:
731
733
  """
732
734
  class_name = self.string_deserialization()
733
735
  if class_name not in DwaType.names():
734
- raise TypeError(f'Attempting to deserialize a DataWithAxes flavor but got the bytes for a {class_name}')
736
+ raise TypeError(f'Attempting to deserialize a DataWithAxes '
737
+ f'flavor but got the bytes for a {class_name}')
735
738
  timestamp = self.scalar_deserialization()
736
739
  dwa = getattr(data_mod, class_name)(self.string_deserialization(),
740
+ units=self.string_deserialization(),
737
741
  source=self.string_deserialization(),
738
742
  dim=self.string_deserialization(),
739
743
  distribution=self.string_deserialization(),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pymodaq
3
- Version: 4.3.7
3
+ Version: 4.4.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
@@ -77,7 +77,7 @@ PyMoDAQ
77
77
  :target: https://pymodaq.readthedocs.io/en/stable/?badge=latest
78
78
  :alt: Documentation Status
79
79
 
80
- .. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/pymodaq-dev/graph/badge.svg?token=IQNJRCQDM2
80
+ .. image:: https://codecov.io/gh/PyMoDAQ/PyMoDAQ/branch/4.4.x/graph/badge.svg?token=IQNJRCQDM2
81
81
  :target: https://codecov.io/gh/PyMoDAQ/PyMoDAQ
82
82
 
83
83
  ====== ========== ======= ======
@@ -93,25 +93,25 @@ Python Qt Backend OS Passed
93
93
  ====== ========== ======= ======
94
94
 
95
95
 
96
- .. |38Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5.yml/badge.svg?branch=4.3.x_dev
96
+ .. |38Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5.yml/badge.svg?branch=4.4.x
97
97
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5.yml
98
98
 
99
- .. |39Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt5.yml/badge.svg?branch=4.3.x_dev
99
+ .. |39Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt5.yml/badge.svg?branch=4.4.x
100
100
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt5.yml
101
101
 
102
- .. |310Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp310pyqt5.yml/badge.svg?branch=4.3.x_dev
102
+ .. |310Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp310pyqt5.yml/badge.svg?branch=4.4.x
103
103
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp310pyqt5.yml
104
104
 
105
- .. |311Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp311pyqt5.yml/badge.svg?branch=4.3.x_dev
105
+ .. |311Qt5| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp311pyqt5.yml/badge.svg?branch=4.4.x
106
106
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp311pyqt5.yml
107
107
 
108
- .. |38Qt5win| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5_win.yml/badge.svg?branch=4.3.x_dev
108
+ .. |38Qt5win| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5_win.yml/badge.svg?branch=4.4.x
109
109
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyqt5_win.yml
110
110
 
111
- .. |38pyside| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyside2.yml/badge.svg?branch=4.3.x_dev
111
+ .. |38pyside| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyside2.yml/badge.svg?branch=4.4.x
112
112
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp38pyside2.yml
113
113
 
114
- .. |39Qt6| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt6.yml/badge.svg?branch=4.3.x_dev
114
+ .. |39Qt6| image:: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt6.yml/badge.svg?branch=4.4.x
115
115
  :target: https://github.com/PyMoDAQ/PyMoDAQ/actions/workflows/Testp39pyqt6.yml
116
116
 
117
117
 
@@ -1,14 +1,14 @@
1
- pymodaq/__init__.py,sha256=boHjxPnKEAQUmOHKbnxlI8DHP1eM5IKSrrnd1EqkvHg,4295
1
+ pymodaq/__init__.py,sha256=e4pcKgxrhCVtCuq3t-gcfkwCWtV72K6jhPa-0zAtRfs,4382
2
2
  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=dq29GbNf-8BpquR3z8ZuqvKaP-0p14LLrAAVg75pIis,36590
7
- pymodaq/control_modules/daq_move_ui.py,sha256=euA8_utFfiscd9ahZxRTqqTiCSrNB4CKCLqgdbUKSlI,15307
6
+ pymodaq/control_modules/daq_move.py,sha256=L4lW_VfdBKmrlpLkKwbNitDeLHbFWk6Lbq4T5k-6xiM,37323
7
+ pymodaq/control_modules/daq_move_ui.py,sha256=IbqNAErwXGjKUbYEptvZUz3J8MapNBFIbQnUf9nQrMw,15753
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=g_Q8eOPetwFuVbQfWTk4gX5XMsJoTvf_a7vdOrR5DoU,35731
11
+ pymodaq/control_modules/move_utility_classes.py,sha256=LUO7kGb-1iQOP_6Wvp46xnx5Z9wNxUhRzOfz9cfXH-s,39664
12
12
  pymodaq/control_modules/utils.py,sha256=5YdSwq_lFJm7IalYWe_Hn1U4LUoUmo1gedvV9UguU0Y,20016
13
13
  pymodaq/control_modules/viewer_utility_classes.py,sha256=OHxwue1t3z2AXyeqNjnwPT2pMc8yXhnqyiWc9IdCI2c,26841
14
14
  pymodaq/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,7 +16,7 @@ pymodaq/examples/custom_app.py,sha256=2wQR0hlPWjZrWK0abNF6ASv8iQyJqRn2CKnBa_nAgN
16
16
  pymodaq/examples/custom_viewer.py,sha256=nUj5n6l7DSyh-qaXboNBfXKa9mAiXrHSiOcuFL1ayRE,4814
17
17
  pymodaq/examples/function_plotter.py,sha256=T-VT0Rd3jHP9GcR2h6Nao6lwZE06P8zWUbOlz8b8Rxk,6104
18
18
  pymodaq/examples/nonlinearscanner.py,sha256=x0R2_FP0YnuOCCAmYRiAiZ1jfUdRxu5RqIYLyGQMZ0U,3790
19
- pymodaq/examples/parameter_ex.py,sha256=AVzs9aeZFe3U5SkFvC9VZuRZPoB-UMCxYYEnFH6if40,8357
19
+ pymodaq/examples/parameter_ex.py,sha256=NmFUvUByNtm3j4leN_MkufQsKlNU8Rx5lmpsVG58IIM,8556
20
20
  pymodaq/examples/preset_MockCamera.xml,sha256=quQlMsX6YSoqqc9_9Y-9zu3TDM6Xvnuc2JSWwg9f948,15774
21
21
  pymodaq/examples/tcp_client.py,sha256=FSdPlb3R_rxxNIqPqHVU8PxJzNZeFk_93l4TqsB5SnA,2584
22
22
  pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases,sha256=t0eKH9Uq_AMk4wQ-6Pm5mKUjGcCvfT9GtvMsvDhkCUk,47
@@ -58,7 +58,7 @@ pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py,sha256=1u7hWDaiwsZ
58
58
  pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui,sha256=PyzbCWPMkh5oIYYteZczXyWMeHKW9EJmM1QlzXhnyTk,7037
59
59
  pymodaq/post_treatment/daq_measurement/daq_measurement_main.py,sha256=CAKwcWMOD86aXB8mbdxOK7e8nZRos5d59FzDtqK1QoY,17093
60
60
  pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat,sha256=e1tu2A67MS9fk3jhriF6saQgRxWIucIvNW92iWXFP6E,164
61
- pymodaq/resources/VERSION,sha256=fP37ke9RfdHBjYyyxWc1kXW6-Gg-gr80uqzn0qTdrE8,19
61
+ pymodaq/resources/VERSION,sha256=x4JbXmXiNnoebdA-11hkfcpy5BKE5mBMlysMVDo9SRw,19
62
62
  pymodaq/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
63
  pymodaq/resources/config_template.toml,sha256=d3pofgIK5FxaRMELAI1qEsRcMD3GlYd87zZjDj9G9m0,3210
64
64
  pymodaq/resources/preset_default.xml,sha256=Dt8iWLwPPOPtcG00JCVP-mh-G7KC6B0YN8hd8RQdnNI,27256
@@ -308,10 +308,10 @@ pymodaq/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
308
308
  pymodaq/utils/array_manipulation.py,sha256=uMdiVVR2mU7j6Z4DKL5VGhUPqiWvFX2YK7RLMGdLyC8,10415
309
309
  pymodaq/utils/calibration_camera.py,sha256=d3aAu0izXOdeLs-vyBaFfBuvyDGT3O-SreTuyd0Kvs4,8921
310
310
  pymodaq/utils/chrono_timer.py,sha256=rwX8apS8B-IKhA0Cp2H9tLz0BRN7G3Pg5ptozvd3MKM,7244
311
- pymodaq/utils/config.py,sha256=u-Q4af-tgXG7h8YitMGSPkigDxGoOYSEMHERQCF9mAM,16203
311
+ pymodaq/utils/config.py,sha256=0QqoBJC4ECuIeh1UsvUQqhxkKl7Vfgi4iERp-6qNWAc,16202
312
312
  pymodaq/utils/conftests.py,sha256=3Ak8WEpa3EhAp73Yb1LLq8YFONhPqiL7gG9eSDIoTNc,58
313
313
  pymodaq/utils/daq_utils.py,sha256=0jTrbT0aaZr3KaTgeDicmK9FbVnu3iaWBmNHnNJpr3A,28050
314
- pymodaq/utils/data.py,sha256=QPPFs57Eu1cEvF_QYJamjrPmickBVgsggg_bSkbd0P0,110621
314
+ pymodaq/utils/data.py,sha256=V4F_H-sUPmCwrcNDuF7JxzcJtnOlQX7fChSoyXdd6ng,111572
315
315
  pymodaq/utils/enums.py,sha256=wpRipioUJkKcEfoaY2NrDQ2WhGxZTZiZoJty5f2Ljpc,2236
316
316
  pymodaq/utils/exceptions.py,sha256=wLO6VlofzfwWkOOWMN2B-3NEWMfpgygyeEdakIx_rAs,668
317
317
  pymodaq/utils/factory.py,sha256=QLqAPFnTZ93eUpmAAIr7kESDk2enD57RNSuFUsjxE4E,2311
@@ -333,7 +333,7 @@ pymodaq/utils/gui_utils/dock.py,sha256=6AcxC9HqM45bt20vJgP6QgGAhkW8mynuOuWhL7gep
333
333
  pymodaq/utils/gui_utils/file_io.py,sha256=mJI_30p986rLZk44FsPjesMazE1tq7HEFLenW-dnmOI,3367
334
334
  pymodaq/utils/gui_utils/layout.py,sha256=6oczLLGwwEN4EQ8yUDnz0-4Ue2wlyCRklKsVD1GNcz8,1099
335
335
  pymodaq/utils/gui_utils/list_picker.py,sha256=ddYnRTlRlgwdJSy0Q98IzYWHzIf2GS6ABl8XSS9kVXM,1190
336
- pymodaq/utils/gui_utils/loader_utils.py,sha256=7sfAvktORgF1DAcYCHsWrl57koaO8v4D4UCqXgUPxbU,1301
336
+ pymodaq/utils/gui_utils/loader_utils.py,sha256=Gg0d31fjkqDq3l1WuRMbLzKiKPOIYdbUrcfGOgoXRk0,1298
337
337
  pymodaq/utils/gui_utils/utils.py,sha256=aPxFCnG4skDp-0UuhAudq6BPZnEXoAf6FOEJyrhUjx0,6089
338
338
  pymodaq/utils/gui_utils/widgets/__init__.py,sha256=LThGzmbFKbp2FtTTF_L7pHjyBzfB7F_bhMF4rPTwrUY,195
339
339
  pymodaq/utils/gui_utils/widgets/label.py,sha256=C2MU8i_Yy_oVRW7yal_ghB1Y5Bj_a9o8IFZWW3br-KM,600
@@ -436,10 +436,10 @@ pymodaq/utils/svg/svg_view.py,sha256=bmXpDqnw9S-Bp3F8Hi_oeYB5Y9gebiCNsQWVJzCq-PA
436
436
  pymodaq/utils/svg/svg_viewer2D.py,sha256=LTJ3Ulb5zWXdRPr7vqcWumbpq7ZctzrYUMtD5QV3x60,1523
437
437
  pymodaq/utils/tcp_ip/__init__.py,sha256=1e_EK0AgvdoLAD_CSGGEaITZdy6OWCO7ih9IAIp7HT4,81
438
438
  pymodaq/utils/tcp_ip/mysocket.py,sha256=StAWj8dzHeMnbLj68Sel81uWFy-YkKVNRnVf7gXrESI,3452
439
- pymodaq/utils/tcp_ip/serializer.py,sha256=oTQ24JNln_vRX4YADitTYiJplwFIdsta1ZDp6TOGMCA,27562
439
+ pymodaq/utils/tcp_ip/serializer.py,sha256=htVQCE4saRBMeIcseEyxTt5G58A341m6OGkaJUA34Wk,27766
440
440
  pymodaq/utils/tcp_ip/tcp_server_client.py,sha256=xIMTNgVW_rKK0yTi4FDNFLf85-Akb27Jz2LdrvOrP68,30660
441
- pymodaq-4.3.7.dist-info/METADATA,sha256=jrLMgm6DJyOZdZaK-32Cxe4h_-ikdS4vDVKW6e8ozRc,7657
442
- pymodaq-4.3.7.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
443
- pymodaq-4.3.7.dist-info/entry_points.txt,sha256=RAzdYNjvUT28I2eiCKki_g2NzXq0woWxhev6lwzwRv8,348
444
- pymodaq-4.3.7.dist-info/licenses/LICENSE,sha256=VKOejxexXAe3XwfhAhcFGqeXQ12irxVHdeAojZwFEI8,1108
445
- pymodaq-4.3.7.dist-info/RECORD,,
441
+ pymodaq-4.4.0.dist-info/METADATA,sha256=PGjrsAiJMph8il3hZKb9aJDvUShYG5CUrEYgTPsY8is,7623
442
+ pymodaq-4.4.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
443
+ pymodaq-4.4.0.dist-info/entry_points.txt,sha256=RAzdYNjvUT28I2eiCKki_g2NzXq0woWxhev6lwzwRv8,348
444
+ pymodaq-4.4.0.dist-info/licenses/LICENSE,sha256=VKOejxexXAe3XwfhAhcFGqeXQ12irxVHdeAojZwFEI8,1108
445
+ pymodaq-4.4.0.dist-info/RECORD,,