pymodaq 5.0.0__py3-none-any.whl → 5.0.2__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 (63) hide show
  1. pymodaq/__init__.py +55 -89
  2. pymodaq/control_modules/daq_move.py +129 -55
  3. pymodaq/control_modules/daq_move_ui.py +42 -11
  4. pymodaq/control_modules/daq_viewer.py +32 -13
  5. pymodaq/control_modules/move_utility_classes.py +346 -79
  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 +28 -8
  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 +49 -41
  21. pymodaq/extensions/pid/utils.py +7 -31
  22. pymodaq/extensions/utils.py +41 -7
  23. pymodaq/post_treatment/load_and_plot.py +43 -10
  24. pymodaq/resources/setup_plugin.py +1 -0
  25. pymodaq/updater.py +107 -0
  26. pymodaq/utils/chrono_timer.py +6 -7
  27. pymodaq/utils/daq_utils.py +6 -3
  28. pymodaq/utils/data.py +21 -17
  29. pymodaq/utils/enums.py +6 -0
  30. pymodaq/utils/gui_utils/loader_utils.py +29 -2
  31. pymodaq/utils/gui_utils/utils.py +9 -12
  32. pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
  33. pymodaq/utils/h5modules/module_saving.py +5 -2
  34. pymodaq/utils/leco/daq_move_LECODirector.py +22 -16
  35. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +15 -9
  36. pymodaq/utils/leco/leco_director.py +4 -3
  37. pymodaq/utils/leco/pymodaq_listener.py +9 -13
  38. pymodaq/utils/leco/utils.py +40 -7
  39. pymodaq/utils/managers/modules_manager.py +22 -12
  40. pymodaq/utils/managers/overshoot_manager.py +45 -1
  41. pymodaq/utils/managers/preset_manager.py +22 -46
  42. pymodaq/utils/managers/preset_manager_utils.py +17 -13
  43. pymodaq/utils/managers/remote_manager.py +1 -1
  44. pymodaq/utils/messenger.py +6 -0
  45. pymodaq/utils/parameter/__init__.py +5 -1
  46. pymodaq/utils/tcp_ip/mysocket.py +4 -110
  47. pymodaq/utils/tcp_ip/serializer.py +4 -769
  48. pymodaq/utils/tcp_ip/tcp_server_client.py +5 -5
  49. pymodaq-5.0.2.dist-info/METADATA +242 -0
  50. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/RECORD +54 -55
  51. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/WHEEL +1 -1
  52. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.dist-info}/entry_points.txt +1 -0
  53. pymodaq/examples/custom_app.py +0 -255
  54. pymodaq/examples/custom_viewer.py +0 -112
  55. pymodaq/examples/parameter_ex.py +0 -158
  56. pymodaq/examples/preset_MockCamera.xml +0 -1
  57. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.py +0 -142
  58. pymodaq/post_treatment/daq_measurement/daq_measurement_GUI.ui +0 -232
  59. pymodaq/post_treatment/daq_measurement/daq_measurement_main.py +0 -391
  60. pymodaq/post_treatment/daq_measurement/process_from_QtDesigner_DAQ_Measurement_GUI.bat +0 -2
  61. pymodaq-5.0.0.dist-info/METADATA +0 -166
  62. /pymodaq/{post_treatment/daq_measurement → daq_utils}/__init__.py +0 -0
  63. {pymodaq-5.0.0.dist-info → pymodaq-5.0.2.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:
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
  import numbers
11
11
  from importlib import import_module
12
12
  from numbers import Number
13
- from random import randint
13
+
14
14
  import sys
15
15
  from typing import List, Tuple, Union, Optional, Type
16
16
  import numpy as np
@@ -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,7 @@ 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]
448
- data_act.name = self.title # for the DataActuator name to be the title of the DAQ_Move
475
+ data_act = self._check_data_type(status.attribute)
449
476
  if self.ui is not None:
450
477
  self.ui.display_value(data_act)
451
478
  if self.ui.is_action_checked('show_graph'):
