pymodaq 4.1.5__py3-none-any.whl → 4.2.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 +41 -4
- pymodaq/control_modules/daq_move.py +32 -73
- pymodaq/control_modules/daq_viewer.py +73 -98
- pymodaq/control_modules/daq_viewer_ui.py +2 -1
- pymodaq/control_modules/move_utility_classes.py +17 -7
- pymodaq/control_modules/utils.py +153 -5
- pymodaq/control_modules/viewer_utility_classes.py +31 -20
- pymodaq/dashboard.py +23 -5
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +4 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/bayesian_optimisation.py +673 -0
- pymodaq/extensions/bayesian/utils.py +403 -0
- pymodaq/extensions/daq_scan.py +4 -4
- pymodaq/extensions/daq_scan_ui.py +2 -1
- pymodaq/extensions/pid/pid_controller.py +12 -7
- pymodaq/extensions/pid/utils.py +9 -26
- pymodaq/extensions/utils.py +3 -0
- pymodaq/post_treatment/load_and_plot.py +42 -19
- pymodaq/resources/VERSION +1 -1
- pymodaq/resources/config_template.toml +9 -24
- pymodaq/resources/setup_plugin.py +1 -1
- pymodaq/utils/config.py +103 -5
- pymodaq/utils/daq_utils.py +35 -134
- pymodaq/utils/data.py +614 -95
- pymodaq/utils/enums.py +17 -1
- pymodaq/utils/factory.py +2 -2
- pymodaq/utils/gui_utils/custom_app.py +5 -2
- pymodaq/utils/gui_utils/dock.py +33 -4
- pymodaq/utils/gui_utils/utils.py +14 -1
- pymodaq/utils/h5modules/backends.py +9 -1
- pymodaq/utils/h5modules/data_saving.py +254 -57
- pymodaq/utils/h5modules/saving.py +1 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +172 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +170 -0
- pymodaq/utils/leco/desktop.ini +2 -0
- pymodaq/utils/leco/director_utils.py +58 -0
- pymodaq/utils/leco/leco_director.py +88 -0
- pymodaq/utils/leco/pymodaq_listener.py +279 -0
- pymodaq/utils/leco/utils.py +41 -0
- pymodaq/utils/managers/action_manager.py +20 -6
- pymodaq/utils/managers/parameter_manager.py +6 -4
- pymodaq/utils/managers/roi_manager.py +63 -54
- pymodaq/utils/math_utils.py +1 -1
- pymodaq/utils/plotting/data_viewers/__init__.py +3 -1
- pymodaq/utils/plotting/data_viewers/base.py +286 -0
- pymodaq/utils/plotting/data_viewers/viewer.py +29 -202
- pymodaq/utils/plotting/data_viewers/viewer0D.py +94 -47
- pymodaq/utils/plotting/data_viewers/viewer1D.py +341 -174
- pymodaq/utils/plotting/data_viewers/viewer1Dbasic.py +1 -1
- pymodaq/utils/plotting/data_viewers/viewer2D.py +271 -181
- pymodaq/utils/plotting/data_viewers/viewerND.py +26 -22
- pymodaq/utils/plotting/items/crosshair.py +3 -3
- pymodaq/utils/plotting/items/image.py +2 -1
- pymodaq/utils/plotting/plotter/plotter.py +94 -0
- pymodaq/utils/plotting/plotter/plotters/__init__.py +0 -0
- pymodaq/utils/plotting/plotter/plotters/matplotlib_plotters.py +134 -0
- pymodaq/utils/plotting/plotter/plotters/qt_plotters.py +78 -0
- pymodaq/utils/plotting/utils/axes_viewer.py +1 -1
- pymodaq/utils/plotting/utils/filter.py +194 -147
- pymodaq/utils/plotting/utils/lineout.py +13 -11
- pymodaq/utils/plotting/utils/plot_utils.py +89 -12
- pymodaq/utils/scanner/__init__.py +0 -3
- pymodaq/utils/scanner/scan_config.py +1 -9
- pymodaq/utils/scanner/scan_factory.py +10 -36
- pymodaq/utils/scanner/scanner.py +3 -2
- pymodaq/utils/scanner/scanners/_1d_scanners.py +7 -5
- pymodaq/utils/scanner/scanners/_2d_scanners.py +36 -49
- pymodaq/utils/scanner/scanners/sequential.py +10 -4
- pymodaq/utils/scanner/scanners/tabular.py +10 -5
- pymodaq/utils/slicing.py +1 -1
- pymodaq/utils/tcp_ip/serializer.py +38 -5
- pymodaq/utils/tcp_ip/tcp_server_client.py +25 -17
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.0.dist-info}/METADATA +4 -2
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.0.dist-info}/RECORD +78 -63
- pymodaq/resources/config_scan_template.toml +0 -42
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.0.dist-info}/WHEEL +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.0.dist-info}/entry_points.txt +0 -0
- {pymodaq-4.1.5.dist-info → pymodaq-4.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
|
|
2
|
+
try:
|
|
3
|
+
from enum import StrEnum # type: ignore
|
|
4
|
+
except ImportError:
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
class StrEnum(str, Enum):
|
|
8
|
+
pass
|
|
9
|
+
import logging
|
|
10
|
+
from threading import Event
|
|
11
|
+
from typing import Optional, Union, List, Type
|
|
12
|
+
|
|
13
|
+
from pyleco.core import COORDINATOR_PORT
|
|
14
|
+
from pyleco.utils.listener import Listener, PipeHandler
|
|
15
|
+
from qtpy.QtCore import QObject, Signal # type: ignore
|
|
16
|
+
|
|
17
|
+
from pymodaq.utils.daq_utils import ThreadCommand
|
|
18
|
+
from pymodaq.utils.parameter import ioxml
|
|
19
|
+
from pymodaq.utils.tcp_ip.serializer import DataWithAxes, SERIALIZABLE, DeSerializer
|
|
20
|
+
from pymodaq.utils.leco.utils import serialize_object
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LECOClientCommands(StrEnum):
|
|
24
|
+
LECO_CONNECTED = "leco_connected"
|
|
25
|
+
LECO_DISCONNECTED = "leco_disconnected"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LECOCommands(StrEnum):
|
|
29
|
+
CONNECT = "ini_connection"
|
|
30
|
+
QUIT = "quit"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LECOMoveCommands(StrEnum):
|
|
34
|
+
POSITION = 'position_is'
|
|
35
|
+
MOVE_DONE = 'move_done'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LECOViewerCommands(StrEnum):
|
|
39
|
+
DATA_READY = 'data_ready'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ListenerSignals(QObject):
|
|
43
|
+
cmd_signal = Signal(ThreadCommand)
|
|
44
|
+
"""
|
|
45
|
+
Possible messages sendable via `cmd_signal`
|
|
46
|
+
For all modules: Info, Infos, Info_xml, set_info
|
|
47
|
+
|
|
48
|
+
For a detector: Send Data 0D, Send Data 1D, Send Data 2D
|
|
49
|
+
|
|
50
|
+
For an actuator: move_abs, move_home, move_rel, check_position, stop_motion
|
|
51
|
+
"""
|
|
52
|
+
# message = Signal(Message)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class PymodaqPipeHandler(PipeHandler):
|
|
56
|
+
|
|
57
|
+
def __init__(self, name: str, signals: ListenerSignals, **kwargs) -> None:
|
|
58
|
+
super().__init__(name, **kwargs)
|
|
59
|
+
self.signals = signals
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ActorHandler(PymodaqPipeHandler):
|
|
63
|
+
|
|
64
|
+
def register_rpc_methods(self) -> None:
|
|
65
|
+
super().register_rpc_methods()
|
|
66
|
+
self.register_rpc_method(self.set_info)
|
|
67
|
+
self.register_rpc_method(self.send_data)
|
|
68
|
+
self.register_rpc_method(self.move_abs)
|
|
69
|
+
self.register_rpc_method(self.move_rel)
|
|
70
|
+
self.register_rpc_method(self.move_home)
|
|
71
|
+
self.register_rpc_method(self.get_actuator_value)
|
|
72
|
+
self.register_rpc_method(self.stop_motion)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def extract_dwa_object(data_string: str) -> DataWithAxes:
|
|
76
|
+
"""Extract a DataWithAxes object from the received message."""
|
|
77
|
+
desererializer = DeSerializer.from_b64_string(data_string)
|
|
78
|
+
return desererializer.dwa_deserialization()
|
|
79
|
+
|
|
80
|
+
# generic commands
|
|
81
|
+
def set_info(self, path: List[str], param_dict_str: str) -> None:
|
|
82
|
+
self.signals.cmd_signal.emit(ThreadCommand("set_info", attribute=[path, param_dict_str]))
|
|
83
|
+
|
|
84
|
+
# detector commands
|
|
85
|
+
def send_data(self, grabber_type: str = "") -> None:
|
|
86
|
+
self.signals.cmd_signal.emit(ThreadCommand(f"Send Data {grabber_type}"))
|
|
87
|
+
|
|
88
|
+
# actuator commands
|
|
89
|
+
def move_abs(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_abs", attribute=[pos]))
|
|
92
|
+
|
|
93
|
+
def move_rel(self, position: Union[float, str]) -> None:
|
|
94
|
+
pos = self.extract_dwa_object(position) if isinstance(position, str) else position
|
|
95
|
+
self.signals.cmd_signal.emit(ThreadCommand("move_rel", attribute=[pos]))
|
|
96
|
+
|
|
97
|
+
def move_home(self) -> None:
|
|
98
|
+
self.signals.cmd_signal.emit(ThreadCommand("move_home"))
|
|
99
|
+
|
|
100
|
+
def get_actuator_value(self) -> None:
|
|
101
|
+
"""Request that the actuator value is sent later on."""
|
|
102
|
+
# according to DAQ_Move, this supersedes "check_position"
|
|
103
|
+
self.signals.cmd_signal.emit(ThreadCommand("get_actuator_value"))
|
|
104
|
+
|
|
105
|
+
def stop_motion(self,) -> None:
|
|
106
|
+
# not implemented in DAQ_Move!
|
|
107
|
+
self.signals.cmd_signal.emit(ThreadCommand("stop_motion"))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# to be able to separate them later on
|
|
111
|
+
MoveActorHandler = ActorHandler
|
|
112
|
+
ViewerActorHandler = ActorHandler
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class PymodaqListener(Listener):
|
|
116
|
+
"""A Listener prepared for PyMoDAQ.
|
|
117
|
+
|
|
118
|
+
:param name: Name of this module.
|
|
119
|
+
:param host: Host name of the communication server.
|
|
120
|
+
:param port: Port number of the communication server.
|
|
121
|
+
"""
|
|
122
|
+
remote_name: str = ""
|
|
123
|
+
|
|
124
|
+
local_methods = ["pong", "set_log_level"]
|
|
125
|
+
|
|
126
|
+
def __init__(self,
|
|
127
|
+
name: str,
|
|
128
|
+
handler_class: Type[PymodaqPipeHandler] = PymodaqPipeHandler,
|
|
129
|
+
host: str = "localhost",
|
|
130
|
+
port: int = COORDINATOR_PORT,
|
|
131
|
+
logger: Optional[logging.Logger] = None,
|
|
132
|
+
timeout: float = 1,
|
|
133
|
+
**kwargs) -> None:
|
|
134
|
+
super().__init__(name, host, port, logger=logger, timeout=timeout,
|
|
135
|
+
**kwargs)
|
|
136
|
+
self.signals = ListenerSignals()
|
|
137
|
+
# self.signals.message.connect(self.handle_message)
|
|
138
|
+
self.cmd_signal = self.signals.cmd_signal
|
|
139
|
+
self._handler_class = handler_class
|
|
140
|
+
|
|
141
|
+
def _listen(self, name: str, stop_event: Event, coordinator_host: str, coordinator_port: int,
|
|
142
|
+
data_host: str, data_port: int) -> None:
|
|
143
|
+
self.message_handler = self._handler_class(name,
|
|
144
|
+
host=coordinator_host, port=coordinator_port,
|
|
145
|
+
data_host=data_host, data_port=data_port,
|
|
146
|
+
signals=self.signals,
|
|
147
|
+
)
|
|
148
|
+
self.message_handler.register_on_name_change_method(self.indicate_sign_in_out)
|
|
149
|
+
self.message_handler.listen(stop_event=stop_event)
|
|
150
|
+
|
|
151
|
+
def stop_listen(self) -> None:
|
|
152
|
+
super().stop_listen()
|
|
153
|
+
try:
|
|
154
|
+
del self.communicator
|
|
155
|
+
except AttributeError:
|
|
156
|
+
pass
|
|
157
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_DISCONNECTED))
|
|
158
|
+
|
|
159
|
+
def indicate_sign_in_out(self, full_name: str):
|
|
160
|
+
if "." in full_name:
|
|
161
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_CONNECTED))
|
|
162
|
+
else:
|
|
163
|
+
self.signals.cmd_signal.emit(ThreadCommand(LECOClientCommands.LECO_DISCONNECTED))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class ActorListener(PymodaqListener):
|
|
167
|
+
"""Listener for modules being an Actor (being remote controlled)."""
|
|
168
|
+
|
|
169
|
+
def __init__(self,
|
|
170
|
+
name: str,
|
|
171
|
+
handler_class: Type[ActorHandler] = ActorHandler,
|
|
172
|
+
host: str = "localhost",
|
|
173
|
+
port: int = COORDINATOR_PORT,
|
|
174
|
+
logger: Optional[logging.Logger] = None,
|
|
175
|
+
timeout: float = 1,
|
|
176
|
+
**kwargs) -> None:
|
|
177
|
+
super().__init__(name, handler_class=handler_class, host=host, port=port,
|
|
178
|
+
logger=logger, timeout=timeout,
|
|
179
|
+
**kwargs)
|
|
180
|
+
|
|
181
|
+
def start_listen(self) -> None:
|
|
182
|
+
super().start_listen()
|
|
183
|
+
self.message_handler.register_rpc_method(self.set_remote_name)
|
|
184
|
+
|
|
185
|
+
def set_remote_name(self, name: str) -> None:
|
|
186
|
+
"""Define what the name of the remote for answers is."""
|
|
187
|
+
self.remote_name = name
|
|
188
|
+
|
|
189
|
+
# @Slot(ThreadCommand)
|
|
190
|
+
def queue_command(self, command: ThreadCommand) -> None:
|
|
191
|
+
"""Queue a command to send it via LECO to the server."""
|
|
192
|
+
|
|
193
|
+
# generic commands
|
|
194
|
+
if command.command == LECOCommands.CONNECT:
|
|
195
|
+
try:
|
|
196
|
+
if self.thread.is_alive():
|
|
197
|
+
return # already started
|
|
198
|
+
except AttributeError:
|
|
199
|
+
pass # start later on, as there is no thread.
|
|
200
|
+
self.start_listen()
|
|
201
|
+
|
|
202
|
+
elif command.command == LECOCommands.QUIT:
|
|
203
|
+
try:
|
|
204
|
+
self.stop_listen()
|
|
205
|
+
except Exception:
|
|
206
|
+
pass
|
|
207
|
+
finally:
|
|
208
|
+
self.cmd_signal.emit(ThreadCommand('disconnected'))
|
|
209
|
+
|
|
210
|
+
elif command.command == 'update_connection':
|
|
211
|
+
# self.ipaddress = command.attribute['ipaddress']
|
|
212
|
+
# self.port = command.attribute['port']
|
|
213
|
+
pass # TODO change name?
|
|
214
|
+
|
|
215
|
+
elif command.command == LECOViewerCommands.DATA_READY:
|
|
216
|
+
# code from the original:
|
|
217
|
+
# self.data_ready(data=command.attribute)
|
|
218
|
+
# def data_ready(data): self.send_data(datas[0]['data'])
|
|
219
|
+
value = command.attribute # type: ignore
|
|
220
|
+
self.communicator.ask_rpc(
|
|
221
|
+
receiver=self.remote_name,
|
|
222
|
+
method="set_data",
|
|
223
|
+
data=serialize_object(value),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
elif command.command == 'send_info':
|
|
227
|
+
path = command.attribute['path'] # type: ignore
|
|
228
|
+
param = command.attribute['param'] # type: ignore
|
|
229
|
+
self.communicator.ask_rpc(
|
|
230
|
+
receiver=self.remote_name,
|
|
231
|
+
method="set_info",
|
|
232
|
+
path=path,
|
|
233
|
+
param_dict_str=ioxml.parameter_to_xml_string(param).decode())
|
|
234
|
+
|
|
235
|
+
elif command.command == LECOMoveCommands.POSITION:
|
|
236
|
+
value = command.attribute[0] # type: ignore
|
|
237
|
+
self.communicator.ask_rpc(receiver=self.remote_name,
|
|
238
|
+
method="set_position",
|
|
239
|
+
position=serialize_object(value),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
elif command.command == LECOMoveCommands.MOVE_DONE:
|
|
243
|
+
value = command.attribute[0] # type: ignore
|
|
244
|
+
self.communicator.ask_rpc(receiver=self.remote_name,
|
|
245
|
+
method="set_move_done",
|
|
246
|
+
position=serialize_object(value),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
elif command.command == 'x_axis':
|
|
250
|
+
value = command.attribute[0] # type: ignore
|
|
251
|
+
if isinstance(value, SERIALIZABLE):
|
|
252
|
+
self.communicator.ask_rpc(receiver=self.remote_name,
|
|
253
|
+
method="set_x_axis",
|
|
254
|
+
data=serialize_object(value),
|
|
255
|
+
)
|
|
256
|
+
elif isinstance(value, dict):
|
|
257
|
+
self.communicator.ask_rpc(receiver=self.remote_name, method="set_x_axis", **value)
|
|
258
|
+
else:
|
|
259
|
+
raise ValueError("Nothing to send!")
|
|
260
|
+
|
|
261
|
+
elif command.command == 'y_axis':
|
|
262
|
+
value = command.attribute[0] # type: ignore
|
|
263
|
+
if isinstance(value, SERIALIZABLE):
|
|
264
|
+
self.communicator.ask_rpc(receiver=self.remote_name,
|
|
265
|
+
method="set_y_axis",
|
|
266
|
+
data=serialize_object(value),
|
|
267
|
+
)
|
|
268
|
+
elif isinstance(value, dict):
|
|
269
|
+
self.communicator.ask_rpc(receiver=self.remote_name, method="set_y_axis", **value)
|
|
270
|
+
else:
|
|
271
|
+
raise ValueError("Nothing to send!")
|
|
272
|
+
|
|
273
|
+
else:
|
|
274
|
+
raise IOError('Unknown TCP client command')
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# to be able to separate them later on
|
|
278
|
+
MoveActorListener = ActorListener
|
|
279
|
+
ViewerActorListener = ActorListener
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Any, Union, get_args
|
|
4
|
+
|
|
5
|
+
# import also the DeSerializer for easier imports in dependents
|
|
6
|
+
from pymodaq.utils.tcp_ip.serializer import SERIALIZABLE, Serializer, DeSerializer # type: ignore # noqa
|
|
7
|
+
from pymodaq.utils.logger import set_logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
logger = set_logger('leco_utils')
|
|
11
|
+
|
|
12
|
+
JSON_TYPES = Union[str, int, float]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def serialize_object(pymodaq_object: Union[SERIALIZABLE, Any]) -> Union[str, Any]:
|
|
16
|
+
"""Serialize a pymodaq object, if it is not JSON compatible."""
|
|
17
|
+
if isinstance(pymodaq_object, get_args(JSON_TYPES)):
|
|
18
|
+
return pymodaq_object
|
|
19
|
+
elif isinstance(pymodaq_object, get_args(SERIALIZABLE)):
|
|
20
|
+
return Serializer(pymodaq_object).to_b64_string()
|
|
21
|
+
else:
|
|
22
|
+
raise ValueError(f"{pymodaq_object} of type '{type(pymodaq_object).__name__}' is neither "
|
|
23
|
+
"JSON serializable, nor via PyMoDAQ.")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_coordinator():
|
|
27
|
+
command = [sys.executable, '-m', 'pyleco.coordinators.coordinator']
|
|
28
|
+
subprocess.Popen(command)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def start_coordinator():
|
|
32
|
+
from pyleco.directors.director import Director
|
|
33
|
+
try:
|
|
34
|
+
with Director(actor="COORDINATOR") as director:
|
|
35
|
+
if director.communicator.namespace is None:
|
|
36
|
+
run_coordinator()
|
|
37
|
+
else:
|
|
38
|
+
logger.info('Coordinator already running')
|
|
39
|
+
except ConnectionRefusedError as e:
|
|
40
|
+
run_coordinator()
|
|
41
|
+
|
|
@@ -50,7 +50,8 @@ class QAction(QAction):
|
|
|
50
50
|
|
|
51
51
|
def addaction(name: str = '', icon_name: str = '', tip='', checkable=False, checked=False,
|
|
52
52
|
slot: Callable = None, toolbar: QtWidgets.QToolBar = None,
|
|
53
|
-
menu: QtWidgets.QMenu = None, visible=True, shortcut=None
|
|
53
|
+
menu: QtWidgets.QMenu = None, visible=True, shortcut=None,
|
|
54
|
+
enabled=True):
|
|
54
55
|
"""Create a new action and add it eventually to a toolbar and a menu
|
|
55
56
|
|
|
56
57
|
Parameters
|
|
@@ -73,6 +74,10 @@ def addaction(name: str = '', icon_name: str = '', tip='', checkable=False, chec
|
|
|
73
74
|
a menu where action should be added.
|
|
74
75
|
visible: bool
|
|
75
76
|
display or not the action in the toolbar/menu
|
|
77
|
+
shortcut: str
|
|
78
|
+
a string defining a shortcut for this action
|
|
79
|
+
enabled: bool
|
|
80
|
+
set the enabled state
|
|
76
81
|
"""
|
|
77
82
|
if icon_name != '':
|
|
78
83
|
action = QAction(create_icon(icon_name), name, None)
|
|
@@ -92,6 +97,7 @@ def addaction(name: str = '', icon_name: str = '', tip='', checkable=False, chec
|
|
|
92
97
|
if shortcut is not None:
|
|
93
98
|
action.setShortcut(shortcut)
|
|
94
99
|
action.setVisible(visible)
|
|
100
|
+
action.setEnabled(enabled)
|
|
95
101
|
return action
|
|
96
102
|
|
|
97
103
|
|
|
@@ -182,9 +188,11 @@ class ActionManager:
|
|
|
182
188
|
raise NotImplementedError(f'You have to define actions here in the following form:'
|
|
183
189
|
f'{self.setup_actions.__doc__}')
|
|
184
190
|
|
|
185
|
-
def add_action(self, short_name: str = '', name: str = '', icon_name: str = '', tip='',
|
|
191
|
+
def add_action(self, short_name: str = '', name: str = '', icon_name: str = '', tip='',
|
|
192
|
+
checkable=False,
|
|
186
193
|
checked=False, toolbar=None, menu=None,
|
|
187
|
-
visible=True, shortcut=None, auto_toolbar=True, auto_menu=True
|
|
194
|
+
visible=True, shortcut=None, auto_toolbar=True, auto_menu=True,
|
|
195
|
+
enabled=True):
|
|
188
196
|
"""Create a new action and add it to toolbar and menu
|
|
189
197
|
|
|
190
198
|
Parameters
|
|
@@ -207,7 +215,12 @@ class ActionManager:
|
|
|
207
215
|
a menu where action should be added. Actions can also be added later see *affect_to*
|
|
208
216
|
visible: bool
|
|
209
217
|
display or not the action in the toolbar/menu
|
|
210
|
-
|
|
218
|
+
auto_toolbar: bool
|
|
219
|
+
if True add this action to the defined toolbar
|
|
220
|
+
auto_menu: bool
|
|
221
|
+
if True add this action to the defined menu
|
|
222
|
+
enabled: bool
|
|
223
|
+
set the enabled state of this action
|
|
211
224
|
See Also
|
|
212
225
|
--------
|
|
213
226
|
affect_to, pymodaq.resources.QtDesigner_Ressources.Icon_Library,
|
|
@@ -219,8 +232,9 @@ class ActionManager:
|
|
|
219
232
|
if auto_menu:
|
|
220
233
|
if menu is None:
|
|
221
234
|
menu = self._menu
|
|
222
|
-
self._actions[short_name] = addaction(name, icon_name, tip, checkable=checkable,
|
|
223
|
-
|
|
235
|
+
self._actions[short_name] = addaction(name, icon_name, tip, checkable=checkable,
|
|
236
|
+
checked=checked, toolbar=toolbar, menu=menu,
|
|
237
|
+
visible=visible, shortcut=shortcut, enabled=enabled)
|
|
224
238
|
|
|
225
239
|
def add_widget(self, short_name, klass: Union[str, QtWidgets.QWidget], *args, tip='',
|
|
226
240
|
toolbar: QtWidgets.QToolBar = None, visible=True, signal_str=None, slot: Callable=None, **kwargs):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import List, Union, Dict
|
|
2
|
+
from typing import List, Union, Dict, Optional
|
|
3
3
|
|
|
4
4
|
from qtpy import QtWidgets, QtCore
|
|
5
5
|
from pymodaq.utils.managers.action_manager import ActionManager
|
|
@@ -84,7 +84,9 @@ class ParameterManager:
|
|
|
84
84
|
settings_name = 'custom_settings'
|
|
85
85
|
params = []
|
|
86
86
|
|
|
87
|
-
def __init__(self, settings_name: str = None,
|
|
87
|
+
def __init__(self, settings_name: Optional[str] = None,
|
|
88
|
+
action_list: tuple = ('save', 'update', 'load'),
|
|
89
|
+
):
|
|
88
90
|
if settings_name is None:
|
|
89
91
|
settings_name = self.settings_name
|
|
90
92
|
# create a settings tree to be shown eventually in a dock
|
|
@@ -96,7 +98,7 @@ class ParameterManager:
|
|
|
96
98
|
self._settings_tree.get_action(f'update_settings').connect_to(self.update_settings_slot)
|
|
97
99
|
self._settings_tree.get_action(f'load_settings').connect_to(self.load_settings_slot)
|
|
98
100
|
|
|
99
|
-
self.settings
|
|
101
|
+
self.settings = Parameter.create(name=settings_name, type='group', children=self.params) # create a Parameter
|
|
100
102
|
# object containing the settings defined in the preamble
|
|
101
103
|
|
|
102
104
|
@property
|
|
@@ -108,7 +110,7 @@ class ParameterManager:
|
|
|
108
110
|
return self._settings_tree.tree
|
|
109
111
|
|
|
110
112
|
@property
|
|
111
|
-
def settings(self):
|
|
113
|
+
def settings(self) -> Parameter:
|
|
112
114
|
return self._settings
|
|
113
115
|
|
|
114
116
|
@settings.setter
|