pymodaq 5.0.0__py3-none-any.whl → 5.0.1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (60) hide show
  1. pymodaq/__init__.py +55 -89
  2. pymodaq/control_modules/daq_move.py +123 -52
  3. pymodaq/control_modules/daq_move_ui.py +42 -11
  4. pymodaq/control_modules/daq_viewer.py +30 -13
  5. pymodaq/control_modules/move_utility_classes.py +345 -78
  6. pymodaq/control_modules/utils.py +26 -9
  7. pymodaq/control_modules/viewer_utility_classes.py +51 -14
  8. pymodaq/daq_utils/daq_utils.py +6 -0
  9. pymodaq/dashboard.py +532 -263
  10. pymodaq/examples/qt_less_standalone_module.py +128 -0
  11. pymodaq/extensions/bayesian/bayesian_optimisation.py +30 -21
  12. pymodaq/extensions/bayesian/utils.py +6 -3
  13. pymodaq/extensions/daq_logger/__init__.py +1 -0
  14. pymodaq/extensions/daq_logger/daq_logger.py +4 -5
  15. pymodaq/extensions/daq_scan.py +1 -3
  16. pymodaq/extensions/daq_scan_ui.py +7 -9
  17. pymodaq/extensions/pid/__init__.py +0 -1
  18. pymodaq/extensions/pid/actuator_controller.py +13 -0
  19. pymodaq/extensions/pid/daq_move_PID.py +25 -46
  20. pymodaq/extensions/pid/pid_controller.py +48 -40
  21. pymodaq/extensions/pid/utils.py +3 -2
  22. pymodaq/extensions/utils.py +41 -7
  23. pymodaq/resources/setup_plugin.py +1 -0
  24. pymodaq/updater.py +107 -0
  25. pymodaq/utils/chrono_timer.py +6 -7
  26. pymodaq/utils/daq_utils.py +6 -3
  27. pymodaq/utils/data.py +11 -16
  28. pymodaq/utils/enums.py +6 -0
  29. pymodaq/utils/gui_utils/loader_utils.py +27 -2
  30. pymodaq/utils/gui_utils/utils.py +9 -12
  31. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  32. pymodaq/utils/leco/daq_move_LECODirector.py +21 -14
  33. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +13 -8
  34. pymodaq/utils/leco/pymodaq_listener.py +8 -7
  35. pymodaq/utils/leco/utils.py +33 -7
  36. pymodaq/utils/managers/modules_manager.py +20 -10
  37. pymodaq/utils/managers/overshoot_manager.py +45 -1
  38. pymodaq/utils/managers/preset_manager.py +22 -46
  39. pymodaq/utils/managers/preset_manager_utils.py +17 -13
  40. pymodaq/utils/managers/remote_manager.py +1 -1
  41. pymodaq/utils/messenger.py +6 -0
  42. pymodaq/utils/parameter/__init__.py +5 -1
  43. pymodaq/utils/tcp_ip/mysocket.py +4 -110
  44. pymodaq/utils/tcp_ip/serializer.py +4 -769
  45. pymodaq/utils/tcp_ip/tcp_server_client.py +5 -5
  46. pymodaq-5.0.1.dist-info/METADATA +242 -0
  47. {pymodaq-5.0.0.dist-info → pymodaq-5.0.1.dist-info}/RECORD +51 -52
  48. {pymodaq-5.0.0.dist-info → pymodaq-5.0.1.dist-info}/WHEEL +1 -1
  49. {pymodaq-5.0.0.dist-info → pymodaq-5.0.1.dist-info}/entry_points.txt +1 -0
  50. pymodaq/examples/custom_app.py +0 -255
  51. pymodaq/examples/custom_viewer.py +0 -112
  52. pymodaq/examples/parameter_ex.py +0 -158
  53. pymodaq/examples/preset_MockCamera.xml +0 -1
  54. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py +0 -142
  55. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui +0 -232
  56. pymodaq/post_treatment/daq_measurement/daq_measurement_main.py +0 -391
  57. pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -2
  58. pymodaq-5.0.0.dist-info/METADATA +0 -166
  59. /pymodaq/{post_treatment/daq_measurement → daq_utils}/__init__.py +0 -0
  60. {pymodaq-5.0.0.dist-info → pymodaq-5.0.1.dist-info}/licenses/LICENSE +0 -0