@@ -454,13 +481,12 @@ class DAQ_Move(ParameterControlModule):
454
481
  self._current_value = data_act
455
482
  self.current_value_signal.emit(self._current_value)
456
483
  if self.settings['main_settings', 'tcpip', 'tcp_connected'] and self._send_to_tcpip:
457
- self._command_tcpip.emit(ThreadCommand('position_is', status.attribute))
484
+ self._command_tcpip.emit(ThreadCommand('position_is', data_act))
458
485
  if self.settings['main_settings', 'leco', 'leco_connected'] and self._send_to_tcpip:
459
- self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.POSITION, status.attribute))
486
+ self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.POSITION, data_act))
460
487
 
461
488
  elif status.command == "move_done":
462
- data_act: DataActuator = status.attribute[0]
463
- data_act.name = self.title # for the DataActuator name to be the title of the DAQ_Move
489
+ data_act = self._check_data_type(status.attribute)
464
490
  if self.ui is not None:
465
491
  self.ui.display_value(data_act)
466
492
  self.ui.move_done = True
@@ -468,9 +494,9 @@ class DAQ_Move(ParameterControlModule):
468
494
  self._move_done_bool = True
469
495
  self.move_done_signal.emit(data_act)
470
496
  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))
497
+ self._command_tcpip.emit(ThreadCommand('move_done', data_act))
472
498
  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))
499
+ self._command_tcpip.emit(ThreadCommand(LECOMoveCommands.MOVE_DONE, data_act))
474
500
 
475
501
  elif status.command == 'outofbounds':
476
502
  self.bounds_signal.emit(True)
@@ -485,6 +511,22 @@ class DAQ_Move(ParameterControlModule):
485
511
  elif status.command == 'units':
486
512
  self.units = status.attribute
487
513
 
514
+ def _check_data_type(self, data_act: Union[list[np.ndarray], float, DataActuator]) -> DataActuator:
515
+ """ Make sure the data is a DataActuator
516
+
517
+ Mostly to make sure DAQ_Move is backcompatible with old style plugins
518
+ """
519
+ if isinstance(data_act, list): # backcompatibility
520
+ data_act = data_act[0]
521
+ if isinstance(data_act, np.ndarray): # backcompatibility
522
+ data_act = DataActuator(data=[data_act], units=self.units)
523
+ data_act.name = self.title # for the DataActuator name to be the title of the DAQ_Move
524
+ if (not Unit(self.units).is_compatible_with(Unit(data_act.units)) and
525
+ data_act.units == ''): #this happens if the units have not been specified in
526
+ # the plugin
527
+ data_act.force_units(self.units)
528
+ return data_act
529
+
488
530
  def get_actuator_value(self):
489
531
  """Get the current actuator value via the "get_actuator_value" command send to the hardware
490
532
 
@@ -545,10 +587,15 @@ class DAQ_Move(ParameterControlModule):
545
587
  self.update_plugin_config()
546
588
  if self.ui is not None:
547
589
  self.ui.actuator = act_type
548
- self.update_settings()
590
+ self.update_settings()
549
591
  else:
550
592
  raise ActuatorError(f'{act_type} is an invalid actuator, should be within {ACTUATOR_TYPES}')
551
593
 
594
+ @property
595
+ def actuators(self) -> List[str]:
596
+ """ Get the list of possible actuators"""
597
+ return ACTUATOR_TYPES
598
+
552
599
  def update_plugin_config(self):
553
600
  parent_module = utils.find_dict_in_list_from_key_val(DAQ_Move_Actuators, 'name', self.actuator)
554
601
  mod = import_module(parent_module['module'].__package__.split('.')[0])
@@ -564,7 +611,35 @@ class DAQ_Move(ParameterControlModule):
564
611
  def units(self, unit: str):
565
612
  self.settings.child('move_settings', 'units').setValue(unit)
566
613
  if self.ui is not None and config('actuator', 'display_units'):
567
- self.ui.set_unit_as_suffix(unit)
614
+ self.ui.set_unit_as_suffix(self.get_unit_to_display(unit))
615
+
616
+ @staticmethod
617
+ def get_unit_to_display(unit: str) -> str:
618
+ """ Get the unit to be displayed in the UI
619
+
620
+ If the controller units are in mm the displayed unit will be m
621
+ because m is the base unit, then the user could ask for mm, km, µm...
622
+ only issue is when the usual displayed unit is not the base one, then add cases below
623
+
624
+ Parameters
625
+ ----------
626
+ unit: str
627
+
628
+ Returns
629
+ -------
630
+ str: the unit to be displayed on the ui
631
+ """
632
+ if ('°' in unit or 'degree' in unit) and not '°C' in unit:
633
+ # special cas as pint base unit for angles are radians
634
+ return '°'
635
+ elif 'W' in unit or 'watt' in unit.lower():
636
+ return 'W'
637
+ elif '°C' in unit or 'Celsius' in unit:
638
+ return '°C'
639
+ elif 'V' in unit or 'volt' in unit.lower():
640
+ return 'V'
641
+ else:
642
+ return str(Q_(1, unit).to_base_units().units)
568
643
 
569
644
  def update_settings(self):
570
645
 
@@ -641,8 +716,6 @@ class DAQ_Move_Hardware(QObject):
641
716
  self._title = title
642
717
  self.hardware: Optional[DAQ_Move_base] = None
643
718
  self.actuator_type = actuator_type
644
- self.current_position: DataActuator = position
645
- self._target_value: Optional[DataActuator] = None
646
719
  self.hardware_adress = None
647
720
  self.axis_address = None
648
721
  self.motion_stoped = False
@@ -656,17 +729,18 @@ class DAQ_Move_Hardware(QObject):
656
729
  Uninitialize the stage closing the hardware.
657
730
 
658
731
  """
659
- self.hardware.close()
732
+ if self.hardware is not None and self.hardware.controller is not None:
733
+ self.hardware.close()
660
734
 
661
735
  return "Stage uninitialized"
662
736
 
663
737
  def get_actuator_value(self):
664
738
  """Get the current position checking the hardware value.
665
739
  """
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:
740
+ if self.hardware is not None:
741
+ pos = self.hardware.get_actuator_value()
742
+ if self.hardware.data_actuator_type == DataActuatorType.float:
743
+ pos = DataActuator(self._title, data=pos, units=self.hardware.axis_unit)
670
744
  return pos
671
745
 
672
746
  def check_position(self):
@@ -716,6 +790,8 @@ class DAQ_Move_Hardware(QObject):
716
790
  status.initialized = infos[1]
717
791
  status.controller = self.hardware.controller
718
792
  self.hardware.move_done_signal.connect(self.move_done)
793
+ if status.initialized:
794
+ self.status_sig.emit(ThreadCommand('get_actuator_value', [self.get_actuator_value()]))
719
795
 
720
796
  return status
721
797
  except Exception as e:
@@ -726,15 +802,13 @@ class DAQ_Move_Hardware(QObject):
726
802
  """
727
803
 
728
804
  """
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
805
+ position = check_units(position, self.hardware.axis_unit)
733
806
  self.hardware.move_is_done = False
734
807
  self.hardware.ispolling = polling
735
808
  if self.hardware.data_actuator_type.name == 'float':
736
809
  self.hardware.move_abs(position.value())
737
810
  else:
811
+ position.units = self.hardware.axis_unit # convert to plugin controller current axis units
738
812
  self.hardware.move_abs(position)
739
813
  self.hardware.poll_moving()
740
814
 
@@ -742,14 +816,14 @@ class DAQ_Move_Hardware(QObject):
742
816
  """
743
817
 
744
818
  """
745
-
819
+ rel_position = check_units(rel_position, self.hardware.axis_unit)
746
820
  self.hardware.move_is_done = False
747
- self._target_value = self.current_position + rel_position
748
821
  self.hardware.ispolling = polling
749
822
 
750
823
  if self.hardware.data_actuator_type.name == 'float':
751
824
  self.hardware.move_rel(rel_position.value())
752
825
  else:
826
+ rel_position.units = self.hardware.axis_unit # convert to plugin current axis units
753
827
  self.hardware.move_rel(rel_position)
754
828
 
755
829
  self.hardware.poll_moving()
@@ -763,7 +837,7 @@ class DAQ_Move_Hardware(QObject):
763
837
  --------
764
838
  DAQ_utils.ThreadCommand
765
839
  """
766
- self.status_sig.emit(ThreadCommand("move_done", [pos]))
840
+ self.status_sig.emit(ThreadCommand("move_done", pos))
767
841
 
768
842
  def move_home(self):
769
843
  """
@@ -771,7 +845,6 @@ class DAQ_Move_Hardware(QObject):
771
845
 
772
846
  """
773
847
  self.hardware.move_is_done = False
774
- self._target_value = 0
775
848
  self.hardware.move_home()
776
849
 
777
850
  @Slot(DataActuator)
@@ -779,7 +852,7 @@ class DAQ_Move_Hardware(QObject):
779
852
  """Send the move_done signal back to the main class
780
853
  """
781
854
  self._current_value = pos
782
- self.status_sig.emit(ThreadCommand(command="move_done", attribute=[pos]))
855
+ self.status_sig.emit(ThreadCommand(command="move_done", attribute=pos))
783
856
 
784
857
  @Slot(ThreadCommand)
785
858
  def queue_command(self, command: ThreadCommand):
@@ -807,10 +880,10 @@ class DAQ_Move_Hardware(QObject):
807
880
  * **reset_stop_motion** command, set the motion_stopped attribute to false
808
881
  """
809
882
  try:
883
+ logger.debug(f'Threadcommand {command.command} sent to {self.title}')
810
884
  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']))
885
+ status: edict = self.ini_stage(*command.attribute)
886
+ self.status_sig.emit(ThreadCommand(command=command.command, attribute=status))
814
887
 
815
888
  elif command.command == "close":
816
889
  status = self.close()
@@ -838,11 +911,13 @@ class DAQ_Move_Hardware(QObject):
838
911
  else: # custom commands for particular plugins (see spectrometer module 'get_spectro_wl' for instance)
839
912
  if hasattr(self.hardware, command.command):
840
913
  cmd = getattr(self.hardware, command.command)
841
- cmd(*command.attribute)
914
+ if isinstance(command.attribute, list):
915
+ cmd(*command.attribute)
916
+ elif isinstance(command.attribute, dict):
917
+ cmd(**command.attribute)
842
918
  except Exception as e:
843
919
  self.logger.exception(str(e))
844
920
 
845
-
846
921
  def stop_motion(self):
847
922
  """
848
923
  stop hardware motion with motion_stopped attribute updtaed to True and a status signal sended with an "update_status" Thread Command
@@ -853,7 +928,8 @@ class DAQ_Move_Hardware(QObject):
853
928
  """
854
929
  self.status_sig.emit(ThreadCommand(command="Update_Status", attribute=["Motion stoping", 'log']))
855
930
  self.motion_stoped = True
856
- self.hardware.stop_motion()
931
+ if self.hardware is not None and self.hardware.controller is not None:
932
+ self.hardware.stop_motion()
857
933
  self.hardware.poll_timer.stop()
858
934
 
859
935
  @Slot(edict)
@@ -879,15 +955,13 @@ class DAQ_Move_Hardware(QObject):
879
955
  setattr(self, path[-1], param.value())
880
956
 
881
957
  elif path[0] == 'move_settings':
882
- self.hardware.update_settings(settings_parameter_dict)
958
+ if self.hardware is not None:
959
+ self.hardware.update_settings(settings_parameter_dict)
883
960
 
884
961
 
885
962
  def main(init_qt=True):
886
963
  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))
964
+ app = mkQApp("PyMoDAQ Move")
891
965
 
892
966
  widget = QtWidgets.QWidget()
893
967
  prog = DAQ_Move(widget, title="test")