pymodaq 5.0.17__py3-none-any.whl → 5.1.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.

Files changed (92) hide show
  1. pymodaq/__init__.py +23 -11
  2. pymodaq/control_modules/__init__.py +1 -0
  3. pymodaq/control_modules/daq_move.py +458 -246
  4. pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
  5. pymodaq/control_modules/daq_move_ui/factory.py +48 -0
  6. pymodaq/control_modules/{daq_move_ui.py → daq_move_ui/ui_base.py} +168 -210
  7. pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
  8. pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
  9. pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
  10. pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
  11. pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
  12. pymodaq/control_modules/daq_viewer.py +113 -101
  13. pymodaq/control_modules/daq_viewer_ui.py +41 -31
  14. pymodaq/control_modules/mocks.py +2 -2
  15. pymodaq/control_modules/move_utility_classes.py +113 -41
  16. pymodaq/control_modules/thread_commands.py +137 -0
  17. pymodaq/control_modules/ui_utils.py +72 -0
  18. pymodaq/control_modules/utils.py +107 -63
  19. pymodaq/control_modules/viewer_utility_classes.py +13 -17
  20. pymodaq/dashboard.py +1294 -625
  21. pymodaq/examples/qt_less_standalone_module.py +48 -11
  22. pymodaq/extensions/__init__.py +8 -3
  23. pymodaq/extensions/adaptive/__init__.py +2 -0
  24. pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
  25. pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
  26. pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
  27. pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
  28. pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
  29. pymodaq/extensions/adaptive/utils.py +123 -0
  30. pymodaq/extensions/bayesian/__init__.py +1 -1
  31. pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
  32. pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
  33. pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
  34. pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
  35. pymodaq/extensions/bayesian/utils.py +71 -297
  36. pymodaq/extensions/daq_logger/daq_logger.py +7 -12
  37. pymodaq/extensions/daq_logger/h5logging.py +1 -1
  38. pymodaq/extensions/daq_scan.py +30 -55
  39. pymodaq/extensions/data_mixer/__init__.py +0 -0
  40. pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
  41. pymodaq/extensions/data_mixer/data_mixer.py +262 -0
  42. pymodaq/extensions/data_mixer/model.py +108 -0
  43. pymodaq/extensions/data_mixer/models/__init__.py +0 -0
  44. pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
  45. pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
  46. pymodaq/extensions/data_mixer/parser.py +53 -0
  47. pymodaq/extensions/data_mixer/utils.py +23 -0
  48. pymodaq/extensions/h5browser.py +3 -34
  49. pymodaq/extensions/optimizers_base/__init__.py +0 -0
  50. pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
  51. pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
  52. pymodaq/extensions/optimizers_base/utils.py +427 -0
  53. pymodaq/extensions/pid/actuator_controller.py +3 -2
  54. pymodaq/extensions/pid/daq_move_PID.py +107 -30
  55. pymodaq/extensions/pid/pid_controller.py +613 -287
  56. pymodaq/extensions/pid/utils.py +8 -5
  57. pymodaq/extensions/utils.py +17 -2
  58. pymodaq/resources/config_template.toml +57 -0
  59. pymodaq/resources/preset_default.xml +1 -1
  60. pymodaq/utils/config.py +13 -4
  61. pymodaq/utils/daq_utils.py +14 -0
  62. pymodaq/utils/data.py +1 -0
  63. pymodaq/utils/gui_utils/loader_utils.py +25 -15
  64. pymodaq/utils/h5modules/module_saving.py +134 -22
  65. pymodaq/utils/leco/daq_move_LECODirector.py +123 -84
  66. pymodaq/utils/leco/daq_xDviewer_LECODirector.py +84 -97
  67. pymodaq/utils/leco/director_utils.py +32 -16
  68. pymodaq/utils/leco/leco_director.py +104 -27
  69. pymodaq/utils/leco/pymodaq_listener.py +186 -97
  70. pymodaq/utils/leco/rpc_method_definitions.py +43 -0
  71. pymodaq/utils/leco/utils.py +25 -25
  72. pymodaq/utils/managers/batchscan_manager.py +12 -11
  73. pymodaq/utils/managers/modules_manager.py +74 -33
  74. pymodaq/utils/managers/overshoot_manager.py +11 -10
  75. pymodaq/utils/managers/preset_manager.py +100 -64
  76. pymodaq/utils/managers/preset_manager_utils.py +163 -107
  77. pymodaq/utils/managers/remote_manager.py +21 -16
  78. pymodaq/utils/scanner/scan_factory.py +18 -4
  79. pymodaq/utils/scanner/scan_selector.py +1 -3
  80. pymodaq/utils/scanner/scanner.py +35 -6
  81. pymodaq/utils/scanner/scanners/_1d_scanners.py +15 -46
  82. pymodaq/utils/scanner/scanners/_2d_scanners.py +21 -68
  83. pymodaq/utils/scanner/scanners/sequential.py +50 -31
  84. pymodaq/utils/scanner/scanners/tabular.py +45 -28
  85. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/METADATA +7 -6
  86. pymodaq-5.1.0.dist-info/RECORD +154 -0
  87. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/entry_points.txt +0 -2
  88. pymodaq/extensions/bayesian/bayesian_optimisation.py +0 -685
  89. pymodaq/utils/leco/desktop.ini +0 -2
  90. pymodaq-5.0.17.dist-info/RECORD +0 -121
  91. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/WHEEL +0 -0
  92. {pymodaq-5.0.17.dist-info → pymodaq-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,19 +3,31 @@ from pymodaq_utils.enums import StrEnum
3
3
 
4
4
  import logging
5
5
  from threading import Event
6
- from typing import Optional, Union, List, Type
6
+ from typing import cast, Optional, Union, List, Sequence, Type
7
7
 
8
8
  from pyleco.core import COORDINATOR_PORT
9
+ from pyleco.json_utils.errors import JSONRPCError, RECEIVER_UNKNOWN, NODE_UNKNOWN
9
10
  from pyleco.utils.listener import Listener, PipeHandler
10
11
  from qtpy.QtCore import QObject, Signal # type: ignore
11
12
 
13
+ from pymodaq_data.data import DataWithAxes
14
+ from pymodaq_utils.serialize.factory import SerializableFactory, SerializableBase
12
15
  from pymodaq_utils.utils import ThreadCommand
13
16
  from pymodaq_gui.parameter import ioxml
14
- from pymodaq_data.data import DataWithAxes
15
- from pymodaq_utils.serialize.serializer_legacy import SERIALIZABLE, DeSerializer
17
+ from pymodaq_gui.parameter.utils import ParameterWithPath
18
+
16
19
  from pymodaq.utils.leco.utils import binary_serialization_to_kwargs
20
+ from pymodaq.utils.leco.rpc_method_definitions import (
21
+ GenericMethods,
22
+ MoveMethods,
23
+ ViewerMethods,
24
+ GenericDirectorMethods,
25
+ MoveDirectorMethods,
26
+ ViewerDirectorMethods,
27
+ )
17
28
 
18
29
 
30
+ # Commands for ThreadCommand
19
31
  class LECOClientCommands(StrEnum):
20
32
  LECO_CONNECTED = "leco_connected"
21
33
  LECO_DISCONNECTED = "leco_disconnected"
@@ -24,15 +36,28 @@ class LECOClientCommands(StrEnum):
24
36
  class LECOCommands(StrEnum):
25
37
  CONNECT = "ini_connection"
26
38
  QUIT = "quit"
39
+ GET_SETTINGS = 'get_settings'
40
+ SET_DIRECTOR_SETTINGS = 'set_director_settings'
41
+ SET_INFO = 'set_info'
42
+ SEND_INFO = 'send_info'
27
43
 
28
44
 
29
45
  class LECOMoveCommands(StrEnum):
30
46
  POSITION = 'position_is'
31
47
  MOVE_DONE = 'move_done'
48
+ UNITS_CHANGED = 'units_changed'
49
+ STOP = 'stop_motion'
50
+ MOVE_ABS = 'move_abs'
51
+ MOVE_REL = 'move_rel'
52
+ MOVE_HOME = 'move_home'
53
+ GET_ACTUATOR_VALUE = 'get_actuator_value'
32
54
 
33
55
 
34
56
  class LECOViewerCommands(StrEnum):
35
57
  DATA_READY = 'data_ready'
58
+ GRAB = 'grab'
59
+ SNAP = 'snap'
60
+ STOP = 'stop_grab'
36
61
 
37
62
 
38
63
  class ListenerSignals(QObject):
@@ -53,54 +78,120 @@ class PymodaqPipeHandler(PipeHandler):
53
78
  def __init__(self, name: str, signals: ListenerSignals, **kwargs) -> None:
54
79
  super().__init__(name, **kwargs)
55
80
  self.signals = signals
56
-
81
+ self.register_data_types_for_deserialization()
82
+
83
+ def register_data_types_for_deserialization(
84
+ self, types: Optional[Sequence[type[SerializableBase]]] = None
85
+ ) -> None:
86
+ """Register different data types for deserialization in subclasses."""
87
+ if types is None:
88
+ return
89
+ for cls in types:
90
+ SerializableFactory().register_from_type(
91
+ cls, cls.serialize, cls.deserialize
92
+ )
57
93
 
58
94
  class ActorHandler(PymodaqPipeHandler):
95
+ def register_data_types_for_deserialization(
96
+ self, types: Optional[Sequence[type[SerializableBase]]] = None
97
+ ) -> None:
98
+ all_types: Sequence[type[SerializableBase]] = [DataWithAxes]
99
+ if types:
100
+ all_types.extend(types) # type: ignore
101
+ super().register_data_types_for_deserialization(all_types)
59
102
 
60
103
  def register_rpc_methods(self) -> None:
61
104
  super().register_rpc_methods()
62
- self.register_rpc_method(self.set_info)
63
- self.register_rpc_method(self.send_data)
64
- self.register_rpc_method(self.move_abs)
65
- self.register_rpc_method(self.move_rel)
66
- self.register_rpc_method(self.move_home)
67
- self.register_rpc_method(self.get_actuator_value)
68
- self.register_rpc_method(self.stop_motion)
105
+ self.register_binary_rpc_method(
106
+ self.set_info, name=GenericMethods.SET_INFO, accept_binary_input=True
107
+ )
108
+ self.register_rpc_method(self.send_data_grab, name=ViewerMethods.GRAB)
109
+ self.register_rpc_method(self.send_data_snap, name=ViewerMethods.SNAP)
110
+ self.register_binary_rpc_method(
111
+ self.move_abs, accept_binary_input=True, name=MoveMethods.MOVE_ABS
112
+ )
113
+ self.register_binary_rpc_method(
114
+ self.move_rel, accept_binary_input=True, name=MoveMethods.MOVE_REL
115
+ )
116
+ self.register_rpc_method(self.move_home, name=MoveMethods.MOVE_HOME)
117
+ self.register_rpc_method(self.get_actuator_value, name=MoveMethods.GET_ACTUATOR_VALUE)
118
+ self.register_rpc_method(self.stop_motion, name=MoveMethods.STOP_MOTION)
119
+ self.register_rpc_method(self.stop_grab, name=ViewerMethods.STOP)
120
+ self.register_rpc_method(self.get_settings, name=GenericMethods.GET_SETTINGS)
69
121
 
70
122
  @staticmethod
71
- def extract_dwa_object(data_string: str) -> DataWithAxes:
72
- """Extract a DataWithAxes object from the received message."""
73
- desererializer = DeSerializer.from_b64_string(data_string)
74
- return desererializer.dwa_deserialization()
123
+ def extract_pymodaq_object(
124
+ value: Optional[Union[float, str]], additional_payload: Optional[List[bytes]]
125
+ ):
126
+ if value is None and additional_payload:
127
+ return cast(
128
+ DataWithAxes, SerializableFactory().get_apply_deserializer(additional_payload[0])
129
+ )
130
+ else:
131
+ return value
75
132
 
76
133
  # generic commands
77
- def set_info(self, path: List[str], param_dict_str: str) -> None:
78
- self.signals.cmd_signal.emit(ThreadCommand("set_info", attribute=[path, param_dict_str]))
134
+ def set_info(self,
135
+ parameter: Optional[Union[float, str]],
136
+ additional_payload: Optional[List[bytes]] = None,
137
+ ) -> None:
138
+ assert additional_payload
139
+ param = cast(
140
+ ParameterWithPath, SerializableFactory().get_apply_deserializer(additional_payload[0])
141
+ )
142
+ self.signals.cmd_signal.emit(ThreadCommand(LECOCommands.SET_INFO, attribute=param))
143
+
144
+ def get_settings(self):
145
+ self.signals.cmd_signal.emit(ThreadCommand(LECOCommands.GET_SETTINGS))
79
146
 
80
147
  # detector commands
81
- def send_data(self, grabber_type: str = "") -> None:
82
- self.signals.cmd_signal.emit(ThreadCommand(f"Send Data {grabber_type}"))
148
+ def send_data_grab(self,) -> None:
149
+ self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.GRAB))
83
150
 