pymodaq/__init__.py CHANGED
@@ -6,23 +6,11 @@ from pathlib import Path
6
6
 
7
7
  import warnings
8
8
 
9
+ import pymodaq_utils # to init stuff related to pymodaq_utils # necessary, leave it there
10
+ import pymodaq_data # to init stuff related to pymodaq_data # necessary, leave it there
11
+ import pymodaq_gui # to init stuff related to pymodaq_gui # necessary, leave it there
9
12
 
10
- def check_qt_presence():
11
- try:
12
- from qtpy import QtWidgets
13
- except ImportError as e:
14
- msg = f"\n\n" \
15
- f"****************************************************************************************\n" \
16
- f"No Qt backend could be found in your system, please install either pyqt5/6 or pyside2/6.\n\n" \
17
- f"pyqt5 is still preferred, while pyqt6 should mostly work.\n\n" \
18
- f"do:\n" \
19
- f"pip install pyqt5\n for instance\n"\
20
- f"****************************************************************************************\n"
21
- warnings.warn(msg, FutureWarning, 2)
22
- sys.exit()
23
-
24
-
25
- check_qt_presence()
13
+ from pymodaq_data import Q_, Unit, ureg # necessary, leave it there
26
14
 
27
15
 
28
16
  try:
@@ -31,87 +19,65 @@ try:
31
19
 
32
20
  from pymodaq_utils.logger import set_logger
33
21
  from pymodaq_utils.utils import get_version
34
- __version__ = get_version()
22
+ __version__ = get_version('pymodaq')
35
23
  try:
36
24
  logger = set_logger('pymodaq', add_handler=True, base_logger=True)
37
- except Exception:
38
- print("Couldn't create the local folder to store logs , presets...")
39
25
 
40
- logger.info('')
41
- logger.info('')
42
- logger.info('************************')
43
- logger.info('Starting PyMoDAQ modules')
44
- logger.info('************************')
45
- logger.info('')
46
- logger.info('')
47
- logger.info('************************')
48
- logger.info('Initializing the pint unit register')
49
- logger.info('************************')
50
- ureg = UnitRegistry()
51
- ureg.default_format = '~'
52
- Q_ = ureg.Quantity
53
- Unit = ureg.Unit
54
- logger.info('')
55
- logger.info('')
56
-
57
- # in a try statement for compilation on readthedocs server but if this fail, you cannot use the code
58
- from pymodaq_gui.plotting import data_viewers # imported here as to avoid circular imports later on
59
- from pymodaq_gui.qt_utils import setLocale, set_qt_backend
60
- from pymodaq.utils.daq_utils import copy_preset, get_instrument_plugins
61
-
62
- from pymodaq_utils.config import Config
63
- from pymodaq.utils.scanner.utils import register_scanners
64
- from pymodaq_data.plotting.plotter.plotter import register_plotter, PlotterFactory
65
-
66
- # issue on windows when using .NET code within multithreads, this below allows it but requires
67
- # the pywin32 (pythoncom) package
68
- if importlib.util.find_spec('clr') is not None:
26
+ from pymodaq.utils.daq_utils import copy_preset, get_instrument_plugins
27
+
28
+ from pymodaq_utils.config import Config
29
+ from pymodaq.utils.scanner.utils import register_scanners
30
+ from pymodaq_data.plotting.plotter.plotter import register_plotter, PlotterFactory
31
+
32
+ # issue on windows when using .NET code within multithreads, this below allows it but requires
33
+ # the pywin32 (pythoncom) package
34
+ if importlib.util.find_spec('clr') is not None:
35
+ try:
36
+ import pythoncom
37
+ pythoncom.CoInitialize()
38
+ except ModuleNotFoundError as e:
39
+ infos = "You have installed plugins requiring the pywin32 package to work correctly," \
40
+ " please type in *pip install pywin32* and restart PyMoDAQ"
41
+ print(infos)
42
+ logger.warning(infos)
43
+
44
+ config = Config() # to ckeck for config file existence, otherwise create one
45
+ copy_preset()
46
+
47
+ from pymodaq_utils.config import Config
48
+ from pymodaq.utils.scanner.utils import register_scanners
49
+
69
50
  try:
70
- import pythoncom
71
- pythoncom.CoInitialize()
51
+ # Need the config to exists before importing
52
+ from pymodaq_utils.environment import EnvironmentBackupManager
53
+
54
+ if config['backup']['keep_backup']:
55
+ ebm = EnvironmentBackupManager()
56
+ ebm.save_backup()
72
57
  except ModuleNotFoundError as e:
73
- infos = "You have installed plugins requiring the pywin32 package to work correctly," \
74
- " please type in *pip install pywin32* and restart PyMoDAQ"
58
+ infos = "Your pymodaq_utils version is outdated and doesn't allow for automatic backup of pip packages." \
59
+ " You should update it."
75
60
  print(infos)
76
61
  logger.warning(infos)
77
62
 
78
- config = Config() # to ckeck for config file existence, otherwise create one
79
- copy_preset()
80
-
81
- logger.info('************************')
82
- logger.info(f"Setting Qt backend to: {config['qtbackend']['backend']} ...")
83
- set_qt_backend()
84
- logger.info('************************')
85
- logger.info('')
86
- logger.info('')
87
- logger.info('************************')
88
- logger.info(f"Setting Locale to {config['style']['language']} / {config['style']['country']}")
89
- logger.info('************************')
90
- setLocale()
91
- logger.info('')
92
- logger.info('')
93
-
94
- logger.info('*************************************************************************')
95
- logger.info(f"Getting the list of instrument plugins...")
96
- logger.info('')
97
- get_instrument_plugins()
98
- logger.info('*************************************************************************')
99
-
100
- logger.info('')
101
- logger.info('')
102
- logger.info('************************')
103
- logger.info(f"Registering Scanners...")
104
- register_scanners()
105
- logger.info(f"Done")
106
- logger.info('************************')
107
-
108
- logger.info('')
109
- logger.info('')
110
- logger.info('************************')
111
- logger.info(f"Registering plotters...")
112
- register_plotter()
113
- logger.info(f"Done")
114
- logger.info('************************')
63
+ logger.info('*************************************************************************')
64
+ logger.info(f"Getting the list of instrument plugins...")
65
+ logger.info('')
66
+ get_instrument_plugins()
67
+ logger.info('*************************************************************************')
68
+
69
+ logger.info('')
70
+ logger.info('')
71
+ logger.info('************************')
72
+ logger.info(f"Registering Scanners...")
73
+ register_scanners()
74
+ logger.info(f"Done")
75
+ logger.info('************************')
76
+
77
+ except Exception:
78
+ print("Couldn't create the local folder to store logs , presets...")
79
+
80
+
115
81
 
116
82
  except Exception as e:
117
83
  try:
@@ -32,14 +32,22 @@ from pymodaq_data.h5modules.backends import Node
32
32
 
33
33
  from pymodaq_gui.parameter import ioxml, Parameter
34
34
  from pymodaq_gui.parameter import utils as putils
35
+ from pymodaq_gui.utils.utils import mkQApp
35
36
 
36
37
  from pymodaq.utils.h5modules import module_saving
37
38
  from pymodaq.control_modules.utils import ParameterControlModule
38
- from pymodaq.control_modules.daq_move_ui import DAQ_Move_UI
39
- from pymodaq.control_modules.move_utility_classes import MoveCommand, DAQ_Move_base
39
+ from pymodaq.control_modules.daq_move_ui import DAQ_Move_UI, ThreadCommand
40
+ from pymodaq.control_modules.move_utility_classes import (MoveCommand, DAQ_Move_base,
41
+ DataActuatorType, check_units,
42
+ DataUnitError)
43
+
44
+
40
45
  from pymodaq.control_modules.move_utility_classes import params as daq_move_params
41
46
  from pymodaq.utils.leco.pymodaq_listener import MoveActorListener, LECOMoveCommands
47
+
42
48
  from pymodaq.utils.daq_utils import get_plugins
49
+ from pymodaq import Q_, Unit
50
+
43
51
 
44
52
 
45
53
  local_path = config_mod.get_set_local_dir()
@@ -116,15 +124,15 @@ class DAQ_Move(ParameterControlModule):
116
124
  self.splash_sc = get_splash_sc()
117
125
  self._title = title
118
126
  if len(ACTUATOR_TYPES) > 0: # will be 0 if no valid plugins are installed
119
- self.actuator = ACTUATOR_TYPES[0]
127
+ self.actuator = kwargs.get('actuator', ACTUATOR_TYPES[0])
120
128
 
121
129
  self.module_and_data_saver = module_saving.ActuatorSaver(self)
122
130
 
123
131
  self._move_done_bool = True
124
132
 
125
- self._current_value = DataActuator(title)
126
- self._target_value: DataActuator(title)
127
- self._relative_value: DataActuator(title)
133
+ self._current_value = DataActuator(title, units=self.units)
134
+ self._target_value = DataActuator(title, units=self.units)
135
+ self._relative_value = DataActuator(title, units=self.units)
128
136
 
129
137
  self._refresh_timer = QTimer()
130
138
  self._refresh_timer.timeout.connect(self.get_actuator_value)
@@ -162,9 +170,15 @@ class DAQ_Move(ParameterControlModule):
162
170
  elif cmd.command == 'stop':
163
171
  self.stop_motion()
164
172
  elif cmd.command == 'move_abs':
165
- self.move_abs(cmd.attribute)
173
+ data_act: DataActuator = cmd.attribute
174
+ if not Unit(data_act.units).is_compatible_with(self.units) and data_act.units != '':
175
+ data_act.force_units(self.units)
176
+ self.move_abs(data_act)
166
177
  elif cmd.command == 'move_rel':
167
- self.move_rel(cmd.attribute)
178
+ data_act: DataActuator = cmd.attribute
179
+ if not Unit(data_act.units).is_compatible_with(self.units) and data_act.units != '':
180
+ data_act.force_units(self.units)
181
+ self.move_rel(data_act)
168
182
  elif cmd.command == 'show_log':
169
183
  self.show_log()
170
184
  elif cmd.command == 'show_config':
@@ -175,6 +189,20 @@ class DAQ_Move(ParameterControlModule):
175
189
  elif cmd.command == 'rel_value':
176
190
  self._relative_value = cmd.attribute
177
191
 
192
+ @property
193
+ def master(self) -> bool:
194
+ """ Get/Set programmatically the Master/Slave status of an actuator"""
195
+ if self.initialized_state:
196
+ return self.settings['move_settings', 'multiaxes', 'multi_status'] == 'Master'
197
+ else:
198
+ return True
199
+
200
+ @master.setter
201
+ def master(self, is_master: bool):
202
+ if self.initialized_state:
203
+ self.settings.child('move_settings', 'multiaxes', 'multi_status').setValue(
204
+ 'Master' if is_master else 'Slave')
205
+
178
206
  def append_data(self, dte: Optional[DataToExport] = None, where: Union[Node, str, None] = None):