84
- # actuator commands
85
- def move_abs(self, position: Union[float, str]) -> None:
86
- pos = self.extract_dwa_object(position) if isinstance(position, str) else position
87
- self.signals.cmd_signal.emit(ThreadCommand("move_abs", attribute=[pos]))
151
+ # detector commands
152
+ def send_data_snap(self,) -> None:
153
+ self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.SNAP))
88
154
 
89
- def move_rel(self, position: Union[float, str]) -> None:
90
- pos = self.extract_dwa_object(position) if isinstance(position, str) else position
91
- self.signals.cmd_signal.emit(ThreadCommand("move_rel", attribute=[pos]))
155
+ # actuator commands
156
+ def move_abs(
157
+ self,
158
+ position: Optional[Union[float, str]],
159
+ additional_payload: Optional[List[bytes]] = None,
160
+ ) -> None:
161
+ """Move to an absolute position.
162
+
163
+ :param position: Deprecated, should be None and content transferred binary.
164
+ :param additional_payload: binary frames containing the position as PyMoDAQ `DataActuator`.
165
+ """
166
+ pos = self.extract_pymodaq_object(position, additional_payload)
167
+ self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_ABS, pos))
168
+
169
+ def move_rel(
170
+ self,
171
+ position: Optional[Union[float, str]],
172
+ additional_payload: Optional[List[bytes]] = None,
173
+ ) -> None:
174
+ """Move by a relative position.
175
+
176
+ :param position: Deprecated, should be None and content transferred binary.
177
+ :param additional_payload: binary frames containing the position as PyMoDAQ `DataActuator`.
178
+ """
179
+ pos = self.extract_pymodaq_object(position, additional_payload)
180
+ self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_REL, pos))
92
181
 