179
207
  """Appends current DataToExport to an ActuatorEnlargeableSaver
180
208
 
@@ -258,7 +286,7 @@ class DAQ_Move(ParameterControlModule):
258
286
  """
259
287
  try:
260
288
  if isinstance(value, Number):
261
- value = DataActuator(self.title, data=[np.array([value])])
289
+ value = DataActuator(self.title, data=[np.array([value])], units=self.units)
262
290
  self._send_to_tcpip = send_to_tcpip
263
291
  if value != self._current_value:
264
292
  if self.ui is not None:
@@ -307,7 +335,7 @@ class DAQ_Move(ParameterControlModule):
307
335
 
308
336
  try:
309
337
  if isinstance(rel_value, Number):
310
- rel_value = DataActuator(self.title, data=[np.array([rel_value])])
338
+ rel_value = DataActuator(self.title, data=[np.array([rel_value])], units=self.units)
311
339
  self._send_to_tcpip = send_to_tcpip
312
340
  if self.ui is not None:
313
341
  self.ui.move_done = False
@@ -362,8 +390,9 @@ class DAQ_Move(ParameterControlModule):
362
390
  self._hardware_thread.hardware = hardware
363
391
  self._hardware_thread.start()
364
392
  self.command_hardware.emit(
365
- ThreadCommand(command="ini_stage", attribute=[self.settings.child('move_settings').saveState(),
366
- self.controller]))
393
+ ThreadCommand(command="ini_stage", attribute=[
394
+ self.settings.child('move_settings').saveState(),
395
+ self.controller]))
367
396
  except Exception as e:
368
397
  self.logger.exception(str(e))
369
398
 
@@ -429,11 +458,10 @@ class DAQ_Move(ParameterControlModule):
429
458
  super().thread_status(status, 'move')
430
459
 
431
460
  if status.command == "ini_stage":
432
- # status.attribute[0]=edict(initialized=bool,info="", controller=)
433
- self.update_status("Stage initialized: {:} info: {:}".format(status.attribute[0]['initialized'],
434
- status.attribute[0]['info']))
435
- if status.attribute[0]['initialized']:
436
- self.controller = status.attribute[0]['controller']
461
+ self.update_status(f"Stage initialized: {status.attribute['initialized']} "
462
+ f"info: {status.attribute['info']}")
463
+ if status.attribute['initialized']:
464
+ self.controller = status.attribute['controller']
437
465
  if self.ui is not None:
438
466
  self.ui.actuator_init = True
439
467
  self._initialized_state = True
@@ -444,8 +472,16 @@ class DAQ_Move(ParameterControlModule):
444
472
  self.init_signal.emit(self._initialized_state)
445
473
 
446
474
  elif status.command == "get_actuator_value" or status.command == 'check_position':
447
- data_act: DataActuator = status.attribute[0]
475
+ if isinstance(status.attribute, DataActuator):
476
+ data_act: DataActuator = status.attribute
477
+ else:
478
+ data_act: DataActuator = status.attribute[0] # backcompatibility
448
479
  data_act.name = self.title # for the DataActuator name to be the title of the DAQ_Move
480
+ if (not Unit(self.units).is_compatible_with(Unit(data_act.units)) and
481
+ data_act.units == ''): #this happens if the units have not been specified in
482
+ # the plugin
483
+ data_act.force_units(self.units)
484
+
449
485
  if self.ui is not None:
450
486
  self.ui.display_value(data_act)
451
487
  if self.ui.is_action_checked('show_graph'):
@@ -454,12 +490,15 @@ class DAQ_Move(ParameterControlModule):
454
490
  self._current_value = data_act
455
491
  self.current_value_signal.emit(self._current_value)
456
492
  if self.settings['main_settings', 'tcpip', 'tcp_connected'] and self._send_to_tcpip:
457
- self._command_tcpip.emit(ThreadCommand('position_is', status.attribute))
493
+ self._command_tcpip.emit(ThreadCommand('position_is', data_act))
458
494
  if self.settings['main_settings', 'leco', 'leco_connected'] and self._send_to_tcpip:
459
- self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.POSITION, status.attribute))
495
+ self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.POSITION, data_act))
460
496
 
461
497
  elif status.command == "move_done":
462
- data_act: DataActuator = status.attribute[0]
498
+ if isinstance(status.attribute, DataActuator):
499
+ data_act: DataActuator = status.attribute
500
+ else:
501
+ data_act: DataActuator = status.attribute[0] # deprecated
463
502
  data_act.name = self.title # for the DataActuator name to be the title of the DAQ_Move
464
503
  if self.ui is not None:
465
504
  self.ui.display_value(data_act)
@@ -468,9 +507,9 @@ class DAQ_Move(ParameterControlModule):
468
507
  self._move_done_bool = True
469
508
  self.move_done_signal.emit(data_act)
470
509
  if self.settings.child('main_settings', 'tcpip', 'tcp_connected').value() and self._send_to_tcpip:
471
- self._command_tcpip.emit(ThreadCommand('move_done', status.attribute))
510
+ self._command_tcpip.emit(ThreadCommand('move_done', data_act))
472
511
  if self.settings.child('main_settings', 'leco', 'leco_connected').value() and self._send_to_tcpip:
473
- self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.MOVE_DONE, status.attribute))
512
+ self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.MOVE_DONE, data_act))
474
513
 
475
514
  elif status.command == 'outofbounds':
476
515
  self.bounds_signal.emit(True)
@@ -545,10 +584,15 @@ class DAQ_Move(ParameterControlModule):
545
584
  self.update_plugin_config()
546
585
  if self.ui is not None:
547
586
  self.ui.actuator = act_type
548
- self.update_settings()
587
+ self.update_settings()
549
588
  else:
550
589
  raise ActuatorError(f'{act_type} is an invalid actuator, should be within {ACTUATOR_TYPES}')
551
590
 
591
+ @property
592
+ def actuators(self) -> List[str]:
593
+ """ Get the list of possible actuators"""
594
+ return ACTUATOR_TYPES
595
+
552
596
  def update_plugin_config(self):
553
597
  parent_module = utils.find_dict_in_list_from_key_val(DAQ_Move_Actuators, 'name', self.actuator)
554
598
  mod = import_module(parent_module['module'].__package__.split('.')[0])
@@ -564,7 +608,35 @@ class DAQ_Move(ParameterControlModule):
564
608
  def units(self, unit: str):
565
609
  self.settings.child('move_settings', 'units').setValue(unit)
566
610
  if self.ui is not None and config('actuator', 'display_units'):
567
- self.ui.set_unit_as_suffix(unit)
611
+ self.ui.set_unit_as_suffix(self.get_unit_to_display(unit))
612
+
613
+ @staticmethod
614
+ def get_unit_to_display(unit: str) -> str:
615
+ """ Get the unit to be displayed in the UI
616
+
617
+ If the controller units are in mm the displayed unit will be m
618
+ because m is the base unit, then the user could ask for mm, km, µm...
619
+ only issue is when the usual displayed unit is not the base one, then add cases below
620
+
621
+ Parameters
622
+ ----------
623
+ unit: str
624
+
625
+ Returns
626
+ -------
627
+ str: the unit to be displayed on the ui
628
+ """
629
+ if ('°' in unit or 'degree' in unit) and not '°C' in unit:
630
+ # special cas as pint base unit for angles are radians
631
+ return '°'
632
+ elif 'W' in unit or 'watt' in unit.lower():
633
+ return 'W'
634
+ elif '°C' in unit or 'Celsius' in unit:
635
+ return '°C'
636
+ elif 'V' in unit or 'volt' in unit.lower():
637
+ return 'V'
638
+ else:
639
+ return str(Q_(1, unit).to_base_units().units)
568
640
 
569
641
  def update_settings(self):
570
642
 
@@ -641,8 +713,6 @@ class DAQ_Move_Hardware(QObject):
641
713
  self._title = title
642
714
  self.hardware: Optional[DAQ_Move_base] = None
643
715
  self.actuator_type = actuator_type
644
- self.current_position: DataActuator = position
645
- self._target_value: Optional[DataActuator] = None
646
716
  self.hardware_adress = None
647
717
  self.axis_address = None
648
718
  self.motion_stoped = False
@@ -656,17 +726,18 @@ class DAQ_Move_Hardware(QObject):
656
726
  Uninitialize the stage closing the hardware.
657
727
 
658
728
  """
659
- self.hardware.close()
729
+ if self.hardware is not None and self.hardware.controller is not None:
730
+ self.hardware.close()
660
731
 
661
732
  return "Stage uninitialized"
662
733
 
663
734
  def get_actuator_value(self):
664
735
  """Get the current position checking the hardware value.
665
736
  """
666
- pos = self.hardware.get_actuator_value()
667
- if self.hardware.data_actuator_type.name == 'float':
668
- return DataActuator(self._title, data=pos)
669
- else:
737
+ if self.hardware is not None:
738
+ pos = self.hardware.get_actuator_value()
739
+ if self.hardware.data_actuator_type == DataActuatorType.float:
740
+ pos = DataActuator(self._title, data=pos, units=self.hardware.axis_unit)
670
741
  return pos
671
742
 
672
743
  def check_position(self):
@@ -716,6 +787,8 @@ class DAQ_Move_Hardware(QObject):
716
787
  status.initialized = infos[1]
717
788
  status.controller = self.hardware.controller
718
789
  self.hardware.move_done_signal.connect(self.move_done)
790
+ if status.initialized:
791
+ self.status_sig.emit(ThreadCommand('get_actuator_value', [self.get_actuator_value()]))
719
792
 
720
793
  return status
721
794
  except Exception as e:
@@ -726,15 +799,13 @@ class DAQ_Move_Hardware(QObject):
726
799
  """
727
800
 
728
801
  """
729
- # if isinstance(position, Number):
730
- # position = float(position) # because it may be a numpy float and could cause issues
731
- # # see https://github.com/pythonnet/pythonnet/issues/1833
732
- self._target_value = position
802
+ position = check_units(position, self.hardware.axis_unit)
733
803
  self.hardware.move_is_done = False
734
804
  self.hardware.ispolling = polling
735
805
  if self.hardware.data_actuator_type.name == 'float':
736
806
  self.hardware.move_abs(position.value())
737
807
  else:
808
+ position.units = self.hardware.axis_unit # convert to plugin controller current axis units
738
809
  self.hardware.move_abs(position)
739
810
  self.hardware.poll_moving()
740
811
 
@@ -742,14 +813,14 @@ class DAQ_Move_Hardware(QObject):
742
813
  """
743
814
 
744
815
  """
745
-
816
+ rel_position = check_units(rel_position, self.hardware.axis_unit)
746
817
  self.hardware.move_is_done = False
747
- self._target_value = self.current_position + rel_position
748
818
  self.hardware.ispolling = polling
749
819
 
750
820
  if self.hardware.data_actuator_type.name == 'float':
751
821
  self.hardware.move_rel(rel_position.value())
752
822
  else:
823
+ rel_position.units = self.hardware.axis_unit # convert to plugin current axis units
753
824
  self.hardware.move_rel(rel_position)
754
825
 
755
826
  self.hardware.poll_moving()
@@ -763,7 +834,7 @@ class DAQ_Move_Hardware(QObject):
763
834
  --------
764
835
  DAQ_utils.ThreadCommand
765
836
  """
766
- self.status_sig.emit(ThreadCommand("move_done", [pos]))
837
+ self.status_sig.emit(ThreadCommand("move_done", pos))
767
838
 
768
839
  def move_home(self):
769
840
  """
@@ -771,7 +842,6 @@ class DAQ_Move_Hardware(QObject):
771
842
 
772
843
  """
773
844
  self.hardware.move_is_done = False
774
- self._target_value = 0
775
845
  self.hardware.move_home()
776
846
 
777
847
  @Slot(DataActuator)
@@ -779,7 +849,7 @@ class DAQ_Move_Hardware(QObject):
779
849
  """Send the move_done signal back to the main class
780
850
  """
781
851
  self._current_value = pos
782
- self.status_sig.emit(ThreadCommand(command="move_done", attribute=[pos]))
852
+ self.status_sig.emit(ThreadCommand(command="move_done", attribute=pos))
783
853
 
784
854
  @Slot(ThreadCommand)
785
855
  def queue_command(self, command: ThreadCommand):
@@ -807,10 +877,10 @@ class DAQ_Move_Hardware(QObject):
807
877
  * **reset_stop_motion** command, set the motion_stopped attribute to false
808
878
  """
809
879
  try:
880
+ logger.debug(f'Threadcommand {command.command} sent to {self.title}')
810
881
  if command.command == "ini_stage":
811
- status = self.ini_stage(
812
- *command.attribute) # return edict(initialized=bool,info="", controller=, stage=)
813
- self.status_sig.emit(ThreadCommand(command=command.command, attribute=[status, 'log']))
882
+ status: edict = self.ini_stage(*command.attribute)
883
+ self.status_sig.emit(ThreadCommand(command=command.command, attribute=status))
814
884
 
815
885
  elif command.command == "close":
816
886
  status = self.close()
@@ -838,11 +908,13 @@ class DAQ_Move_Hardware(QObject):
838
908
  else: # custom commands for particular plugins (see spectrometer module 'get_spectro_wl' for instance)
839
909
  if hasattr(self.hardware, command.command):
840
910
  cmd = getattr(self.hardware, command.command)
841
- cmd(*command.attribute)
911
+ if isinstance(command.attribute, list):
912
+ cmd(*command.attribute)
913
+ elif isinstance(command.attribute, dict):
914
+ cmd(**command.attribute)
842
915
  except Exception as e:
843
916
  self.logger.exception(str(e))
844
917
 
845
-
846
918
  def stop_motion(self):
847
919
  """
848
920
  stop hardware motion with motion_stopped attribute updtaed to True and a status signal sended with an "update_status" Thread Command
@@ -853,7 +925,8 @@ class DAQ_Move_Hardware(QObject):
853
925
  """
854
926
  self.status_sig.emit(ThreadCommand(command="Update_Status", attribute=["Motion stoping", 'log']))
855
927
  self.motion_stoped = True
856
- self.hardware.stop_motion()
928
+ if self.hardware is not None and self.hardware.controller is not None:
929
+ self.hardware.stop_motion()
857
930
  self.hardware.poll_timer.stop()
858
931
 
859
932
  @Slot(edict)
@@ -879,15 +952,13 @@ class DAQ_Move_Hardware(QObject):
879
952
  setattr(self, path[-1], param.value())
880
953
 
881
954
  elif path[0] == 'move_settings':
882
- self.hardware.update_settings(settings_parameter_dict)
955
+ if self.hardware is not None:
956
+ self.hardware.update_settings(settings_parameter_dict)
883
957
 
884
958
 
885
959
  def main(init_qt=True):
886
960
  if init_qt: # used for the test suite
887
- app = QtWidgets.QApplication(sys.argv)
888
- if config('style', 'darkstyle'):
889
- import qdarkstyle
890
- app.setStyleSheet(qdarkstyle.load_stylesheet(qdarkstyle.DarkPalette))
961
+ app = mkQApp("PyMoDAQ Move")
891
962
 
892
963
  widget = QtWidgets.QWidget()
893
964
  prog = DAQ_Move(widget, title="test")