93
182
  def move_home(self) -> None:
94
- self.signals.cmd_signal.emit(ThreadCommand("move_home"))
183
+ self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.MOVE_HOME))
95
184
 
96
185
  def get_actuator_value(self) -> None:
97
186
  """Request that the actuator value is sent later on."""
98
187
  # according to DAQ_Move, this supersedes "check_position"
99
- self.signals.cmd_signal.emit(ThreadCommand("get_actuator_value"))
188
+ self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.GET_ACTUATOR_VALUE))
100
189
 
101
190
  def stop_motion(self,) -> None:
102
- # not implemented in DAQ_Move!
103
- self.signals.cmd_signal.emit(ThreadCommand("stop_motion"))
191
+ self.signals.cmd_signal.emit(ThreadCommand(LECOMoveCommands.STOP))
192
+
193
+ def stop_grab(self,) -> None:
194
+ self.signals.cmd_signal.emit(ThreadCommand(LECOViewerCommands.STOP))
104
195
 
105
196
 
106
197
  # to be able to separate them later on
@@ -115,7 +206,7 @@ class PymodaqListener(Listener):
115
206
  :param host: Host name of the communication server.
116
207
  :param port: Port number of the communication server.
117
208
  """
118
- remote_name: str = ""
209
+ remote_names: set[str]
119
210
 
120
211
  local_methods = ["pong", "set_log_level"]
121
212
 
@@ -129,6 +220,7 @@ class PymodaqListener(Listener):
129
220
  **kwargs) -> None:
130
221
  super().__init__(name, host, port, logger=logger, timeout=timeout,
131
222
  **kwargs)
223
+ self.remote_names = set()
132
224
  self.signals = ListenerSignals()
133
225
  # self.signals.message.connect(self.handle_message)
134
226
  self.cmd_signal = self.signals.cmd_signal
@@ -176,13 +268,17 @@ class ActorListener(PymodaqListener):
176
268
 
177
269
  def start_listen(self) -> None:
178
270
  super().start_listen()
179
- self.message_handler.register_rpc_method(self.set_remote_name)
271
+ self.message_handler.register_rpc_method(
272
+ self.set_remote_name, name=GenericMethods.SET_REMOTE_NAME
273
+ )
180
274
 
181
275
  def set_remote_name(self, name: str) -> None:
182
276
  """Define what the name of the remote for answers is."""
183
- self.remote_name = name
277
+ self.remote_names.add(name)
278
+
279
+ def remove_remote_name(self, name: str) -> None:
280
+ self.remote_names.discard(name)
184
281
 
185
- # @Slot(ThreadCommand)
186
282
  def queue_command(self, command: ThreadCommand) -> None:
187
283
  """Queue a command to send it via LECO to the server."""
188
284
 
@@ -203,71 +299,64 @@ class ActorListener(PymodaqListener):
203
299
  finally:
204
300
  self.cmd_signal.emit(ThreadCommand('disconnected'))
205
301
 
206
- elif command.command == 'update_connection':
207
- # self.ipaddress = command.attribute['ipaddress']
208
- # self.port = command.attribute['port']
209
- pass # TODO change name?
210
-
211
- elif command.command == LECOViewerCommands.DATA_READY:
212
- # code from the original:
213
- # self.data_ready(data=command.attribute)
214
- # def data_ready(data): self.send_data(datas[0]['data'])
215
- value = command.attribute # type: ignore
216
- self.communicator.ask_rpc(
217
- receiver=self.remote_name,
218
- method="set_data",
219
- **binary_serialization_to_kwargs(value),
220
- )
221
-
222
- elif command.command == 'send_info':
223
- path = command.attribute['path'] # type: ignore
224
- param = command.attribute['param'] # type: ignore
225
- self.communicator.ask_rpc(
226
- receiver=self.remote_name,
227
- method="set_info",
228
- path=path,
229
- param_dict_str=ioxml.parameter_to_xml_string(param).decode())
230
-
231
- elif command.command == LECOMoveCommands.POSITION:
232
- value = command.attribute[0] # type: ignore
233
- self.communicator.ask_rpc(receiver=self.remote_name,
234
- method="set_position",
235
- **binary_serialization_to_kwargs(value, data_key="position"),
236
- )
237
-
238
- elif command.command == LECOMoveCommands.MOVE_DONE:
239
- value = command.attribute[0] # type: ignore
240
- self.communicator.ask_rpc(receiver=self.remote_name,
241
- method="set_move_done",
242
- **binary_serialization_to_kwargs(value, data_key="position"),
243
- )
244
-
245
- elif command.command == 'x_axis':
246
- value = command.attribute[0] # type: ignore
247
- if isinstance(value, SERIALIZABLE):
248
- self.communicator.ask_rpc(receiver=self.remote_name,
249
- method="set_x_axis",
250
- **binary_serialization_to_kwargs(value),
251
- )
252
- elif isinstance(value, dict):
253
- self.communicator.ask_rpc(receiver=self.remote_name, method="set_x_axis", **value)
254
- else:
255
- raise ValueError("Nothing to send!")
256
-
257
- elif command.command == 'y_axis':
258
- value = command.attribute[0] # type: ignore
259
- if isinstance(value, SERIALIZABLE):
260
- self.communicator.ask_rpc(receiver=self.remote_name,
261
- method="set_y_axis",
262
- **binary_serialization_to_kwargs(value),
263
- )
264
- elif isinstance(value, dict):
265
- self.communicator.ask_rpc(receiver=self.remote_name, method="set_y_axis", **value)
266
- else:
267
- raise ValueError("Nothing to send!")
302
+ elif command.attribute is not None:
303
+ # here are all methods requiring an attribute in the ThreadCommand
304
+
305
+ if command.command == LECOViewerCommands.DATA_READY:
306
+ value = command.attribute
307
+ self.send_rpc_message_to_remote(
308
+ method=ViewerDirectorMethods.SET_DATA,
309
+ **binary_serialization_to_kwargs(value),
310
+ )
311
+
312
+ elif command.command == LECOCommands.SEND_INFO:
313
+ self.send_rpc_message_to_remote(
314
+ method=GenericDirectorMethods.SET_DIRECTOR_INFO,
315
+ **binary_serialization_to_kwargs(command.attribute, data_key='parameter'))
316
+
317
+ elif command.command == LECOMoveCommands.POSITION:
318
+ value = command.attribute
319
+ if isinstance(value, (list, tuple)):
320
+ value = value[0] # for backward compatibility with attributes list
321
+ self.send_rpc_message_to_remote(
322
+ method=MoveDirectorMethods.SEND_POSITION,
323
+ **binary_serialization_to_kwargs(pymodaq_object=value, data_key="position"),
324
+ )
325
+
326
+ elif command.command == LECOMoveCommands.MOVE_DONE:
327
+ value = command.attribute
328
+ if isinstance(value, (list, tuple)):
329
+ value = value[0] # for backward compatibility with attributes list
330
+ self.send_rpc_message_to_remote(
331
+ method=MoveDirectorMethods.SET_MOVE_DONE,
332
+ **binary_serialization_to_kwargs(value, data_key="position"),
333
+ )
334
+
335
+ elif command.command == LECOMoveCommands.UNITS_CHANGED:
336
+ units: str = command.attribute
337
+ self.send_rpc_message_to_remote(
338
+ method=MoveDirectorMethods.SET_UNITS,
339
+ units=units.encode(),
340
+ )
341
+
342
+ elif command.command == LECOCommands.SET_DIRECTOR_SETTINGS:
343
+ self.send_rpc_message_to_remote(
344
+ method=GenericDirectorMethods.SET_DIRECTOR_SETTINGS,
345
+ settings=command.attribute.decode(),
346
+ )
268
347
 
269
348
  else:
270
- raise IOError('Unknown TCP client command')
349
+ raise IOError("Unknown TCP client command")
350
+
351
+ def send_rpc_message_to_remote(self, method: str, **kwargs) -> None:
352
+ if not self.remote_names:
353
+ return
354
+ for name in list(self.remote_names):
355
+ try:
356
+ self.communicator.ask_rpc(receiver=name, method=method, **kwargs)
357
+ except JSONRPCError as exc:
358
+ if exc.rpc_error.code in (RECEIVER_UNKNOWN.code, NODE_UNKNOWN.code):
359
+ self.remove_remote_name(name)
271
360
 
272
361
 
273
362
  # to be able to separate them later on
@@ -0,0 +1,43 @@
1
+ """
2
+ Names of methods used between remotely controlled modules and
3
+ remote controlling director modules.
4
+ """
5
+
6
+ from pymodaq_utils.enums import StrEnum
7
+
8
+
9
+ # Methods for all PyMoDAQ modules
10
+ class GenericMethods(StrEnum):
11
+ SET_INFO = "set_info"
12
+ GET_SETTINGS = "get_settings"
13
+ SET_REMOTE_NAME = "set_remote_name"
14
+
15
+
16
+ class MoveMethods(StrEnum):
17
+ MOVE_ABS = "move_abs"
18
+ MOVE_REL = "move_rel"
19
+ MOVE_HOME = "move_home"
20
+ STOP_MOTION = "stop_motion"
21
+ GET_ACTUATOR_VALUE = "get_actuator_value"
22
+
23
+
24
+ class ViewerMethods(StrEnum):
25
+ GRAB = "send_data_grab"
26
+ SNAP = "send_data_snap"
27
+ STOP = "stop_grab"
28
+
29
+
30
+ # Director module methods
31
+ class GenericDirectorMethods(StrEnum):
32
+ SET_DIRECTOR_SETTINGS = "set_director_settings"
33
+ SET_DIRECTOR_INFO = "set_director_info"
34
+
35
+
36
+ class MoveDirectorMethods(StrEnum):
37
+ SET_UNITS = "set_units"
38
+ SEND_POSITION = "send_position"
39
+ SET_MOVE_DONE = "set_move_done"
40
+
41
+
42
+ class ViewerDirectorMethods(StrEnum):
43
+ SET_DATA = "set_data"
@@ -3,27 +3,17 @@ import subprocess
3
3
  import sys
4
4
  from typing import Any, Optional, Union, get_args, TypeVar
5
5
 
6
+
6
7
  from pymodaq.utils import data
7
- # import also the DeSerializer for easier imports in dependents
8
- from pymodaq_utils.serialize.serializer_legacy import Serializer, DeSerializer, SerializableFactory
8
+ from pymodaq_utils.serialize.factory import SerializableFactory
9
9
 
10
- # type: ignore # noqa
11
10
  from pymodaq_utils.logger import set_logger
12
11
 
13
12
 
14
13
  logger = set_logger('leco_utils')
15
- ser_factory = SerializableFactory()
16
- JSON_TYPES = Union[str, int, float]
17
-
14
+ JSON_TYPES = Union[str, int, float, list]
18
15
 
19
-
20
- def serialize_object(pymodaq_object: Union[SERIALIZABLE, Any]) -> Union[str, Any]:
21
- """Serialize a pymodaq object, if it is not JSON compatible."""
22
- if isinstance(pymodaq_object, get_args(JSON_TYPES)):
23
- return pymodaq_object
24
- else:
25
- return Serializer(pymodaq_object).to_b64_string() # will raise a proper error if the object
26
- #is not serializable
16
+ ser_factory = SerializableFactory()
27
17
 
28
18
 
29
19
  ## this form below is to be compatible with python <= 3.10
@@ -36,22 +26,33 @@ for klass in ser_factory.get_serializables()[1:]:
36
26
  def binary_serialization(
37
27
  pymodaq_object: Union[SERIALIZABLE, Any],
38
28
  ) -> tuple[Optional[Any], Optional[list[bytes]]]:
39
- """Serialize (binary) a pymodaq object, if it is not JSON compatible."""
29
+ """Serialize (binary) a pymodaq object, if it is not JSON compatible.
30
+
31
+ If an object is JSON serializable, we can send it as a value to the JSON
32
+ encoder and do not need a binary value.
33
+ Otherwise, the JSON value is None and the serialized object is returned.
34
+
35
+ :param pymodaq_object: the object which shall be sent via LECO protocol.
36
+ :return: tuple of the JSON value and a list of additional payload frames.
37
+ """
40
38
  if isinstance(pymodaq_object, get_args(JSON_TYPES)):
41
39
  return pymodaq_object, None
42
- elif isinstance(pymodaq_object, get_args(SERIALIZABLE)):
43
- return None, [Serializer(pymodaq_object).to_bytes()]
44
- else:
45
- raise ValueError(
46
- f"{pymodaq_object} of type '{type(pymodaq_object).__name__}' is neither "
47
- "JSON serializable, nor via PyMoDAQ."
48
- )
40
+ return None, [SerializableFactory().get_apply_serializer(pymodaq_object)]
41
+
49
42
 
50
43
 
51
44
  def binary_serialization_to_kwargs(
52
45
  pymodaq_object: Union[SERIALIZABLE, Any], data_key: str = "data"
53
46
  ) -> dict[str, Any]:
54
- """Create a dictionary of data parameters and of additional payload to send."""
47
+ """Create a dictionary of data parameters and of additional payload to send.
48
+
49
+ This method prepares arguments for PyLECO's :meth:`Communicator.ask_rpc` method.
50
+ In order to send a binary value as an argument `data_key` for a method, the
51
+ argument has to be None, which is JSON describable.
52
+ The binary data has to be sent via the `additional_payload` parameter.
53
+ If the data can be sent as JSON, it is sent directly (`data_key`) and no
54
+ additional frames are sent.
55
+ """
55
56
  d, b = binary_serialization(pymodaq_object=pymodaq_object)
56
57
  return {data_key: d, "additional_payload": b}
57
58
 
@@ -69,6 +70,5 @@ def start_coordinator():
69
70
  run_coordinator()
70
71
  else:
71
72
  logger.info('Coordinator already running')
72
- except ConnectionRefusedError as e:
73
+ except ConnectionRefusedError:
73
74
  run_coordinator()
74
-
@@ -4,6 +4,7 @@ import sys
4
4
  from typing import List
5
5
 
6
6
  from qtpy import QtWidgets, QtCore
7
+ from qtpy.QtWidgets import QMessageBox, QDialog, QDialogButtonBox
7
8
 
8
9
  from pymodaq_utils.logger import set_logger, get_module_name
9
10
  from pymodaq_utils import config as config_mod
@@ -59,13 +60,13 @@ class BatchManager(ParameterManager):
59
60
  self.batch_path = path
60
61
 
61
62
  if msgbox:
62
- msgBox = QtWidgets.QMessageBox()
63
+ msgBox = QMessageBox()
63
64
  msgBox.setText("Scan Batch Manager?")
64
65
  msgBox.setInformativeText("What do you want to do?")
65
- cancel_button = msgBox.addButton(QtWidgets.QMessageBox.Cancel)
66
- new_button = msgBox.addButton("New", QtWidgets.QMessageBox.ActionRole)
67
- modify_button = msgBox.addButton('Modify', QtWidgets.QMessageBox.AcceptRole)
68
- msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel)
66
+ cancel_button = msgBox.addButton(QMessageBox.StandardButton.Cancel)
67
+ new_button = msgBox.addButton("New", QMessageBox.ButtonRole.AcceptRole)
68
+ modify_button = msgBox.addButton('Modify', QMessageBox.ButtonRole.AcceptRole)
69
+ msgBox.setDefaultButton(QMessageBox.StandardButton.Cancel)
69
70
  ret = msgBox.exec()
70
71
 
71
72
  if msgBox.clickedButton() == new_button:
@@ -154,16 +155,16 @@ class BatchManager(ParameterManager):
154
155
  def show_tree(self):
155
156
 
156
157
 
157
- dialog = QtWidgets.QDialog()
158
+ dialog = QDialog()
158
159
  dialog.setLayout(QtWidgets.QVBoxLayout())
159
160
 
160
161
  widget_all_settings = QtWidgets.QWidget()
161
162
 
162
163
  dialog.layout().addWidget(widget_all_settings)
163
- buttonBox = QtWidgets.QDialogButtonBox(parent=dialog)
164
- buttonBox.addButton('Save', buttonBox.AcceptRole)
164
+ buttonBox = QDialogButtonBox(parent=dialog)
165
+ buttonBox.addButton("Save", QDialogButtonBox.ButtonRole.AcceptRole)
165
166
  buttonBox.accepted.connect(dialog.accept)
166
- buttonBox.addButton('Cancel', buttonBox.RejectRole)
167
+ buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
167
168
  buttonBox.rejected.connect(dialog.reject)
168
169
 
169
170
  dialog.layout().addWidget(buttonBox)
@@ -186,11 +187,11 @@ class BatchManager(ParameterManager):
186
187
 
187
188
  res = dialog.exec()
188
189
 
189
- if res == dialog.Accepted:
190
+ if res == QDialog.DialogCode.Accepted:
190
191
  ioxml.parameter_to_xml_file(
191
192
  self.settings, self.batch_path.joinpath(self.settings.child('filename').value()))
192
193
 
193
- return res == dialog.Accepted
194
+ return res == QDialog.DialogCode.Accepted
194
195
 
195
196
  def set_scanner_settings(self, settings_tree: QtWidgets.QWidget):
196
197
  while True: