pymodaq 5.1.6__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.
- pymodaq/__init__.py +98 -0
- pymodaq/control_modules/__init__.py +1 -0
- pymodaq/control_modules/daq_move.py +1238 -0
- pymodaq/control_modules/daq_move_ui/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/factory.py +48 -0
- pymodaq/control_modules/daq_move_ui/ui_base.py +359 -0
- pymodaq/control_modules/daq_move_ui/uis/__init__.py +0 -0
- pymodaq/control_modules/daq_move_ui/uis/binary.py +139 -0
- pymodaq/control_modules/daq_move_ui/uis/original.py +120 -0
- pymodaq/control_modules/daq_move_ui/uis/relative.py +124 -0
- pymodaq/control_modules/daq_move_ui/uis/simple.py +126 -0
- pymodaq/control_modules/daq_viewer.py +1517 -0
- pymodaq/control_modules/daq_viewer_ui.py +407 -0
- pymodaq/control_modules/mocks.py +57 -0
- pymodaq/control_modules/move_utility_classes.py +1141 -0
- pymodaq/control_modules/thread_commands.py +137 -0
- pymodaq/control_modules/ui_utils.py +72 -0
- pymodaq/control_modules/utils.py +591 -0
- pymodaq/control_modules/viewer_utility_classes.py +670 -0
- pymodaq/daq_utils/__init__.py +0 -0
- pymodaq/daq_utils/daq_utils.py +6 -0
- pymodaq/dashboard.py +2396 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.aliases +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvlps +3 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.lvproj +32 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Client.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_1Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_Server_2Dgaussian.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_cmd.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_float.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_read_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_data.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_int.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_scalar.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/DAQ_TCP_send_string.vi +0 -0
- pymodaq/examples/Labview_TCP_Client/client_state.ctl +0 -0
- pymodaq/examples/Labview_TCP_Client/cmd_types.ctl +0 -0
- pymodaq/examples/__init__.py +0 -0
- pymodaq/examples/function_plotter.py +160 -0
- pymodaq/examples/nonlinearscanner.py +126 -0
- pymodaq/examples/qt_less_standalone_module.py +165 -0
- pymodaq/examples/tcp_client.py +97 -0
- pymodaq/extensions/__init__.py +25 -0
- pymodaq/extensions/adaptive/__init__.py +2 -0
- pymodaq/extensions/adaptive/adaptive_optimization.py +179 -0
- pymodaq/extensions/adaptive/loss_function/_1d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/_2d_loss_functions.py +73 -0
- pymodaq/extensions/adaptive/loss_function/__init__.py +3 -0
- pymodaq/extensions/adaptive/loss_function/loss_factory.py +110 -0
- pymodaq/extensions/adaptive/utils.py +123 -0
- pymodaq/extensions/bayesian/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/__init__.py +2 -0
- pymodaq/extensions/bayesian/acquisition/acquisition_function_factory.py +80 -0
- pymodaq/extensions/bayesian/acquisition/base_acquisition_function.py +105 -0
- pymodaq/extensions/bayesian/bayesian_optimization.py +143 -0
- pymodaq/extensions/bayesian/utils.py +180 -0
- pymodaq/extensions/console.py +73 -0
- pymodaq/extensions/daq_logger/__init__.py +1 -0
- pymodaq/extensions/daq_logger/abstract.py +52 -0
- pymodaq/extensions/daq_logger/daq_logger.py +519 -0
- pymodaq/extensions/daq_logger/db/__init__.py +0 -0
- pymodaq/extensions/daq_logger/db/db_logger.py +300 -0
- pymodaq/extensions/daq_logger/db/db_logger_models.py +100 -0
- pymodaq/extensions/daq_logger/h5logging.py +84 -0
- pymodaq/extensions/daq_scan.py +1218 -0
- pymodaq/extensions/daq_scan_ui.py +241 -0
- pymodaq/extensions/data_mixer/__init__.py +0 -0
- pymodaq/extensions/data_mixer/daq_0Dviewer_DataMixer.py +97 -0
- pymodaq/extensions/data_mixer/data_mixer.py +262 -0
- pymodaq/extensions/data_mixer/model.py +108 -0
- pymodaq/extensions/data_mixer/models/__init__.py +0 -0
- pymodaq/extensions/data_mixer/models/equation_model.py +91 -0
- pymodaq/extensions/data_mixer/models/gaussian_fit_model.py +65 -0
- pymodaq/extensions/data_mixer/parser.py +53 -0
- pymodaq/extensions/data_mixer/utils.py +23 -0
- pymodaq/extensions/h5browser.py +9 -0
- pymodaq/extensions/optimizers_base/__init__.py +0 -0
- pymodaq/extensions/optimizers_base/optimizer.py +1016 -0
- pymodaq/extensions/optimizers_base/thread_commands.py +22 -0
- pymodaq/extensions/optimizers_base/utils.py +427 -0
- pymodaq/extensions/pid/__init__.py +16 -0
- pymodaq/extensions/pid/actuator_controller.py +14 -0
- pymodaq/extensions/pid/daq_move_PID.py +154 -0
- pymodaq/extensions/pid/pid_controller.py +1016 -0
- pymodaq/extensions/pid/utils.py +189 -0
- pymodaq/extensions/utils.py +111 -0
- pymodaq/icon.ico +0 -0
- pymodaq/post_treatment/__init__.py +6 -0
- pymodaq/post_treatment/load_and_plot.py +352 -0
- pymodaq/resources/__init__.py +0 -0
- pymodaq/resources/config_template.toml +57 -0
- pymodaq/resources/preset_default.xml +1 -0
- pymodaq/resources/setup_plugin.py +73 -0
- pymodaq/splash.png +0 -0
- pymodaq/utils/__init__.py +0 -0
- pymodaq/utils/array_manipulation.py +6 -0
- pymodaq/utils/calibration_camera.py +180 -0
- pymodaq/utils/chrono_timer.py +203 -0
- pymodaq/utils/config.py +53 -0
- pymodaq/utils/conftests.py +5 -0
- pymodaq/utils/daq_utils.py +158 -0
- pymodaq/utils/data.py +128 -0
- pymodaq/utils/enums.py +6 -0
- pymodaq/utils/exceptions.py +38 -0
- pymodaq/utils/gui_utils/__init__.py +10 -0
- pymodaq/utils/gui_utils/loader_utils.py +75 -0
- pymodaq/utils/gui_utils/utils.py +18 -0
- pymodaq/utils/gui_utils/widgets/lcd.py +8 -0
- pymodaq/utils/h5modules/__init__.py +2 -0
- pymodaq/utils/h5modules/module_saving.py +526 -0
- pymodaq/utils/leco/__init__.py +25 -0
- pymodaq/utils/leco/daq_move_LECODirector.py +217 -0
- pymodaq/utils/leco/daq_xDviewer_LECODirector.py +163 -0
- pymodaq/utils/leco/director_utils.py +74 -0
- pymodaq/utils/leco/leco_director.py +166 -0
- pymodaq/utils/leco/pymodaq_listener.py +364 -0
- pymodaq/utils/leco/rpc_method_definitions.py +43 -0
- pymodaq/utils/leco/utils.py +74 -0
- pymodaq/utils/logger.py +6 -0
- pymodaq/utils/managers/__init__.py +0 -0
- pymodaq/utils/managers/batchscan_manager.py +346 -0
- pymodaq/utils/managers/modules_manager.py +589 -0
- pymodaq/utils/managers/overshoot_manager.py +242 -0
- pymodaq/utils/managers/preset_manager.py +229 -0
- pymodaq/utils/managers/preset_manager_utils.py +262 -0
- pymodaq/utils/managers/remote_manager.py +484 -0
- pymodaq/utils/math_utils.py +6 -0
- pymodaq/utils/messenger.py +6 -0
- pymodaq/utils/parameter/__init__.py +10 -0
- pymodaq/utils/parameter/utils.py +6 -0
- pymodaq/utils/scanner/__init__.py +5 -0
- pymodaq/utils/scanner/scan_config.py +16 -0
- pymodaq/utils/scanner/scan_factory.py +259 -0
- pymodaq/utils/scanner/scan_selector.py +477 -0
- pymodaq/utils/scanner/scanner.py +324 -0
- pymodaq/utils/scanner/scanners/_1d_scanners.py +174 -0
- pymodaq/utils/scanner/scanners/_2d_scanners.py +299 -0
- pymodaq/utils/scanner/scanners/__init__.py +1 -0
- pymodaq/utils/scanner/scanners/sequential.py +224 -0
- pymodaq/utils/scanner/scanners/tabular.py +319 -0
- pymodaq/utils/scanner/utils.py +110 -0
- pymodaq/utils/svg/__init__.py +6 -0
- pymodaq/utils/svg/svg_renderer.py +20 -0
- pymodaq/utils/svg/svg_view.py +35 -0
- pymodaq/utils/svg/svg_viewer2D.py +50 -0
- pymodaq/utils/tcp_ip/__init__.py +6 -0
- pymodaq/utils/tcp_ip/mysocket.py +12 -0
- pymodaq/utils/tcp_ip/serializer.py +13 -0
- pymodaq/utils/tcp_ip/tcp_server_client.py +772 -0
- pymodaq-5.1.6.dist-info/METADATA +238 -0
- pymodaq-5.1.6.dist-info/RECORD +154 -0
- pymodaq-5.1.6.dist-info/WHEEL +4 -0
- pymodaq-5.1.6.dist-info/entry_points.txt +7 -0
- pymodaq-5.1.6.dist-info/licenses/LICENSE +21 -0
pymodaq/dashboard.py
ADDED
|
@@ -0,0 +1,2396 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import datetime
|
|
6
|
+
import subprocess
|
|
7
|
+
import logging
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from importlib import import_module
|
|
10
|
+
from packaging import version as version_mod
|
|
11
|
+
from typing import Tuple, Union, List, Any, TYPE_CHECKING, Sequence
|
|
12
|
+
import argparse
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from qtpy import QtGui, QtWidgets, QtCore
|
|
16
|
+
from qtpy.QtCore import Qt, QThread, Signal, QSize
|
|
17
|
+
from qtpy.QtWidgets import (
|
|
18
|
+
QTableWidget,
|
|
19
|
+
QTableWidgetItem,
|
|
20
|
+
QLabel,
|
|
21
|
+
QDialogButtonBox,
|
|
22
|
+
QMessageBox,
|
|
23
|
+
)
|
|
24
|
+
from time import perf_counter
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
from pymodaq_plugin_manager.manager import PluginManager
|
|
28
|
+
from pymodaq_plugin_manager.validate import get_pypi_pymodaq
|
|
29
|
+
|
|
30
|
+
from pymodaq_utils.logger import set_logger, get_module_name
|
|
31
|
+
from pymodaq_utils import utils
|
|
32
|
+
from pymodaq_utils.utils import get_version, find_dict_in_list_from_key_val
|
|
33
|
+
from pymodaq_utils import config as configmod
|
|
34
|
+
from pymodaq_utils.enums import BaseEnum
|
|
35
|
+
|
|
36
|
+
from pymodaq_gui.parameter import ParameterTree, Parameter
|
|
37
|
+
from pymodaq_gui.utils import DockArea, Dock, select_file
|
|
38
|
+
import pymodaq_gui.utils.layout as layout_mod
|
|
39
|
+
from pymodaq_gui.messenger import messagebox
|
|
40
|
+
from pymodaq_gui.parameter import utils as putils
|
|
41
|
+
from pymodaq_gui.managers.roi_manager import ROISaver
|
|
42
|
+
from pymodaq_gui.utils.custom_app import CustomApp
|
|
43
|
+
|
|
44
|
+
from pymodaq.utils.managers.modules_manager import ModulesManager
|
|
45
|
+
from pymodaq.utils.managers.preset_manager import PresetManager
|
|
46
|
+
from pymodaq.utils.managers.overshoot_manager import OvershootManager
|
|
47
|
+
from pymodaq.utils.managers.remote_manager import RemoteManager
|
|
48
|
+
from pymodaq.utils.exceptions import DetectorError, ActuatorError, MasterSlaveError
|
|
49
|
+
from pymodaq.utils.daq_utils import get_instrument_plugins
|
|
50
|
+
from pymodaq.utils.leco.utils import start_coordinator
|
|
51
|
+
from pymodaq.utils import config as config_mod_pymodaq
|
|
52
|
+
|
|
53
|
+
from pymodaq.control_modules.daq_move import DAQ_Move
|
|
54
|
+
from pymodaq.control_modules.daq_viewer import DAQ_Viewer
|
|
55
|
+
from pymodaq.control_modules.daq_move_ui.factory import ActuatorUIFactory
|
|
56
|
+
from pymodaq_gui.utils.splash import get_splash_sc
|
|
57
|
+
from pymodaq import extensions as extmod
|
|
58
|
+
from pymodaq.utils.config import Config as ControlModulesConfig
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
logger = set_logger(get_module_name(__file__))
|
|
62
|
+
|
|
63
|
+
config_utils = configmod.Config()
|
|
64
|
+
config = ControlModulesConfig()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
get_instrument_plugins()
|
|
68
|
+
extensions = extmod.get_extensions()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
local_path = configmod.get_set_local_dir()
|
|
72
|
+
now = datetime.datetime.now()
|
|
73
|
+
preset_path = config_mod_pymodaq.get_set_preset_path()
|
|
74
|
+
log_path = configmod.get_set_log_path()
|
|
75
|
+
layout_path = config_mod_pymodaq.get_set_layout_path()
|
|
76
|
+
overshoot_path = config_mod_pymodaq.get_set_overshoot_path()
|
|
77
|
+
roi_path = config_mod_pymodaq.get_set_roi_path()
|
|
78
|
+
remote_path = config_mod_pymodaq.get_set_remote_path()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ManagerEnums(BaseEnum):
|
|
82
|
+
preset = 0
|
|
83
|
+
remote = 1
|
|
84
|
+
overshoot = 2
|
|
85
|
+
roi = 3
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class PymodaqUpdateTableWidget(QTableWidget):
|
|
89
|
+
"""
|
|
90
|
+
A class to represent PyMoDAQ and its subpackages'
|
|
91
|
+
available updates as a table.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(self):
|
|
95
|
+
super().__init__()
|
|
96
|
+
self._row = 0
|
|
97
|
+
|
|
98
|
+
def setHorizontalHeaderLabels(self, labels):
|
|
99
|
+
super().setHorizontalHeaderLabels(labels)
|
|
100
|
+
self.setColumnCount(len(labels))
|
|
101
|
+
|
|
102
|
+
def append_row(self, package, current_version, available_version):
|
|
103
|
+
# Add labels
|
|
104
|
+
self.setItem(self._row, 0, QTableWidgetItem(str(package)))
|
|
105
|
+
self.setItem(self._row, 1, QTableWidgetItem(str(current_version)))
|
|
106
|
+
self.setItem(self._row, 2, QTableWidgetItem(str(available_version)))
|
|
107
|
+
|
|
108
|
+
self._row += 1
|
|
109
|
+
|
|
110
|
+
def sizeHint(self):
|
|
111
|
+
self.resizeColumnsToContents()
|
|
112
|
+
self.resizeRowsToContents()
|
|
113
|
+
|
|
114
|
+
# Compute the size to adapt the window (header + borders + sum of all the elements)
|
|
115
|
+
width = (
|
|
116
|
+
self.verticalHeader().width()
|
|
117
|
+
+ self.frameWidth() * 2
|
|
118
|
+
+ sum([self.columnWidth(i) for i in range(self.columnCount())])
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
height = (
|
|
122
|
+
self.horizontalHeader().height()
|
|
123
|
+
+ self.frameWidth() * 2
|
|
124
|
+
+ sum([self.rowHeight(i) for i in range(self.rowCount())])
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return QSize(width, height)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class DashBoard(CustomApp):
|
|
131
|
+
"""
|
|
132
|
+
Main class initializing a DashBoard interface to display det and move modules and logger"""
|
|
133
|
+
|
|
134
|
+
status_signal = Signal(str)
|
|
135
|
+
preset_loaded_signal = Signal(bool)
|
|
136
|
+
new_preset_created = Signal()
|
|
137
|
+
|
|
138
|
+
settings_name = "dashboard_settings"
|
|
139
|
+
_splash_sc = None
|
|
140
|
+
|
|
141
|
+
params = [
|
|
142
|
+
{
|
|
143
|
+
"title": "Log level",
|
|
144
|
+
"name": "log_level",
|
|
145
|
+
"type": "list",
|
|
146
|
+
"value": config_utils("general", "debug_levels")[0],
|
|
147
|
+
"limits": config_utils("general", "debug_levels"),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"title": "Loaded presets",
|
|
151
|
+
"name": "loaded_files",
|
|
152
|
+
"type": "group",
|
|
153
|
+
"children": [
|
|
154
|
+
{
|
|
155
|
+
"title": "Preset file",
|
|
156
|
+
"name": "preset_file",
|
|
157
|
+
"type": "str",
|
|
158
|
+
"value": "",
|
|
159
|
+
"readonly": True,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"title": "Overshoot file",
|
|
163
|
+
"name": "overshoot_file",
|
|
164
|
+
"type": "str",
|
|
165
|
+
"value": "",
|
|
166
|
+
"readonly": True,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"title": "Layout file",
|
|
170
|
+
"name": "layout_file",
|
|
171
|
+
"type": "str",
|
|
172
|
+
"value": "",
|
|
173
|
+
"readonly": True,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"title": "ROI file",
|
|
177
|
+
"name": "roi_file",
|
|
178
|
+
"type": "str",
|
|
179
|
+
"value": "",
|
|
180
|
+
"readonly": True,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"title": "Remote file",
|
|
184
|
+
"name": "remote_file",
|
|
185
|
+
"type": "str",
|
|
186
|
+
"value": "",
|
|
187
|
+
"readonly": True,
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"title": "Actuators Init.",
|
|
193
|
+
"name": "actuators",
|
|
194
|
+
"type": "group",
|
|
195
|
+
"children": [],
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"title": "Detectors Init.",
|
|
199
|
+
"name": "detectors",
|
|
200
|
+
"type": "group",
|
|
201
|
+
"children": [],
|
|
202
|
+
},
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
def __init__(self, dockarea):
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
parent: (dockarea) instance of the modified pyqtgraph Dockarea (see daq_utils)
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
super().__init__(dockarea)
|
|
214
|
+
|
|
215
|
+
logger.info("Initializing Dashboard")
|
|
216
|
+
self.extra_params = []
|
|
217
|
+
self.preset_path = preset_path
|
|
218
|
+
self.wait_time = 1000
|
|
219
|
+
self.scan_module = None
|
|
220
|
+
self.log_module = None
|
|
221
|
+
self.pid_module = None
|
|
222
|
+
self.pid_window = None
|
|
223
|
+
self.retriever_module = None
|
|
224
|
+
self.database_module = None
|
|
225
|
+
self.extensions = dict([])
|
|
226
|
+
self.extension_windows = []
|
|
227
|
+
|
|
228
|
+
self.dockarea.dock_signal.connect(self.save_layout_state_auto)
|
|
229
|
+
|
|
230
|
+
self.title = ""
|
|
231
|
+
|
|
232
|
+
self.overshoot_manager = None
|
|
233
|
+
self.preset_manager = None
|
|
234
|
+
self.roi_saver: ROISaver = None
|
|
235
|
+
|
|
236
|
+
self.remote_timer = QtCore.QTimer()
|
|
237
|
+
self.remote_manager = None
|
|
238
|
+
self.shortcuts = dict([])
|
|
239
|
+
self.joysticks = dict([])
|
|
240
|
+
self.ispygame_init = False
|
|
241
|
+
|
|
242
|
+
self.modules_manager: ModulesManager = None
|
|
243
|
+
|
|
244
|
+
self.overshoot = False
|
|
245
|
+
self.preset_file = None
|
|
246
|
+
self.actuators_modules = []
|
|
247
|
+
self.detector_modules = []
|
|
248
|
+
|
|
249
|
+
self.compact_actuator_dock: Dock = None
|
|
250
|
+
|
|
251
|
+
self.setup_ui()
|
|
252
|
+
|
|
253
|
+
self.mainwindow.setVisible(True)
|
|
254
|
+
|
|
255
|
+
logger.info("Dashboard Initialized")
|
|
256
|
+
|
|
257
|
+
if config_utils("general", "check_version"):
|
|
258
|
+
if self.check_update(show=False):
|
|
259
|
+
sys.exit(0)
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def splash_sc(self) -> QtWidgets.QSplashScreen:
|
|
263
|
+
if not hasattr(self, "_splash_sc") or self._splash_sc is None:
|
|
264
|
+
self._splash_sc = get_splash_sc()
|
|
265
|
+
return self._splash_sc
|
|
266
|
+
|
|
267
|
+
def set_preset_path(self, path):
|
|
268
|
+
self.preset_path = path
|
|
269
|
+
self.set_extra_preset_params(self.extra_params)
|
|
270
|
+
self.create_menu(self.menubar)
|
|
271
|
+
|
|
272
|
+
def set_extra_preset_params(self, params, param_options=[]):
|
|
273
|
+
self.extra_params = params
|
|
274
|
+
self.preset_manager = PresetManager(
|
|
275
|
+
path=self.preset_path, extra_params=params, param_options=param_options
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
def add_status(self, txt):
|
|
279
|
+
"""
|
|
280
|
+
Add the QListWisgetItem initialized with txt informations to the User Interface
|
|
281
|
+
logger_list and to the save_parameters.logger array.
|
|
282
|
+
|
|
283
|
+
=============== =========== ======================
|
|
284
|
+
**Parameters** **Type** **Description**
|
|
285
|
+
*txt* string the log info to add.
|
|
286
|
+
=============== =========== ======================
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
now = datetime.datetime.now()
|
|
290
|
+
new_item = QtWidgets.QListWidgetItem(
|
|
291
|
+
now.strftime("%Y/%m/%d %H:%M:%S") + ": " + txt
|
|
292
|
+
)
|
|
293
|
+
self.logger_list.addItem(new_item)
|
|
294
|
+
|
|
295
|
+
except Exception as e:
|
|
296
|
+
logger.exception(str(e))
|
|
297
|
+
|
|
298
|
+
def remove_detectors(self, detector_modules: List[DAQ_Viewer] = None):
|
|
299
|
+
"""
|
|
300
|
+
Remove the given list of detectors from the dashboard.
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
detector_modules: List[DAQ_Viewer]
|
|
304
|
+
List of DAQ_Viewer instances to be removed.
|
|
305
|
+
"""
|
|
306
|
+
if detector_modules is None:
|
|
307
|
+
detector_modules = []
|
|
308
|
+
try:
|
|
309
|
+
for detector_module in detector_modules:
|
|
310
|
+
if detector_module in self.detector_modules:
|
|
311
|
+
self.detector_modules.remove(detector_module)
|
|
312
|
+
detector_module.quit_fun()
|
|
313
|
+
dock = self.dockarea.docks.get(
|
|
314
|
+
f"{detector_module.title} settings", None
|
|
315
|
+
)
|
|
316
|
+
if dock:
|
|
317
|
+
dock.close()
|
|
318
|
+
dock = self.dockarea.docks.get(f"{detector_module.title} viewer", None)
|
|
319
|
+
if dock:
|
|
320
|
+
dock.close()
|
|
321
|
+
self.update_module_manager()
|
|
322
|
+
except Exception as e:
|
|
323
|
+
logger.exception(str(e))
|
|
324
|
+
|
|
325
|
+
def remove_actuators(self, actuator_modules: List[DAQ_Move] = None):
|
|
326
|
+
"""
|
|
327
|
+
Remove the given list of actuators from the dashboard.
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
actuator_modules: List[DAQ_Move]
|
|
331
|
+
List of DAQ_Move instances to be removed.
|
|
332
|
+
"""
|
|
333
|
+
if actuator_modules is None:
|
|
334
|
+
actuator_modules = []
|
|
335
|
+
try:
|
|
336
|
+
for actuator_module in actuator_modules:
|
|
337
|
+
if actuator_module in self.actuators_modules:
|
|
338
|
+
self.actuators_modules.remove(actuator_module)
|
|
339
|
+
actuator_module.quit_fun()
|
|
340
|
+
dock = self.dockarea.docks.get(actuator_module.title, None)
|
|
341
|
+
if dock:
|
|
342
|
+
dock.close()
|
|
343
|
+
self.update_module_manager()
|
|
344
|
+
except Exception as e:
|
|
345
|
+
logger.exception(str(e))
|
|
346
|
+
|
|
347
|
+
def get_docks_from_modules(
|
|
348
|
+
self, modules: Sequence[Union["DAQ_Move", "DAQ_Viewer"]]
|
|
349
|
+
) -> List[Dock]:
|
|
350
|
+
"""
|
|
351
|
+
Get a list of Dock instances from the given modules.
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
modules: Sequence[DAQ_Move/DAQ_Viewer]
|
|
356
|
+
Sequence of DAQ_Move or DAQ_Viewer instances.
|
|
357
|
+
|
|
358
|
+
Returns
|
|
359
|
+
-------
|
|
360
|
+
List[Dock]
|
|
361
|
+
List of Dock instances corresponding to the given modules.
|
|
362
|
+
"""
|
|
363
|
+
docks = []
|
|
364
|
+
for module in modules:
|
|
365
|
+
if hasattr(module, "dock"):
|
|
366
|
+
docks.append(module.dock)
|
|
367
|
+
return docks
|
|
368
|
+
|
|
369
|
+
def remove_modules(
|
|
370
|
+
self, modules: List[Union["DAQ_Move", "DAQ_Viewer", "str"]] = None
|
|
371
|
+
):
|
|
372
|
+
"""
|
|
373
|
+
Remove the given list of actuators/detectors from the dashboard.
|
|
374
|
+
|
|
375
|
+
Parameters
|
|
376
|
+
----------
|
|
377
|
+
modules: List[DAQ_Move/DAQ_Viewer]
|
|
378
|
+
List of DAQ_Move/DAQ_Viewer instances to be removed.
|
|
379
|
+
"""
|
|
380
|
+
if modules is None:
|
|
381
|
+
modules = []
|
|
382
|
+
try:
|
|
383
|
+
actuators_modules = []
|
|
384
|
+
detector_modules = []
|
|
385
|
+
for module in modules:
|
|
386
|
+
if isinstance(
|
|
387
|
+
module, DAQ_Move
|
|
388
|
+
): # Test if module is an instance of DAQ_Move
|
|
389
|
+
actuators_modules.append(module)
|
|
390
|
+
elif isinstance(
|
|
391
|
+
module, DAQ_Viewer
|
|
392
|
+
): # Test if module is an instance of DAQ_Viewer
|
|
393
|
+
detector_modules.append(module)
|
|
394
|
+
if isinstance(
|
|
395
|
+
module, str
|
|
396
|
+
): # Test if module is a string (name of the module)
|
|
397
|
+
actuators_modules.extend(
|
|
398
|
+
self.modules_manager.get_mods_from_names(
|
|
399
|
+
[
|
|
400
|
+
module,
|
|
401
|
+
],
|
|
402
|
+
"act",
|
|
403
|
+
) # For actuators
|
|
404
|
+
)
|
|
405
|
+
detector_modules.extend(
|
|
406
|
+
self.modules_manager.get_mods_from_names(
|
|
407
|
+
[
|
|
408
|
+
module,
|
|
409
|
+
],
|
|
410
|
+
"det",
|
|
411
|
+
) # For detectors
|
|
412
|
+
)
|
|
413
|
+
if (hasattr(self, "actuators_modules")) & (
|
|
414
|
+
self.actuators_modules is not None
|
|
415
|
+
): # Remove actuators
|
|
416
|
+
self.remove_actuators(actuators_modules)
|
|
417
|
+
if (hasattr(self, "detector_modules")) & (
|
|
418
|
+
self.detector_modules is not None
|
|
419
|
+
): # Remove detectors
|
|
420
|
+
self.remove_detectors(detector_modules)
|
|
421
|
+
except Exception as e:
|
|
422
|
+
logger.exception(str(e))
|
|
423
|
+
|
|
424
|
+
def clear_move_det_controllers(self):
|
|
425
|
+
"""
|
|
426
|
+
Remove all docks containing Moves or Viewers.
|
|
427
|
+
|
|
428
|
+
See Also
|
|
429
|
+
--------
|
|
430
|
+
quit_fun, update_status
|
|
431
|
+
"""
|
|
432
|
+
try:
|
|
433
|
+
# remove all docks containing Moves or Viewers
|
|
434
|
+
if hasattr(self, "actuators_modules") & (
|
|
435
|
+
self.actuators_modules is not None
|
|
436
|
+
):
|
|
437
|
+
for module in self.actuators_modules:
|
|
438
|
+
module.quit_fun()
|
|
439
|
+
self.actuators_modules = []
|
|
440
|
+
|
|
441
|
+
if hasattr(self, "detector_modules") & (self.detector_modules is not None):
|
|
442
|
+
for module in self.detector_modules:
|
|
443
|
+
module.quit_fun()
|
|
444
|
+
self.detector_modules = []
|
|
445
|
+
except Exception as e:
|
|
446
|
+
logger.exception(str(e))
|
|
447
|
+
|
|
448
|
+
def load_scan_module(self, win=None):
|
|
449
|
+
if win is None:
|
|
450
|
+
win = QtWidgets.QMainWindow()
|
|
451
|
+
area = DockArea()
|
|
452
|
+
win.setWindowFlags(
|
|
453
|
+
Qt.Window
|
|
454
|
+
| Qt.WindowTitleHint
|
|
455
|
+
| Qt.WindowMinimizeButtonHint
|
|
456
|
+
| Qt.WindowMaximizeButtonHint
|
|
457
|
+
)
|
|
458
|
+
win.setCentralWidget(area)
|
|
459
|
+
win.setWindowTitle("Scanner")
|
|
460
|
+
self.scan_module = extmod.DAQScan(dockarea=area, dashboard=self)
|
|
461
|
+
self.extensions["DAQScan"] = self.scan_module
|
|
462
|
+
self.scan_module.status_signal.connect(self.add_status)
|
|
463
|
+
# win.setWindowTitle("DAQScan")
|
|
464
|
+
win.show()
|
|
465
|
+
return self.scan_module
|
|
466
|
+
|
|
467
|
+
def load_log_module(self, win=None):
|
|
468
|
+
if win is None:
|
|
469
|
+
win = QtWidgets.QMainWindow()
|
|
470
|
+
area = DockArea()
|
|
471
|
+
win.setWindowFlags(
|
|
472
|
+
Qt.Window
|
|
473
|
+
| Qt.WindowTitleHint
|
|
474
|
+
| Qt.WindowMinimizeButtonHint
|
|
475
|
+
| Qt.WindowMaximizeButtonHint
|
|
476
|
+
)
|
|
477
|
+
win.setCentralWidget(area)
|
|
478
|
+
win.setWindowTitle("Logger")
|
|
479
|
+
self.log_module = extmod.DAQ_Logger(dockarea=area, dashboard=self)
|
|
480
|
+
self.extensions["DAQ_Logger"] = self.log_module
|
|
481
|
+
self.log_module.status_signal.connect(self.add_status)
|
|
482
|
+
win.show()
|
|
483
|
+
return self.log_module
|
|
484
|
+
|
|
485
|
+
def load_pid_module(self, win=None):
|
|
486
|
+
if win is None:
|
|
487
|
+
self.pid_window = QtWidgets.QMainWindow()
|
|
488
|
+
else:
|
|
489
|
+
self.pid_window = win
|
|
490
|
+
self.pid_window.setWindowFlags(
|
|
491
|
+
Qt.Window
|
|
492
|
+
| Qt.WindowTitleHint
|
|
493
|
+
| Qt.WindowMinimizeButtonHint
|
|
494
|
+
| Qt.WindowMaximizeButtonHint
|
|
495
|
+
)
|
|
496
|
+
dockarea = DockArea()
|
|
497
|
+
self.pid_window.setCentralWidget(dockarea)
|
|
498
|
+
self.pid_window.setWindowTitle("PID Controller")
|
|
499
|
+
self.pid_module = extmod.DAQ_PID(dockarea=dockarea, dashboard=self)
|
|
500
|
+
self.extensions["DAQ_PID"] = self.pid_module
|
|
501
|
+
self.pid_window.show()
|
|
502
|
+
return self.pid_module
|
|
503
|
+
|
|
504
|
+
def load_console(self):
|
|
505
|
+
dock_console = Dock("QTConsole")
|
|
506
|
+
self.dockarea.addDock(dock_console, "bottom")
|
|
507
|
+
qtconsole = extmod.QtConsole(
|
|
508
|
+
style_sheet=config_utils("style", "syntax_highlighting"),
|
|
509
|
+
syntax_style=config_utils("style", "syntax_highlighting"),
|
|
510
|
+
custom_banner=extmod.console.BANNER,
|
|
511
|
+
)
|
|
512
|
+
dock_console.addWidget(qtconsole)
|
|
513
|
+
self.extensions["qtconsole"] = qtconsole
|
|
514
|
+
|
|
515
|
+
qtconsole.push_variables(dict(dashboard=self, mods=self.modules_manager, np=np))
|
|
516
|
+
|
|
517
|
+
return qtconsole
|
|
518
|
+
|
|
519
|
+
def load_bayesian(self, win=None):
|
|
520
|
+
if win is None:
|
|
521
|
+
self.bayesian_window = QtWidgets.QMainWindow()
|
|
522
|
+
else:
|
|
523
|
+
self.bayesian_window = win
|
|
524
|
+
self.bayesian_window.setWindowFlags(
|
|
525
|
+
Qt.Window
|
|
526
|
+
| Qt.WindowTitleHint
|
|
527
|
+
| Qt.WindowMinimizeButtonHint
|
|
528
|
+
| Qt.WindowMaximizeButtonHint
|
|
529
|
+
)
|
|
530
|
+
dockarea = DockArea()
|
|
531
|
+
self.bayesian_window.setCentralWidget(dockarea)
|
|
532
|
+
self.bayesian_window.setWindowTitle("Bayesian Optimiser")
|
|
533
|
+
self.bayesian_module = extmod.BayesianOptimization(
|
|
534
|
+
dockarea=dockarea, dashboard=self
|
|
535
|
+
)
|
|
536
|
+
self.extensions["bayesian"] = self.bayesian_module
|
|
537
|
+
|
|
538
|
+
if self.bayesian_module.validate_config():
|
|
539
|
+
self.bayesian_window.show()
|
|
540
|
+
else:
|
|
541
|
+
messagebox(
|
|
542
|
+
severity="critical",
|
|
543
|
+
title="Bayesian Optimisation error",
|
|
544
|
+
text=f"""
|
|
545
|
+
<p>Saved Bayesian Optimisation configuration file is not compatible anymore.</p>
|
|
546
|
+
<p>Please delete the file at <b>{self.bayesian_module.config_path}</b>.</p>
|
|
547
|
+
""",
|
|
548
|
+
)
|
|
549
|
+
self.bayesian_module.quit()
|
|
550
|
+
return self.bayesian_module
|
|
551
|
+
|
|
552
|
+
def load_adaptive(self, win=None):
|
|
553
|
+
if win is None:
|
|
554
|
+
self.adaptive_window = QtWidgets.QMainWindow()
|
|
555
|
+
else:
|
|
556
|
+
self.adaptive_window = win
|
|
557
|
+
self.adaptive_window.setWindowFlags(
|
|
558
|
+
Qt.Window
|
|
559
|
+
| Qt.WindowTitleHint
|
|
560
|
+
| Qt.WindowMinimizeButtonHint
|
|
561
|
+
| Qt.WindowMaximizeButtonHint
|
|
562
|
+
)
|
|
563
|
+
dockarea = DockArea()
|
|
564
|
+
self.adaptive_window.setCentralWidget(dockarea)
|
|
565
|
+
self.adaptive_window.setWindowTitle("Adaptive Scan")
|
|
566
|
+
self.adaptive_module = extmod.AdaptiveOptimisation(
|
|
567
|
+
dockarea=dockarea, dashboard=self
|
|
568
|
+
)
|
|
569
|
+
self.extensions["adaptive"] = self.adaptive_module
|
|
570
|
+
|
|
571
|
+
if self.adaptive_module.validate_config():
|
|
572
|
+
self.adaptive_window.show()
|
|
573
|
+
else:
|
|
574
|
+
messagebox(
|
|
575
|
+
severity="critical",
|
|
576
|
+
title="Adaptive Optimisation error",
|
|
577
|
+
text=f"""
|
|
578
|
+
<p>Saved Adaptive Optimisation configuration file is not compatible anymore.</p>
|
|
579
|
+
<p>Please delete the file at <b>{self.adaptive_module.config_path}</b>.</p>
|
|
580
|
+
""",
|
|
581
|
+
)
|
|
582
|
+
self.adaptive_module.quit()
|
|
583
|
+
return self.adaptive_module
|
|
584
|
+
|
|
585
|
+
def load_datamixer(self, win=None):
|
|
586
|
+
if win is None:
|
|
587
|
+
self.datamixer_window = QtWidgets.QMainWindow()
|
|
588
|
+
else:
|
|
589
|
+
self.datamixer_window = win
|
|
590
|
+
self.datamixer_window.setWindowFlags(
|
|
591
|
+
Qt.Window
|
|
592
|
+
| Qt.WindowTitleHint
|
|
593
|
+
| Qt.WindowMinimizeButtonHint
|
|
594
|
+
| Qt.WindowMaximizeButtonHint
|
|
595
|
+
)
|
|
596
|
+
dockarea = DockArea()
|
|
597
|
+
self.datamixer_window.setCentralWidget(dockarea)
|
|
598
|
+
self.datamixer_window.setWindowTitle("DataMixer")
|
|
599
|
+
self.datamixer_module = extmod.DataMixer(
|
|
600
|
+
parent=dockarea, dashboard=self
|
|
601
|
+
)
|
|
602
|
+
self.extensions["datamixer"] = self.datamixer_module
|
|
603
|
+
|
|
604
|
+
if self.datamixer_module.validate_config():
|
|
605
|
+
self.datamixer_window.show()
|
|
606
|
+
else:
|
|
607
|
+
messagebox(
|
|
608
|
+
severity="critical",
|
|
609
|
+
title="DataMixer error",
|
|
610
|
+
text=f"""
|
|
611
|
+
<p>Saved DataMixer configuration file is not compatible anymore.</p>
|
|
612
|
+
<p>Please delete the file at <b>{self.datamixer_module.config_path}</b>.</p>
|
|
613
|
+
""",
|
|
614
|
+
)
|
|
615
|
+
self.datamixer_module.quit()
|
|
616
|
+
return self.datamixer_module
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def load_extension_from_name(self, name: str) -> dict:
|
|
620
|
+
return self.load_extensions_module(
|
|
621
|
+
find_dict_in_list_from_key_val(extensions, "name", name)
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
def load_extensions_module(self, ext: dict):
|
|
625
|
+
"""Init and load an extension from a plugin package
|
|
626
|
+
|
|
627
|
+
ext: dict
|
|
628
|
+
dictionary containing info on the extension plugin package and class to be loaded,
|
|
629
|
+
it contains four
|
|
630
|
+
keys:
|
|
631
|
+
|
|
632
|
+
* pkg: the name of the plugin package
|
|
633
|
+
* module: the module name where your extension class is defined
|
|
634
|
+
* class_name: the name of the class defining the extension
|
|
635
|
+
* name: a nice name for your extension to be displayed in the menu
|
|
636
|
+
|
|
637
|
+
See Also
|
|
638
|
+
--------
|
|
639
|
+
pymodaq.extensions.utils.get_extensions
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
self.extension_windows.append(QtWidgets.QMainWindow())
|
|
643
|
+
area = DockArea()
|
|
644
|
+
self.extension_windows[-1].setCentralWidget(area)
|
|
645
|
+
self.extension_windows[-1].resize(1000, 500)
|
|
646
|
+
self.extension_windows[-1].setWindowTitle(ext["name"])
|
|
647
|
+
module = import_module(f"{ext['pkg']}.extensions.{ext['module']}")
|
|
648
|
+
klass = getattr(module, ext["class_name"])
|
|
649
|
+
self.extensions[ext["class_name"]] = klass(area, dashboard=self)
|
|
650
|
+
self.extension_windows[-1].show()
|
|
651
|
+
return self.extensions[ext["class_name"]]
|
|
652
|
+
|
|
653
|
+
def setup_actions(self):
|
|
654
|
+
self.add_action(
|
|
655
|
+
"log", "Log File", "", "Show Log File in default editor", auto_toolbar=False
|
|
656
|
+
)
|
|
657
|
+
self.add_action("quit", "Quit", "close2", "Quit program")
|
|
658
|
+
self.toolbar.addSeparator()
|
|
659
|
+
self.add_action(
|
|
660
|
+
"config_utils",
|
|
661
|
+
"Utils Config.",
|
|
662
|
+
"tree",
|
|
663
|
+
tip="Show utility configuration file",
|
|
664
|
+
)
|
|
665
|
+
self.add_action(
|
|
666
|
+
"config",
|
|
667
|
+
"Controls/Extensions Config.",
|
|
668
|
+
"tree",
|
|
669
|
+
tip="Show Control Modules and Extensions configuration file",
|
|
670
|
+
)
|
|
671
|
+
self.add_action(
|
|
672
|
+
"restart", "Restart", "", "Restart the Dashboard", auto_toolbar=False
|
|
673
|
+
)
|
|
674
|
+
self.add_action(
|
|
675
|
+
"leco",
|
|
676
|
+
"Run Leco Coordinator",
|
|
677
|
+
"",
|
|
678
|
+
"Run a Coordinator on this localhost",
|
|
679
|
+
auto_toolbar=False,
|
|
680
|
+
)
|
|
681
|
+
self.add_action(
|
|
682
|
+
"load_layout",
|
|
683
|
+
"Load Layout",
|
|
684
|
+
"",
|
|
685
|
+
"Load the Saved Docks layout corresponding to the current preset",
|
|
686
|
+
auto_toolbar=False,
|
|
687
|
+
)
|
|
688
|
+
self.add_action(
|
|
689
|
+
"save_layout",
|
|
690
|
+
"Save Layout",
|
|
691
|
+
"",
|
|
692
|
+
"Save the Saved Docks layout corresponding to the current preset",
|
|
693
|
+
auto_toolbar=False,
|
|
694
|
+
)
|
|
695
|
+
self.add_action(
|
|
696
|
+
"log_window", "Show/hide log window", "", checkable=True, auto_toolbar=False
|
|
697
|
+
)
|
|
698
|
+
self.add_action(
|
|
699
|
+
"new_preset",
|
|
700
|
+
"New Preset",
|
|
701
|
+
"",
|
|
702
|
+
'Create a new experimental setup configuration file: a "preset"',
|
|
703
|
+
auto_toolbar=False,
|
|
704
|
+
)
|
|
705
|
+
self.add_action(
|
|
706
|
+
"modify_preset",
|
|
707
|
+
"Modify Preset",
|
|
708
|
+
"",
|
|
709
|
+
'Modify an existing experimental setup configuration file: a "preset"',
|
|
710
|
+
auto_toolbar=False,
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
self.add_widget(
|
|
714
|
+
"preset_list",
|
|
715
|
+
QtWidgets.QComboBox,
|
|
716
|
+
toolbar=self.toolbar,
|
|
717
|
+
signal_str="currentTextChanged",
|
|
718
|
+
slot=self.update_preset_action,
|
|
719
|
+
)
|
|
720
|
+
self.add_action("load_preset", "LOAD", "Open", tip="Load the selected Preset: ")
|
|
721
|
+
self.update_preset_action_list()
|
|
722
|
+
|
|
723
|
+
self.add_action(
|
|
724
|
+
"new_overshoot",
|
|
725
|
+
"New Overshoot",
|
|
726
|
+
"",
|
|
727
|
+
"Create a new experimental setup overshoot configuration file",
|
|
728
|
+
auto_toolbar=False,
|
|
729
|
+
)
|
|
730
|
+
self.add_action(
|
|
731
|
+
"modify_overshoot",
|
|
732
|
+
"Modify Overshoot",
|
|
733
|
+
"",
|
|
734
|
+
"Modify an existing experimental setup overshoot configuration file",
|
|
735
|
+
auto_toolbar=False,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
for ind_file, file in enumerate(
|
|
739
|
+
config_mod_pymodaq.get_set_overshoot_path().iterdir()
|
|
740
|
+
):
|
|
741
|
+
if file.suffix == ".xml":
|
|
742
|
+
self.add_action(
|
|
743
|
+
self.get_action_from_file(file, ManagerEnums.overshoot),
|
|
744
|
+
file.stem,
|
|
745
|
+
auto_toolbar=False,
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
self.add_action("save_roi", "Save ROIs as a file", "", auto_toolbar=False)
|
|
749
|
+
self.add_action("modify_roi", "Modify ROI file", "", auto_toolbar=False)
|
|
750
|
+
|
|
751
|
+
for ind_file, file in enumerate(
|
|
752
|
+
config_mod_pymodaq.get_set_roi_path().iterdir()
|
|
753
|
+
):
|
|
754
|
+
if file.suffix == ".xml":
|
|
755
|
+
self.add_action(
|
|
756
|
+
self.get_action_from_file(file, ManagerEnums.roi),
|
|
757
|
+
file.stem,
|
|
758
|
+
"",
|
|
759
|
+
auto_toolbar=False,
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
self.add_action("new_remote", "Create New Remote", "", auto_toolbar=False)
|
|
763
|
+
self.add_action("modify_remote", "Modify Remote file", "", auto_toolbar=False)
|
|
764
|
+
for ind_file, file in enumerate(
|
|
765
|
+
config_mod_pymodaq.get_set_remote_path().iterdir()
|
|
766
|
+
):
|
|
767
|
+
if file.suffix == ".xml":
|
|
768
|
+
self.add_action(
|
|
769
|
+
self.get_action_from_file(file, ManagerEnums.remote),
|
|
770
|
+
file.stem,
|
|
771
|
+
"",
|
|
772
|
+
auto_toolbar=False,
|
|
773
|
+
)
|
|
774
|
+
self.add_action(
|
|
775
|
+
"activate_overshoot",
|
|
776
|
+
"Activate overshoot",
|
|
777
|
+
"Error",
|
|
778
|
+
tip="if activated, apply an overshoot if one is configured",
|
|
779
|
+
checkable=True,
|
|
780
|
+
enabled=False,
|
|
781
|
+
)
|
|
782
|
+
self.toolbar.addSeparator()
|
|
783
|
+
self.add_action(
|
|
784
|
+
"do_scan",
|
|
785
|
+
"Do Scans",
|
|
786
|
+
"surfacePlot",
|
|
787
|
+
tip="Open the DAQ Scan extension to acquire data as a function of "
|
|
788
|
+
"one or more parameter",
|
|
789
|
+
)
|
|
790
|
+
self.toolbar.addSeparator()
|
|
791
|
+
self.add_action("do_log", "Log data", "", auto_toolbar=False)
|
|
792
|
+
self.add_action("do_pid", "PID module", auto_toolbar=False)
|
|
793
|
+
self.add_action("console", "IPython Console", auto_toolbar=False)
|
|
794
|
+
self.add_action("bayesian", "Bayesian Optimisation", auto_toolbar=False)
|
|
795
|
+
self.add_action("adaptive", "Adaptive Scan", auto_toolbar=False)
|
|
796
|
+
self.add_action("datamixer", "DataMixer", auto_toolbar=False)
|
|
797
|
+
|
|
798
|
+
self.add_action("about", "About", "information2")
|
|
799
|
+
self.add_action("help", "Help", "help1")
|
|
800
|
+
self.get_action("help").setShortcut(QtGui.QKeySequence("F1"))
|
|
801
|
+
self.add_action("check_update", "Check Updates", "", auto_toolbar=False)
|
|
802
|
+
self.toolbar.addSeparator()
|
|
803
|
+
self.add_action("plugin_manager", "Plugin Manager", "")
|
|
804
|
+
|
|
805
|
+
def update_preset_action_list(self):
|
|
806
|
+
presets = []
|
|
807
|
+
self.get_action("preset_list").clear()
|
|
808
|
+
for ind_file, file in enumerate(self.preset_path.iterdir()):
|
|
809
|
+
if file.suffix == ".xml":
|
|
810
|
+
filestem = file.stem
|
|
811
|
+
if not self.has_action(
|
|
812
|
+
self.get_action_from_file(file, ManagerEnums.preset)
|
|
813
|
+
):
|
|
814
|
+
self.add_action(
|
|
815
|
+
self.get_action_from_file(file, ManagerEnums.preset),
|
|
816
|
+
filestem,
|
|
817
|
+
"",
|
|
818
|
+
f"Load the {filestem}.xml preset",
|
|
819
|
+
auto_toolbar=False,
|
|
820
|
+
)
|
|
821
|
+
presets.append(filestem)
|
|
822
|
+
|
|
823
|
+
self.get_action("preset_list").addItems(presets)
|
|
824
|
+
|
|
825
|
+
def update_preset_action(self, preset_name: str):
|
|
826
|
+
self.get_action("load_preset").setToolTip(
|
|
827
|
+
f"Load the {preset_name}.xml preset file!"
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
def connect_things(self):
|
|
831
|
+
self.status_signal[str].connect(self.add_status)
|
|
832
|
+
self.connect_action("log", self.show_log)
|
|
833
|
+
self.connect_action("config_utils", lambda: self.show_config(config_utils))
|
|
834
|
+
self.connect_action("config", lambda: self.show_config(config))
|
|
835
|
+
self.connect_action("quit", self.quit_fun)
|
|
836
|
+
self.connect_action("restart", self.restart_fun)
|
|
837
|
+
self.connect_action("leco", start_coordinator)
|
|
838
|
+
self.connect_action("load_layout", self.load_layout_state)
|
|
839
|
+
self.connect_action("save_layout", self.save_layout_state)
|
|
840
|
+
self.connect_action("log_window", self.logger_dock.setVisible)
|
|
841
|
+
self.connect_action("new_preset", self.create_preset)
|
|
842
|
+
self.connect_action("modify_preset", self.modify_preset)
|
|
843
|
+
|
|
844
|
+
for ind_file, file in enumerate(self.preset_path.iterdir()):
|
|
845
|
+
if file.suffix == ".xml":
|
|
846
|
+
self.connect_action(
|
|
847
|
+
self.get_action_from_file(file, ManagerEnums.preset),
|
|
848
|
+
self.create_menu_slot(self.preset_path.joinpath(file)),
|
|
849
|
+
)
|
|
850
|
+
self.connect_action(
|
|
851
|
+
"load_preset",
|
|
852
|
+
lambda: self.set_preset_mode(
|
|
853
|
+
self.preset_path.joinpath(
|
|
854
|
+
f"{self.get_action('preset_list').currentText()}.xml"
|
|
855
|
+
)
|
|
856
|
+
),
|
|
857
|
+
)
|
|
858
|
+
self.connect_action("new_overshoot", self.create_overshoot)
|
|
859
|
+
self.connect_action("modify_overshoot", self.modify_overshoot)
|
|
860
|
+
self.connect_action("activate_overshoot", self.activate_overshoot)
|
|
861
|
+
|
|
862
|
+
for ind_file, file in enumerate(
|
|
863
|
+
config_mod_pymodaq.get_set_overshoot_path().iterdir()
|
|
864
|
+
):
|
|
865
|
+
if file.suffix == ".xml":
|
|
866
|
+
self.connect_action(
|
|
867
|
+
self.get_action_from_file(file, ManagerEnums.overshoot),
|
|
868
|
+
self.create_menu_slot_over(
|
|
869
|
+
config_mod_pymodaq.get_set_overshoot_path().joinpath(file)
|
|
870
|
+
),
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
self.connect_action("save_roi", self.create_roi_file)
|
|
874
|
+
self.connect_action("modify_roi", self.modify_roi)
|
|
875
|
+
|
|
876
|
+
for ind_file, file in enumerate(
|
|
877
|
+
config_mod_pymodaq.get_set_roi_path().iterdir()
|
|
878
|
+
):
|
|
879
|
+
if file.suffix == ".xml":
|
|
880
|
+
self.connect_action(
|
|
881
|
+
self.get_action_from_file(file, ManagerEnums.roi),
|
|
882
|
+
self.create_menu_slot_roi(
|
|
883
|
+
config_mod_pymodaq.get_set_roi_path().joinpath(file)
|
|
884
|
+
),
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
self.connect_action("new_remote", self.create_remote)
|
|
888
|
+
self.connect_action("modify_remote", self.modify_remote)
|
|
889
|
+
for ind_file, file in enumerate(
|
|
890
|
+
config_mod_pymodaq.get_set_remote_path().iterdir()
|
|
891
|
+
):
|
|
892
|
+
if file.suffix == ".xml":
|
|
893
|
+
self.connect_action(
|
|
894
|
+
self.get_action_from_file(file, ManagerEnums.remote),
|
|
895
|
+
self.create_menu_slot_remote(
|
|
896
|
+
config_mod_pymodaq.get_set_remote_path().joinpath(file)
|
|
897
|
+
),
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
self.connect_action("do_scan", lambda: self.load_scan_module())
|
|
901
|
+
self.connect_action("do_log", lambda: self.load_log_module())
|
|
902
|
+
self.connect_action("do_pid", lambda: self.load_pid_module())
|
|
903
|
+
self.connect_action("console", lambda: self.load_console())
|
|
904
|
+
self.connect_action("bayesian", lambda: self.load_bayesian())
|
|
905
|
+
self.connect_action("adaptive", lambda: self.load_adaptive())
|
|
906
|
+
self.connect_action("datamixer", lambda: self.load_datamixer())
|
|
907
|
+
|
|
908
|
+
self.connect_action("about", self.show_about)
|
|
909
|
+
self.connect_action("help", self.show_help)
|
|
910
|
+
self.connect_action("check_update", lambda: self.check_update(True))
|
|
911
|
+
self.connect_action("plugin_manager", self.start_plugin_manager)
|
|
912
|
+
|
|
913
|
+
def setup_menu(self, menubar: QtWidgets.QMenuBar = None):
|
|
914
|
+
"""
|
|
915
|
+
Create the menubar object looking like :
|
|
916
|
+
"""
|
|
917
|
+
menubar.clear()
|
|
918
|
+
|
|
919
|
+
# %% create Settings menu
|
|
920
|
+
self.file_menu = menubar.addMenu("File")
|
|
921
|
+
self.file_menu.addAction(self.get_action("log"))
|
|
922
|
+
self.file_menu.addAction(self.get_action("config_utils"))
|
|
923
|
+
self.file_menu.addAction(self.get_action("config"))
|
|
924
|
+
self.file_menu.addSeparator()
|
|
925
|
+
self.file_menu.addAction(self.get_action("quit"))
|
|
926
|
+
self.file_menu.addAction(self.get_action("restart"))
|
|
927
|
+
|
|
928
|
+
self.settings_menu = menubar.addMenu("Settings")
|
|
929
|
+
self.settings_menu.addAction(self.get_action("leco"))
|
|
930
|
+
docked_menu = self.settings_menu.addMenu("Docked windows")
|
|
931
|
+
docked_menu.addAction(self.get_action("load_layout"))
|
|
932
|
+
docked_menu.addAction(self.get_action("save_layout"))
|
|
933
|
+
|
|
934
|
+
docked_menu.addSeparator()
|
|
935
|
+
docked_menu.addAction(self.get_action("log_window"))
|
|
936
|
+
|
|
937
|
+
self.preset_menu = menubar.addMenu("Preset Modes")
|
|
938
|
+
self.preset_menu.addAction(self.get_action("new_preset"))
|
|
939
|
+
self.preset_menu.addAction(self.get_action("modify_preset"))
|
|
940
|
+
self.preset_menu.addSeparator()
|
|
941
|
+
self.load_preset_menu = self.preset_menu.addMenu("Load presets")
|
|
942
|
+
|
|
943
|
+
for ind_file, file in enumerate(self.preset_path.iterdir()):
|
|
944
|
+
if file.suffix == ".xml":
|
|
945
|
+
self.load_preset_menu.addAction(
|
|
946
|
+
self.get_action(
|
|
947
|
+
self.get_action_from_file(file, ManagerEnums.preset)
|
|
948
|
+
)
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
self.overshoot_menu = menubar.addMenu("Overshoot Modes")
|
|
952
|
+
self.overshoot_menu.addAction(self.get_action("new_overshoot"))
|
|
953
|
+
self.overshoot_menu.addAction(self.get_action("modify_overshoot"))
|
|
954
|
+
self.overshoot_menu.addAction(self.get_action("activate_overshoot"))
|
|
955
|
+
self.overshoot_menu.addSeparator()
|
|
956
|
+
load_overshoot_menu = self.overshoot_menu.addMenu("Load Overshoots")
|
|
957
|
+
|
|
958
|
+
for ind_file, file in enumerate(
|
|
959
|
+
config_mod_pymodaq.get_set_overshoot_path().iterdir()
|
|
960
|
+
):
|
|
961
|
+
if file.suffix == ".xml":
|
|
962
|
+
load_overshoot_menu.addAction(
|
|
963
|
+
self.get_action(
|
|
964
|
+
self.get_action_from_file(file, ManagerEnums.overshoot)
|
|
965
|
+
)
|
|
966
|
+
)
|
|
967
|
+
|
|
968
|
+
self.roi_menu = menubar.addMenu("ROI Modes")
|
|
969
|
+
self.roi_menu.addAction(self.get_action("save_roi"))
|
|
970
|
+
self.roi_menu.addAction(self.get_action("modify_roi"))
|
|
971
|
+
self.roi_menu.addSeparator()
|
|
972
|
+
load_roi_menu = self.roi_menu.addMenu("Load roi configs")
|
|
973
|
+
|
|
974
|
+
for ind_file, file in enumerate(
|
|
975
|
+
config_mod_pymodaq.get_set_roi_path().iterdir()
|
|
976
|
+
):
|
|
977
|
+
if file.suffix == ".xml":
|
|
978
|
+
load_roi_menu.addAction(
|
|
979
|
+
self.get_action(self.get_action_from_file(file, ManagerEnums.roi))
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
self.remote_menu = menubar.addMenu("Remote/Shortcuts Control")
|
|
983
|
+
self.remote_menu.addAction("New remote config.", self.create_remote)
|
|
984
|
+
self.remote_menu.addAction("Modify remote config.", self.modify_remote)
|
|
985
|
+
self.remote_menu.addSeparator()
|
|
986
|
+
load_remote_menu = self.remote_menu.addMenu("Load remote config.")
|
|
987
|
+
|
|
988
|
+
for ind_file, file in enumerate(
|
|
989
|
+
config_mod_pymodaq.get_set_remote_path().iterdir()
|
|
990
|
+
):
|
|
991
|
+
if file.suffix == ".xml":
|
|
992
|
+
load_remote_menu.addAction(
|
|
993
|
+
self.get_action(
|
|
994
|
+
self.get_action_from_file(file, ManagerEnums.remote)
|
|
995
|
+
)
|
|
996
|
+
)
|
|
997
|
+
|
|
998
|
+
# extensions menu
|
|
999
|
+
self.extensions_menu = menubar.addMenu("Extensions")
|
|
1000
|
+
self.extensions_menu.addAction(self.get_action("do_scan"))
|
|
1001
|
+
self.extensions_menu.addAction(self.get_action("do_log"))
|
|
1002
|
+
self.extensions_menu.addAction(self.get_action("do_pid"))
|
|
1003
|
+
self.extensions_menu.addAction(self.get_action("console"))
|
|
1004
|
+
self.extensions_menu.addAction(self.get_action("bayesian"))
|
|
1005
|
+
self.extensions_menu.addAction(self.get_action("adaptive"))
|
|
1006
|
+
self.extensions_menu.addAction(self.get_action("datamixer"))
|
|
1007
|
+
|
|
1008
|
+
# extensions from plugins
|
|
1009
|
+
extensions_actions = []
|
|
1010
|
+
for ext in extensions:
|
|
1011
|
+
extensions_actions.append(self.extensions_menu.addAction(ext["name"]))
|
|
1012
|
+
extensions_actions[-1].triggered.connect(self.create_menu_slot_ext(ext))
|
|
1013
|
+
|
|
1014
|
+
# help menu
|
|
1015
|
+
help_menu = menubar.addMenu("?")
|
|
1016
|
+
help_menu.addAction(self.get_action("about"))
|
|
1017
|
+
help_menu.addAction(self.get_action("help"))
|
|
1018
|
+
help_menu.addSeparator()
|
|
1019
|
+
help_menu.addAction(self.get_action("check_update"))
|
|
1020
|
+
help_menu.addAction(self.get_action("plugin_manager"))
|
|
1021
|
+
|
|
1022
|
+
status = self.preset_file is None
|
|
1023
|
+
|
|
1024
|
+
self.overshoot_menu.setEnabled(not status)
|
|
1025
|
+
self.roi_menu.setEnabled(not status)
|
|
1026
|
+
self.remote_menu.setEnabled(not status)
|
|
1027
|
+
self.extensions_menu.setEnabled(not status)
|
|
1028
|
+
self.file_menu.setEnabled(True)
|
|
1029
|
+
self.settings_menu.setEnabled(True)
|
|
1030
|
+
self.preset_menu.setEnabled(status)
|
|
1031
|
+
|
|
1032
|
+
def start_plugin_manager(self):
|
|
1033
|
+
self.win_plug_manager = QtWidgets.QMainWindow()
|
|
1034
|
+
self.win_plug_manager.setWindowTitle("PyMoDAQ Plugin Manager")
|
|
1035
|
+
widget = QtWidgets.QWidget()
|
|
1036
|
+
self.win_plug_manager.setCentralWidget(widget)
|
|
1037
|
+
self.plugin_manager = PluginManager(widget)
|
|
1038
|
+
self.plugin_manager.quit_signal.connect(self.quit_fun)
|
|
1039
|
+
self.plugin_manager.restart_signal.connect(self.restart_fun)
|
|
1040
|
+
self.win_plug_manager.show()
|
|
1041
|
+
|
|
1042
|
+
def create_menu_slot(self, filename):
|
|
1043
|
+
return lambda: self.set_preset_mode(filename)
|
|
1044
|
+
|
|
1045
|
+
def create_menu_slot_ext(self, ext):
|
|
1046
|
+
return lambda: self.load_extensions_module(ext)
|
|
1047
|
+
|
|
1048
|
+
def create_menu_slot_roi(self, filename):
|
|
1049
|
+
return lambda: self.set_roi_configuration(filename)
|
|
1050
|
+
|
|
1051
|
+
def create_menu_slot_over(self, filename):
|
|
1052
|
+
return lambda: self.set_overshoot_configuration(filename)
|
|
1053
|
+
|
|
1054
|
+
def create_menu_slot_remote(self, filename):
|
|
1055
|
+
return lambda: self.set_remote_configuration(filename)
|
|
1056
|
+
|
|
1057
|
+
def create_roi_file(self):
|
|
1058
|
+
try:
|
|
1059
|
+
if self.preset_file is not None:
|
|
1060
|
+
self.roi_saver.set_new_roi(self.preset_file.stem)
|
|
1061
|
+
self.add_action(
|
|
1062
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.roi),
|
|
1063
|
+
self.preset_file.stem,
|
|
1064
|
+
"",
|
|
1065
|
+
)
|
|
1066
|
+
self.setup_menu(self.menubar)
|
|
1067
|
+
self.connect_action(
|
|
1068
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.roi),
|
|
1069
|
+
self.create_menu_slot_roi(
|
|
1070
|
+
config_mod_pymodaq.get_set_roi_path().joinpath(self.preset_file.name)
|
|
1071
|
+
),
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
except Exception as e:
|
|
1076
|
+
logger.exception(str(e))
|
|
1077
|
+
|
|
1078
|
+
def create_remote(self):
|
|
1079
|
+
try:
|
|
1080
|
+
if self.preset_file is not None:
|
|
1081
|
+
self.remote_manager.set_new_remote(self.preset_file.stem)
|
|
1082
|
+
self.add_action(
|
|
1083
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.remote),
|
|
1084
|
+
self.preset_file.stem,
|
|
1085
|
+
"",
|
|
1086
|
+
)
|
|
1087
|
+
self.setup_menu(self.menubar)
|
|
1088
|
+
self.connect_action(
|
|
1089
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.remote),
|
|
1090
|
+
self.create_menu_slot_remote(
|
|
1091
|
+
config_mod_pymodaq.get_set_remote_path().joinpath(self.preset_file.name)
|
|
1092
|
+
),
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
except Exception as e:
|
|
1096
|
+
logger.exception(str(e))
|
|
1097
|
+
|
|
1098
|
+
def create_overshoot(self):
|
|
1099
|
+
try:
|
|
1100
|
+
if self.preset_file is not None:
|
|
1101
|
+
self.overshoot_manager.set_new_overshoot(self.preset_file.stem)
|
|
1102
|
+
self.add_action(
|
|
1103
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.overshoot),
|
|
1104
|
+
self.preset_file.stem,
|
|
1105
|
+
"",
|
|
1106
|
+
)
|
|
1107
|
+
self.setup_menu(self.menubar)
|
|
1108
|
+
self.connect_action(
|
|
1109
|
+
self.get_action_from_file(self.preset_file, ManagerEnums.overshoot),
|
|
1110
|
+
self.create_menu_slot_over(
|
|
1111
|
+
config_mod_pymodaq.get_set_overshoot_path().joinpath(self.preset_file.name)
|
|
1112
|
+
),
|
|
1113
|
+
)
|
|
1114
|
+
except Exception as e:
|
|
1115
|
+
logger.exception(str(e))
|
|
1116
|
+
|
|
1117
|
+
def create_preset(self):
|
|
1118
|
+
try:
|
|
1119
|
+
status = self.preset_manager.set_new_preset()
|
|
1120
|
+
if status:
|
|
1121
|
+
self.update_preset_action_list()
|
|
1122
|
+
self.setup_menu(self.menubar)
|
|
1123
|
+
self.new_preset_created.emit()
|
|
1124
|
+
except Exception as e:
|
|
1125
|
+
logger.exception(str(e))
|
|
1126
|
+
|
|
1127
|
+
@staticmethod
|
|
1128
|
+
def get_action_from_file(file: Path, manager: ManagerEnums):
|
|
1129
|
+
return f"{file.stem}_{manager.name}"
|
|
1130
|
+
|
|
1131
|
+
def modify_remote(self):
|
|
1132
|
+
try:
|
|
1133
|
+
path = select_file(
|
|
1134
|
+
start_path=config_mod_pymodaq.get_set_remote_path(),
|
|
1135
|
+
save=False,
|
|
1136
|
+
ext="xml",
|
|
1137
|
+
)
|
|
1138
|
+
if path != "":
|
|
1139
|
+
self.remote_manager.set_file_remote(path)
|
|
1140
|
+
|
|
1141
|
+
else: # cancel
|
|
1142
|
+
pass
|
|
1143
|
+
except Exception as e:
|
|
1144
|
+
logger.exception(str(e))
|
|
1145
|
+
|
|
1146
|
+
def modify_overshoot(self):
|
|
1147
|
+
try:
|
|
1148
|
+
path = select_file(
|
|
1149
|
+
start_path=config_mod_pymodaq.get_set_overshoot_path(),
|
|
1150
|
+
save=False,
|
|
1151
|
+
ext="xml",
|
|
1152
|
+
)
|
|
1153
|
+
if path != "":
|
|
1154
|
+
self.overshoot_manager.set_file_overshoot(path)
|
|
1155
|
+
|
|
1156
|
+
else: # cancel
|
|
1157
|
+
pass
|
|
1158
|
+
except Exception as e:
|
|
1159
|
+
logger.exception(str(e))
|
|
1160
|
+
|
|
1161
|
+
def modify_roi(self):
|
|
1162
|
+
try:
|
|
1163
|
+
path = select_file(
|
|
1164
|
+
start_path=config_mod_pymodaq.get_set_roi_path(), save=False, ext="xml"
|
|
1165
|
+
)
|
|
1166
|
+
if path != "":
|
|
1167
|
+
self.roi_saver.set_file_roi(path)
|
|
1168
|
+
|
|
1169
|
+
else: # cancel
|
|
1170
|
+
pass
|
|
1171
|
+
except Exception as e:
|
|
1172
|
+
logger.exception(str(e))
|
|
1173
|
+
|
|
1174
|
+
def modify_preset(self):
|
|
1175
|
+
try:
|
|
1176
|
+
path = select_file(start_path=self.preset_path, save=False, ext="xml")
|
|
1177
|
+
if path != "":
|
|
1178
|
+
modified = self.preset_manager.set_file_preset(path)
|
|
1179
|
+
|
|
1180
|
+
if modified:
|
|
1181
|
+
self.remove_preset_related_files(path.name)
|
|
1182
|
+
if self.detector_modules:
|
|
1183
|
+
mssg = QMessageBox()
|
|
1184
|
+
mssg.setText(
|
|
1185
|
+
"You have to restart the application to take the modifications"
|
|
1186
|
+
" into account!\n\n"
|
|
1187
|
+
"The related files: ROI, Layout, Overshoot and Remote will be"
|
|
1188
|
+
" deleted if existing!\n\n"
|
|
1189
|
+
"Quitting the application..."
|
|
1190
|
+
)
|
|
1191
|
+
mssg.exec()
|
|
1192
|
+
self.restart_fun()
|
|
1193
|
+
|
|
1194
|
+
else: # cancel
|
|
1195
|
+
pass
|
|
1196
|
+
except Exception as e:
|
|
1197
|
+
logger.exception(str(e))
|
|
1198
|
+
|
|
1199
|
+
def remove_preset_related_files(self, name):
|
|
1200
|
+
config_mod_pymodaq.get_set_roi_path().joinpath(name).unlink(missing_ok=True)
|
|
1201
|
+
config_mod_pymodaq.get_set_layout_path().joinpath(name).unlink(missing_ok=True)
|
|
1202
|
+
config_mod_pymodaq.get_set_overshoot_path().joinpath(name).unlink(
|
|
1203
|
+
missing_ok=True
|
|
1204
|
+
)
|
|
1205
|
+
config_mod_pymodaq.get_set_remote_path().joinpath(name).unlink(missing_ok=True)
|
|
1206
|
+
|
|
1207
|
+
def quit_fun(self):
|
|
1208
|
+
"""
|
|
1209
|
+
Quit the current instance of DAQ_scan and close on cascade move and detector modules.
|
|
1210
|
+
|
|
1211
|
+
See Also
|
|
1212
|
+
--------
|
|
1213
|
+
quit_fun
|
|
1214
|
+
"""
|
|
1215
|
+
try:
|
|
1216
|
+
self.remote_timer.stop()
|
|
1217
|
+
|
|
1218
|
+
for ext in self.extensions:
|
|
1219
|
+
if hasattr(self.extensions[ext], "quit_fun"):
|
|
1220
|
+
self.extensions[ext].quit_fun()
|
|
1221
|
+
for mov in self.actuators_modules:
|
|
1222
|
+
try:
|
|
1223
|
+
mov.init_signal.disconnect(self.update_init_tree)
|
|
1224
|
+
except TypeError:
|
|
1225
|
+
pass
|
|
1226
|
+
for det in self.detector_modules:
|
|
1227
|
+
try:
|
|
1228
|
+
det.init_signal.disconnect(self.update_init_tree)
|
|
1229
|
+
except TypeError:
|
|
1230
|
+
pass
|
|
1231
|
+
|
|
1232
|
+
for module in self.actuators_modules:
|
|
1233
|
+
try:
|
|
1234
|
+
module.quit_fun()
|
|
1235
|
+
QtWidgets.QApplication.processEvents()
|
|
1236
|
+
QThread.msleep(1000)
|
|
1237
|
+
QtWidgets.QApplication.processEvents()
|
|
1238
|
+
except Exception:
|
|
1239
|
+
pass
|
|
1240
|
+
|
|
1241
|
+
for module in self.detector_modules:
|
|
1242
|
+
try:
|
|
1243
|
+
module.quit_fun()
|
|
1244
|
+
QtWidgets.QApplication.processEvents()
|
|
1245
|
+
QThread.msleep(1000)
|
|
1246
|
+
QtWidgets.QApplication.processEvents()
|
|
1247
|
+
except Exception:
|
|
1248
|
+
pass
|
|
1249
|
+
areas = self.dockarea.tempAreas[:]
|
|
1250
|
+
for area in areas:
|
|
1251
|
+
area.win.close()
|
|
1252
|
+
QtWidgets.QApplication.processEvents()
|
|
1253
|
+
QThread.msleep(1000)
|
|
1254
|
+
QtWidgets.QApplication.processEvents()
|
|
1255
|
+
|
|
1256
|
+
if hasattr(self, "mainwindow"):
|
|
1257
|
+
self.mainwindow.close()
|
|
1258
|
+
|
|
1259
|
+
if self.pid_window is not None:
|
|
1260
|
+
self.pid_window.close()
|
|
1261
|
+
|
|
1262
|
+
except Exception as e:
|
|
1263
|
+
logger.exception(str(e))
|
|
1264
|
+
|
|
1265
|
+
def restart_fun(self, ask=False):
|
|
1266
|
+
ret = False
|
|
1267
|
+
mssg = QMessageBox()
|
|
1268
|
+
if ask:
|
|
1269
|
+
mssg.setText(
|
|
1270
|
+
"You have to restart the application to take the"
|
|
1271
|
+
" modifications into account!"
|
|
1272
|
+
)
|
|
1273
|
+
mssg.setInformativeText("Do you want to restart?")
|
|
1274
|
+
mssg.setStandardButtons(QMessageBox.StandardButton.Ok | QMessageBox.StandardButton.Cancel)
|
|
1275
|
+
ret = mssg.exec()
|
|
1276
|
+
|
|
1277
|
+
if ret == QMessageBox.StandardButton.Ok or not ask:
|
|
1278
|
+
self.quit_fun()
|
|
1279
|
+
subprocess.call([sys.executable, __file__])
|
|
1280
|
+
|
|
1281
|
+
def load_layout_state(self, file=None):
|
|
1282
|
+
"""
|
|
1283
|
+
Load and restore a layout state from the select_file obtained pathname file.
|
|
1284
|
+
|
|
1285
|
+
See Also
|
|
1286
|
+
--------
|
|
1287
|
+
utils.select_file
|
|
1288
|
+
"""
|
|
1289
|
+
try:
|
|
1290
|
+
file = layout_mod.load_layout_state(self.dockarea, file)
|
|
1291
|
+
self.settings.child("loaded_files", "layout_file").setValue(file)
|
|
1292
|
+
except Exception as e:
|
|
1293
|
+
logger.exception(str(e))
|
|
1294
|
+
|
|
1295
|
+
def save_layout_state(self, file=None):
|
|
1296
|
+
"""
|
|
1297
|
+
Save the current layout state in the select_file obtained pathname file.
|
|
1298
|
+
Once done dump the pickle.
|
|
1299
|
+
|
|
1300
|
+
See Also
|
|
1301
|
+
--------
|
|
1302
|
+
utils.select_file
|
|
1303
|
+
"""
|
|
1304
|
+
try:
|
|
1305
|
+
layout_mod.save_layout_state(self.dockarea, file)
|
|
1306
|
+
except Exception as e:
|
|
1307
|
+
logger.exception(str(e))
|
|
1308
|
+
|
|
1309
|
+
def save_layout_state_auto(self):
|
|
1310
|
+
if self.preset_file is not None:
|
|
1311
|
+
path = layout_path.joinpath(self.preset_file.stem + ".dock")
|
|
1312
|
+
self.save_layout_state(path)
|
|
1313
|
+
|
|
1314
|
+
def add_move(
|
|
1315
|
+
self,
|
|
1316
|
+
plug_name: str = None,
|
|
1317
|
+
plug_settings: Parameter = None,
|
|
1318
|
+
plug_type: str = None,
|
|
1319
|
+
move_docks: list[Dock] = None,
|
|
1320
|
+
move_forms: list[QtWidgets.QWidget] = None,
|
|
1321
|
+
actuators_modules: list[DAQ_Move] = None,
|
|
1322
|
+
ui_identifier: str = None,
|
|
1323
|
+
**kwargs
|
|
1324
|
+
) -> DAQ_Move:
|
|
1325
|
+
if move_docks is None:
|
|
1326
|
+
move_docks = []
|
|
1327
|
+
if move_forms is None:
|
|
1328
|
+
move_forms = []
|
|
1329
|
+
if actuators_modules is None:
|
|
1330
|
+
actuators_modules = []
|
|
1331
|
+
|
|
1332
|
+
if ui_identifier is not None:
|
|
1333
|
+
pass
|
|
1334
|
+
elif plug_settings is None:
|
|
1335
|
+
ui_identifier = config("actuator", "ui")
|
|
1336
|
+
else:
|
|
1337
|
+
try:
|
|
1338
|
+
ui_identifier = plug_settings["main_settings", "ui_type"]
|
|
1339
|
+
except KeyError:
|
|
1340
|
+
ui_identifier = config("actuator", "ui")
|
|
1341
|
+
|
|
1342
|
+
is_compact = (
|
|
1343
|
+
ActuatorUIFactory.get(ui_identifier).is_compact
|
|
1344
|
+
if ui_identifier is not None
|
|
1345
|
+
else False
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
if is_compact:
|
|
1349
|
+
if self.compact_actuator_dock is None:
|
|
1350
|
+
self.compact_actuator_dock = Dock("Simple Actuators")
|
|
1351
|
+
self.compact_actuator_dock.layout.setSpacing(0)
|
|
1352
|
+
self.compact_actuator_dock.layout.setContentsMargins(0, 0, 0, 0)
|
|
1353
|
+
|
|
1354
|
+
dock = self.compact_actuator_dock
|
|
1355
|
+
self.logger_dock.area.addDock(dock, "bottom")
|
|
1356
|
+
else:
|
|
1357
|
+
dock = Dock(plug_name, size=(150, 250))
|
|
1358
|
+
move_docks.append(dock)
|
|
1359
|
+
|
|
1360
|
+
if len(move_docks) == 1:
|
|
1361
|
+
self.dockarea.addDock(dock, "right", self.logger_dock)
|
|
1362
|
+
else:
|
|
1363
|
+
self.dockarea.addDock(dock, "above", move_docks[-2])
|
|
1364
|
+
|
|
1365
|
+
move_forms.append(QtWidgets.QWidget())
|
|
1366
|
+
mov_mod_tmp = DAQ_Move(move_forms[-1], plug_name, ui_identifier=ui_identifier)
|
|
1367
|
+
|
|
1368
|
+
mov_mod_tmp.actuator = plug_type
|
|
1369
|
+
QtWidgets.QApplication.processEvents()
|
|
1370
|
+
mov_mod_tmp.manage_ui_actions("quit", "setEnabled", False)
|
|
1371
|
+
|
|
1372
|
+
if plug_settings is not None:
|
|
1373
|
+
try:
|
|
1374
|
+
putils.set_param_from_param(mov_mod_tmp.settings, plug_settings)
|
|
1375
|
+
except KeyError as e:
|
|
1376
|
+
mssg = (
|
|
1377
|
+
f"Could not set this setting: {str(e)}\n"
|
|
1378
|
+
f"The Preset is no more compatible with the plugin {plug_type}"
|
|
1379
|
+
)
|
|
1380
|
+
logger.warning(mssg)
|
|
1381
|
+
self.splash_sc.showMessage(mssg)
|
|
1382
|
+
QtWidgets.QApplication.processEvents()
|
|
1383
|
+
|
|
1384
|
+
mov_mod_tmp.bounds_signal[bool].connect(self.do_stuff_from_out_bounds)
|
|
1385
|
+
dock.addWidget(move_forms[-1])
|
|
1386
|
+
|
|
1387
|
+
actuators_modules.append(mov_mod_tmp)
|
|
1388
|
+
return mov_mod_tmp
|
|
1389
|
+
|
|
1390
|
+
def add_move_from_extension(
|
|
1391
|
+
self, name: str, instrument_name: str, instrument_controller: Any,
|
|
1392
|
+
ui_identifier = None,
|
|
1393
|
+
**kwargs
|
|
1394
|
+
):
|
|
1395
|
+
"""Specific method to add a DAQ_Move within the Dashboard. This Particular actuator
|
|
1396
|
+
should be defined in the plugin of the extension and is used to mimic an actuator while
|
|
1397
|
+
move_abs is actually triggering an action on the extension which loaded it
|
|
1398
|
+
|
|
1399
|
+
For an exemple, see the PyMoDAQ builtin PID extension
|
|
1400
|
+
|
|
1401
|
+
Parameters
|
|
1402
|
+
----------
|
|
1403
|
+
name: str
|
|
1404
|
+
The name to print on the UI title
|
|
1405
|
+
instrument_name: str
|
|
1406
|
+
The name of the instrument class, for instance PID for the daq_move_PID
|
|
1407
|
+
module and the DAQ_Move_PID instrument class
|
|
1408
|
+
instrument_controller: object
|
|
1409
|
+
whatever object is used to communicate between the instrument module and the extension
|
|
1410
|
+
which created it
|
|
1411
|
+
ui_identifier: str
|
|
1412
|
+
One of the possible registered UI
|
|
1413
|
+
kwargs: named arguments to be passed to add_move
|
|
1414
|
+
"""
|
|
1415
|
+
actuator = self.add_move(name, None, instrument_name, [], [], [],
|
|
1416
|
+
ui_identifier=ui_identifier,
|
|
1417
|
+
**kwargs)
|
|
1418
|
+
actuator.controller = instrument_controller
|
|
1419
|
+
actuator.master = False
|
|
1420
|
+
actuator.init_hardware_ui()
|
|
1421
|
+
QtWidgets.QApplication.processEvents()
|
|
1422
|
+
self.poll_init(actuator)
|
|
1423
|
+
QtWidgets.QApplication.processEvents()
|
|
1424
|
+
|
|
1425
|
+
# Update actuators modules and module manager
|
|
1426
|
+
self.actuators_modules.append(actuator)
|
|
1427
|
+
self.update_module_manager()
|
|
1428
|
+
|
|
1429
|
+
def add_det(
|
|
1430
|
+
self,
|
|
1431
|
+
plug_name,
|
|
1432
|
+
plug_settings,
|
|
1433
|
+
det_docks_settings,
|
|
1434
|
+
det_docks_viewer,
|
|
1435
|
+
detector_modules,
|
|
1436
|
+
plug_type: str = None,
|
|
1437
|
+
plug_subtype: str = None,
|
|
1438
|
+
) -> DAQ_Viewer:
|
|
1439
|
+
if plug_type is None:
|
|
1440
|
+
plug_type = plug_settings.child("main_settings", "DAQ_type").value()
|
|
1441
|
+
if plug_subtype is None:
|
|
1442
|
+
plug_subtype = plug_settings.child("main_settings", "detector_type").value()
|
|
1443
|
+
det_docks_settings.append(Dock(plug_name + " settings", size=(150, 250)))
|
|
1444
|
+
det_docks_viewer.append(Dock(plug_name + " viewer", size=(350, 350)))
|
|
1445
|
+
if len(detector_modules) == 0:
|
|
1446
|
+
self.logger_dock.area.addDock(det_docks_settings[-1], "bottom")
|
|
1447
|
+
# dockarea of the logger dock
|
|
1448
|
+
else:
|
|
1449
|
+
self.dockarea.addDock(
|
|
1450
|
+
det_docks_settings[-1], "right", detector_modules[-1].viewer_docks[-1]
|
|
1451
|
+
)
|
|
1452
|
+
self.dockarea.addDock(det_docks_viewer[-1], "right", det_docks_settings[-1])
|
|
1453
|
+
det_mod_tmp = DAQ_Viewer(
|
|
1454
|
+
self.dockarea,
|
|
1455
|
+
title=plug_name,
|
|
1456
|
+
daq_type=plug_type,
|
|
1457
|
+
dock_settings=det_docks_settings[-1],
|
|
1458
|
+
dock_viewer=det_docks_viewer[-1],
|
|
1459
|
+
)
|
|
1460
|
+
QtWidgets.QApplication.processEvents()
|
|
1461
|
+
det_mod_tmp.detector = plug_subtype
|
|
1462
|
+
QtWidgets.QApplication.processEvents()
|
|
1463
|
+
det_mod_tmp.manage_ui_actions("quit", "setEnabled", False)
|
|
1464
|
+
|
|
1465
|
+
if plug_settings is not None:
|
|
1466
|
+
try:
|
|
1467
|
+
putils.set_param_from_param(det_mod_tmp.settings, plug_settings)
|
|
1468
|
+
except KeyError as e:
|
|
1469
|
+
mssg = (
|
|
1470
|
+
f"Could not set this setting: {str(e)}\n"
|
|
1471
|
+
f"The Preset is no more compatible with the plugin {plug_subtype}"
|
|
1472
|
+
)
|
|
1473
|
+
logger.warning(mssg)
|
|
1474
|
+
self.splash_sc.showMessage(mssg)
|
|
1475
|
+
|
|
1476
|
+
detector_modules.append(det_mod_tmp)
|
|
1477
|
+
return det_mod_tmp
|
|
1478
|
+
|
|
1479
|
+
def override_det_from_extension(self, overriden_grabbers: Sequence[str] = None):
|
|
1480
|
+
"""(Experimental) If an extension adding detectors within the Dashboard need to,
|
|
1481
|
+
it could call this method.
|
|
1482
|
+
|
|
1483
|
+
Then if some other extension trigger a grab from it, the request of a grab won't be done twice
|
|
1484
|
+
|
|
1485
|
+
Parameters
|
|
1486
|
+
----------
|
|
1487
|
+
overriden_grabbers: Sequence[str]
|
|
1488
|
+
sequence of detector names whose corresponding modules should set their
|
|
1489
|
+
attribute override_grab_from_extension to True.
|
|
1490
|
+
"""
|
|
1491
|
+
if overriden_grabbers is not None:
|
|
1492
|
+
for mod_name in overriden_grabbers:
|
|
1493
|
+
mod = self.modules_manager.get_mod_from_name(mod_name, "det")
|
|
1494
|
+
if mod is not None:
|
|
1495
|
+
mod.override_grab_from_extension = True
|
|
1496
|
+
|
|
1497
|
+
def add_det_from_extension(
|
|
1498
|
+
self, name: str, daq_type: str, instrument_name: str, instrument_controller: Any
|
|
1499
|
+
):
|
|
1500
|
+
"""Specific method to add a DAQ_Viewer within the Dashboard. This Particular detector
|
|
1501
|
+
should be defined in the plugin of the extension and is used to mimic a grab while data
|
|
1502
|
+
are actually coming from the extension which loaded it
|
|
1503
|
+
|
|
1504
|
+
For an exemple, see the pymodaq_plugins_datamixer plugin and its DataMixer extension
|
|
1505
|
+
or the DAQ_PID extension
|
|
1506
|
+
|
|
1507
|
+
Parameters
|
|
1508
|
+
----------
|
|
1509
|
+
name: str
|
|
1510
|
+
The name to print on the UI title
|
|
1511
|
+
daq_type: str
|
|
1512
|
+
either DAQ0D, DAQ1D, DAQ2D or DAQND depending the type of the instrument
|
|
1513
|
+
instrument_name: str
|
|
1514
|
+
The name of the instrument class, for instance DataMixer for the daq_0Dviewer_DataMixer
|
|
1515
|
+
module and the DAQ_0DViewer_DataMixer instrument class
|
|
1516
|
+
instrument_controller: object
|
|
1517
|
+
whatever object is used to communicate between the instrument module and the extension
|
|
1518
|
+
which created it
|
|
1519
|
+
"""
|
|
1520
|
+
detector = self.add_det(
|
|
1521
|
+
name, None, [], [], [], plug_type=daq_type, plug_subtype=instrument_name
|
|
1522
|
+
)
|
|
1523
|
+
detector.controller = instrument_controller
|
|
1524
|
+
detector.master = False
|
|
1525
|
+
detector.init_hardware_ui()
|
|
1526
|
+
QtWidgets.QApplication.processEvents()
|
|
1527
|
+
self.poll_init(detector)
|
|
1528
|
+
QtWidgets.QApplication.processEvents()
|
|
1529
|
+
|
|
1530
|
+
# Update actuators modules and module manager
|
|
1531
|
+
self.detector_modules.append(detector)
|
|
1532
|
+
self.update_module_manager()
|
|
1533
|
+
|
|
1534
|
+
def update_module_manager(self):
|
|
1535
|
+
if self.modules_manager is None:
|
|
1536
|
+
self.modules_manager = ModulesManager(
|
|
1537
|
+
self.detector_modules, self.actuators_modules, parent_name="Dashboard"
|
|
1538
|
+
)
|
|
1539
|
+
else:
|
|
1540
|
+
self.modules_manager.actuators_all = self.actuators_modules
|
|
1541
|
+
self.modules_manager.detectors_all = self.detector_modules
|
|
1542
|
+
|
|
1543
|
+
def set_file_preset(self, filename) -> Tuple[List[DAQ_Move], List[DAQ_Viewer]]:
|
|
1544
|
+
"""
|
|
1545
|
+
Set a file managers from the converted xml file given by the filename parameter.
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
=============== =========== ===================================================
|
|
1549
|
+
**Parameters** **Type** **Description**
|
|
1550
|
+
*filename* string the name of the xml file to be converted/treated
|
|
1551
|
+
=============== =========== ===================================================
|
|
1552
|
+
|
|
1553
|
+
Returns
|
|
1554
|
+
-------
|
|
1555
|
+
(Object list, Object list) tuple
|
|
1556
|
+
The updated (Move modules list, Detector modules list).
|
|
1557
|
+
|
|
1558
|
+
"""
|
|
1559
|
+
actuators_modules = []
|
|
1560
|
+
detector_modules = []
|
|
1561
|
+
if not isinstance(filename, Path):
|
|
1562
|
+
filename = Path(filename)
|
|
1563
|
+
|
|
1564
|
+
if filename.suffix == ".xml":
|
|
1565
|
+
self.preset_file = filename
|
|
1566
|
+
self.preset_manager.set_file_preset(filename, show=False)
|
|
1567
|
+
move_docks = []
|
|
1568
|
+
det_docks_settings = []
|
|
1569
|
+
det_docks_viewer = []
|
|
1570
|
+
move_forms = []
|
|
1571
|
+
|
|
1572
|
+
# ################################################################
|
|
1573
|
+
# ##### sort plugins by IDs and within the same IDs by Master and Slave status
|
|
1574
|
+
plugins = []
|
|
1575
|
+
plugins += [
|
|
1576
|
+
{"type": "move", "value": child}
|
|
1577
|
+
for child in self.preset_manager.preset_params.child("Moves").children()
|
|
1578
|
+
]
|
|
1579
|
+
plugins += [
|
|
1580
|
+
{"type": "det", "value": child}
|
|
1581
|
+
for child in self.preset_manager.preset_params.child(
|
|
1582
|
+
"Detectors"
|
|
1583
|
+
).children()
|
|
1584
|
+
]
|
|
1585
|
+
for plug in plugins:
|
|
1586
|
+
if plug["type"] == "det":
|
|
1587
|
+
try:
|
|
1588
|
+
plug["ID"] = plug["value"][
|
|
1589
|
+
"params", "detector_settings", "controller_ID"
|
|
1590
|
+
]
|
|
1591
|
+
plug["status"] = plug["value"][
|
|
1592
|
+
"params", "detector_settings", "controller_status"
|
|
1593
|
+
]
|
|
1594
|
+
except KeyError as e:
|
|
1595
|
+
raise DetectorError
|
|
1596
|
+
else:
|
|
1597
|
+
try:
|
|
1598
|
+
plug["ID"] = plug["value"][
|
|
1599
|
+
"params", "move_settings", "multiaxes", "controller_ID"
|
|
1600
|
+
]
|
|
1601
|
+
plug["status"] = plug["value"][
|
|
1602
|
+
"params", "move_settings", "multiaxes", "multi_status"
|
|
1603
|
+
]
|
|
1604
|
+
except KeyError as e:
|
|
1605
|
+
raise ActuatorError
|
|
1606
|
+
|
|
1607
|
+
IDs = list(set([plug["ID"] for plug in plugins]))
|
|
1608
|
+
# %%
|
|
1609
|
+
plugins_sorted = []
|
|
1610
|
+
for id in IDs:
|
|
1611
|
+
plug_Ids = []
|
|
1612
|
+
for plug in plugins:
|
|
1613
|
+
if plug["ID"] == id:
|
|
1614
|
+
plug_Ids.append(plug)
|
|
1615
|
+
plug_Ids.sort(key=lambda status: status["status"])
|
|
1616
|
+
plugins_sorted.append(plug_Ids)
|
|
1617
|
+
#################################################################
|
|
1618
|
+
#######################
|
|
1619
|
+
|
|
1620
|
+
ind_det = -1
|
|
1621
|
+
for plug_IDs in plugins_sorted:
|
|
1622
|
+
for ind_plugin, plugin in enumerate(plug_IDs):
|
|
1623
|
+
plug_name = plugin["value"].child("name").value()
|
|
1624
|
+
plug_init = plugin["value"].child("init").value()
|
|
1625
|
+
plug_settings = plugin["value"].child("params")
|
|
1626
|
+
self.splash_sc.showMessage(
|
|
1627
|
+
"Loading {:s} module: {:s}".format(plugin["type"], plug_name)
|
|
1628
|
+
)
|
|
1629
|
+
|
|
1630
|
+
if plugin["type"] == "move":
|
|
1631
|
+
plug_type = plug_settings.child(
|
|
1632
|
+
"main_settings", "move_type"
|
|
1633
|
+
).value()
|
|
1634
|
+
self.add_move(
|
|
1635
|
+
plug_name,
|
|
1636
|
+
plug_settings,
|
|
1637
|
+
plug_type,
|
|
1638
|
+
move_docks,
|
|
1639
|
+
move_forms,
|
|
1640
|
+
actuators_modules,
|
|
1641
|
+
)
|
|
1642
|
+
|
|
1643
|
+
if ind_plugin == 0: # should be a master type plugin
|
|
1644
|
+
if plugin["status"] != "Master":
|
|
1645
|
+
raise MasterSlaveError(
|
|
1646
|
+
f"The instrument {plug_name} should"
|
|
1647
|
+
f" be defined as Master"
|
|
1648
|
+
)
|
|
1649
|
+
if plug_init:
|
|
1650
|
+
actuators_modules[-1].init_hardware_ui()
|
|
1651
|
+
QtWidgets.QApplication.processEvents()
|
|
1652
|
+
self.poll_init(actuators_modules[-1])
|
|
1653
|
+
QtWidgets.QApplication.processEvents()
|
|
1654
|
+
master_controller = actuators_modules[-1].controller
|
|
1655
|
+
elif plugin["status"] == "Master" and len(plug_IDs) > 1:
|
|
1656
|
+
raise MasterSlaveError(
|
|
1657
|
+
f"The instrument {plug_name} defined as Master has to be "
|
|
1658
|
+
f"initialized (init checked in the preset) in order to init "
|
|
1659
|
+
f"its associated slave instrument"
|
|
1660
|
+
)
|
|
1661
|
+
else:
|
|
1662
|
+
if plugin["status"] != "Slave":
|
|
1663
|
+
raise MasterSlaveError(
|
|
1664
|
+
f"The instrument {plug_name} should"
|
|
1665
|
+
f" be defined as slave"
|
|
1666
|
+
)
|
|
1667
|
+
if plug_init:
|
|
1668
|
+
actuators_modules[-1].controller = master_controller
|
|
1669
|
+
actuators_modules[-1].init_hardware_ui()
|
|
1670
|
+
QtWidgets.QApplication.processEvents()
|
|
1671
|
+
self.poll_init(actuators_modules[-1])
|
|
1672
|
+
QtWidgets.QApplication.processEvents()
|
|
1673
|
+
else:
|
|
1674
|
+
ind_det += 1
|
|
1675
|
+
self.add_det(
|
|
1676
|
+
plug_name,
|
|
1677
|
+
plug_settings,
|
|
1678
|
+
det_docks_settings,
|
|
1679
|
+
det_docks_viewer,
|
|
1680
|
+
detector_modules,
|
|
1681
|
+
)
|
|
1682
|
+
QtWidgets.QApplication.processEvents()
|
|
1683
|
+
|
|
1684
|
+
if ind_plugin == 0: # should be a master type plugin
|
|
1685
|
+
if plugin["status"] != "Master":
|
|
1686
|
+
raise MasterSlaveError(
|
|
1687
|
+
f"The instrument {plug_name} should"
|
|
1688
|
+
f" be defined as Master"
|
|
1689
|
+
)
|
|
1690
|
+
if plug_init:
|
|
1691
|
+
detector_modules[-1].init_hardware_ui()
|
|
1692
|
+
QtWidgets.QApplication.processEvents()
|
|
1693
|
+
self.poll_init(detector_modules[-1])
|
|
1694
|
+
QtWidgets.QApplication.processEvents()
|
|
1695
|
+
master_controller = detector_modules[-1].controller
|
|
1696
|
+
elif plugin["status"] == "Master" and len(plug_IDs) > 1:
|
|
1697
|
+
raise MasterSlaveError(
|
|
1698
|
+
f"The instrument {plug_name} defined as Master has to be "
|
|
1699
|
+
f"initialized (init checked in the preset) in order to init "
|
|
1700
|
+
f"its associated slave instrument"
|
|
1701
|
+
)
|
|
1702
|
+
else:
|
|
1703
|
+
if plugin["status"] != "Slave":
|
|
1704
|
+
raise MasterSlaveError(
|
|
1705
|
+
f"The instrument {plug_name} should"
|
|
1706
|
+
f" be defined as Slave"
|
|
1707
|
+
)
|
|
1708
|
+
if plug_init:
|
|
1709
|
+
detector_modules[-1].controller = master_controller
|
|
1710
|
+
detector_modules[-1].init_hardware_ui()
|
|
1711
|
+
QtWidgets.QApplication.processEvents()
|
|
1712
|
+
self.poll_init(detector_modules[-1])
|
|
1713
|
+
QtWidgets.QApplication.processEvents()
|
|
1714
|
+
|
|
1715
|
+
detector_modules[-1].settings.child(
|
|
1716
|
+
"main_settings", "overshoot"
|
|
1717
|
+
).show()
|
|
1718
|
+
detector_modules[-1].overshoot_signal[bool].connect(
|
|
1719
|
+
self.stop_moves_from_overshoot
|
|
1720
|
+
)
|
|
1721
|
+
|
|
1722
|
+
QtWidgets.QApplication.processEvents()
|
|
1723
|
+
# restore dock state if saved
|
|
1724
|
+
|
|
1725
|
+
self.title = self.preset_file.stem
|
|
1726
|
+
path = layout_path.joinpath(self.title + ".dock")
|
|
1727
|
+
if path.is_file():
|
|
1728
|
+
self.load_layout_state(path)
|
|
1729
|
+
|
|
1730
|
+
self.mainwindow.setWindowTitle(f"PyMoDAQ Dashboard: {self.title}")
|
|
1731
|
+
if self.pid_module is not None:
|
|
1732
|
+
self.pid_module.set_module_manager(detector_modules, actuators_modules)
|
|
1733
|
+
return actuators_modules, detector_modules
|
|
1734
|
+
else:
|
|
1735
|
+
logger.error("Invalid file selected")
|
|
1736
|
+
return actuators_modules, detector_modules
|
|
1737
|
+
|
|
1738
|
+
def poll_init(self, module):
|
|
1739
|
+
is_init = False
|
|
1740
|
+
tstart = perf_counter()
|
|
1741
|
+
while not is_init:
|
|
1742
|
+
QThread.msleep(1000)
|
|
1743
|
+
QtWidgets.QApplication.processEvents()
|
|
1744
|
+
is_init = module.initialized_state
|
|
1745
|
+
if perf_counter() - tstart > 60: # timeout of 60sec
|
|
1746
|
+
break
|
|
1747
|
+
return is_init
|
|
1748
|
+
|
|
1749
|
+
def set_roi_configuration(self, filename):
|
|
1750
|
+
if not isinstance(filename, Path):
|
|
1751
|
+
filename = Path(filename)
|
|
1752
|
+
try:
|
|
1753
|
+
if filename.suffix == ".xml":
|
|
1754
|
+
file = filename.stem
|
|
1755
|
+
self.settings.child("loaded_files", "roi_file").setValue(file)
|
|
1756
|
+
self.update_status(
|
|
1757
|
+
"ROI configuration ({}) has been loaded".format(file),
|
|
1758
|
+
log_type="log",
|
|
1759
|
+
)
|
|
1760
|
+
self.roi_saver.set_file_roi(filename, show=False)
|
|
1761
|
+
|
|
1762
|
+
except Exception as e:
|
|
1763
|
+
logger.exception(str(e))
|
|
1764
|
+
|
|
1765
|
+
def set_remote_configuration(self, filename):
|
|
1766
|
+
if not isinstance(filename, Path):
|
|
1767
|
+
filename = Path(filename)
|
|
1768
|
+
ext = filename.suffix
|
|
1769
|
+
if ext == ".xml":
|
|
1770
|
+
self.remote_file = filename
|
|
1771
|
+
self.remote_manager.remote_changed.connect(self.activate_remote)
|
|
1772
|
+
self.remote_manager.set_file_remote(filename, show=False)
|
|
1773
|
+
self.settings.child("loaded_files", "remote_file").setValue(filename)
|
|
1774
|
+
self.remote_manager.set_remote_configuration()
|
|
1775
|
+
self.remote_dock.addWidget(self.remote_manager.remote_settings_tree)
|
|
1776
|
+
self.remote_dock.setVisible(True)
|
|
1777
|
+
|
|
1778
|
+
def activate_remote(self, remote_action, activate_all=False):
|
|
1779
|
+
"""
|
|
1780
|
+
remote_action = dict(action_type='shortcut' or 'joystick',
|
|
1781
|
+
action_name='blabla',
|
|
1782
|
+
action_dict= either:
|
|
1783
|
+
dict(shortcut=action.child(('shortcut')).value(), activated=True,
|
|
1784
|
+
name=f'action{ind:02d}', action=action.child(('action')).value(),
|
|
1785
|
+
module_name=module, module_type=module_type)
|
|
1786
|
+
|
|
1787
|
+
or:
|
|
1788
|
+
dict(joystickID=action.child(('joystickID')).value(),
|
|
1789
|
+
actionner_type=action.child(('actionner_type')).value(),
|
|
1790
|
+
actionnerID=action.child(('actionnerID')).value(),
|
|
1791
|
+
activated=True, name=f'action{ind:02d}',
|
|
1792
|
+
module_name=module, module_type=module_type)
|
|
1793
|
+
|
|
1794
|
+
"""
|
|
1795
|
+
if remote_action["action_type"] == "shortcut":
|
|
1796
|
+
if remote_action["action_name"] not in self.shortcuts:
|
|
1797
|
+
self.shortcuts[remote_action["action_name"]] = QtWidgets.QShortcut(
|
|
1798
|
+
QtGui.QKeySequence(remote_action["action_dict"]["shortcut"]),
|
|
1799
|
+
self.dockarea,
|
|
1800
|
+
)
|
|
1801
|
+
self.activate_shortcut(
|
|
1802
|
+
self.shortcuts[remote_action["action_name"]],
|
|
1803
|
+
remote_action["action_dict"],
|
|
1804
|
+
activate=remote_action["action_dict"]["activated"],
|
|
1805
|
+
)
|
|
1806
|
+
|
|
1807
|
+
elif remote_action["action_type"] == "joystick":
|
|
1808
|
+
if not self.ispygame_init:
|
|
1809
|
+
self.init_pygame()
|
|
1810
|
+
|
|
1811
|
+
if remote_action["action_name"] not in self.joysticks:
|
|
1812
|
+
self.joysticks[remote_action["action_name"]] = remote_action[
|
|
1813
|
+
"action_dict"
|
|
1814
|
+
]
|
|
1815
|
+
|
|
1816
|
+
def init_pygame(self):
|
|
1817
|
+
try:
|
|
1818
|
+
import pygame
|
|
1819
|
+
|
|
1820
|
+
self.pygame = pygame
|
|
1821
|
+
pygame.init()
|
|
1822
|
+
pygame.joystick.init()
|
|
1823
|
+
joystick_count = pygame.joystick.get_count()
|
|
1824
|
+
self.joysticks_obj = []
|
|
1825
|
+
for ind in range(joystick_count):
|
|
1826
|
+
self.joysticks_obj.append(dict(obj=pygame.joystick.Joystick(ind)))
|
|
1827
|
+
self.joysticks_obj[-1]["obj"].init()
|
|
1828
|
+
self.joysticks_obj[-1]["id"] = self.joysticks_obj[-1]["obj"].get_id()
|
|
1829
|
+
|
|
1830
|
+
self.remote_timer.timeout.connect(self.pygame_loop)
|
|
1831
|
+
self.ispygame_init = True
|
|
1832
|
+
self.remote_timer.start(10)
|
|
1833
|
+
|
|
1834
|
+
except ImportError as e:
|
|
1835
|
+
logger.warning("No pygame module installed. Needed for joystick control")
|
|
1836
|
+
|
|
1837
|
+
def pygame_loop(self):
|
|
1838
|
+
"""
|
|
1839
|
+
check is event correspond to any
|
|
1840
|
+
dict(joystickID=action.child(('joystickID')).value(),
|
|
1841
|
+
actionner_type=action.child(('actionner_type')).value(),
|
|
1842
|
+
actionnerID=action.child(('actionnerID')).value(),
|
|
1843
|
+
activated=True, name=f'action{ind:02d}',
|
|
1844
|
+
module_name=module, module_type=module_type)
|
|
1845
|
+
contained in self.joysticks
|
|
1846
|
+
"""
|
|
1847
|
+
|
|
1848
|
+
for action_dict in self.joysticks.values():
|
|
1849
|
+
if (
|
|
1850
|
+
action_dict["activated"]
|
|
1851
|
+
and action_dict["actionner_type"].lower() == "axis"
|
|
1852
|
+
):
|
|
1853
|
+
if action_dict["module_type"] == "act":
|
|
1854
|
+
joy = utils.find_dict_in_list_from_key_val(
|
|
1855
|
+
self.joysticks_obj, "id", action_dict["joystickID"]
|
|
1856
|
+
)
|
|
1857
|
+
val = joy["obj"].get_axis(action_dict["actionnerID"])
|
|
1858
|
+
if abs(val) > 1e-4:
|
|
1859
|
+
module = self.modules_manager.get_mod_from_name(
|
|
1860
|
+
action_dict["module_name"], mod=action_dict["module_type"]
|
|
1861
|
+
)
|
|
1862
|
+
action = getattr(module, action_dict["action"])
|
|
1863
|
+
if module.move_done_bool:
|
|
1864
|
+
action(
|
|
1865
|
+
val
|
|
1866
|
+
* 1
|
|
1867
|
+
* module.settings.child(
|
|
1868
|
+
"move_settings", "epsilon"
|
|
1869
|
+
).value()
|
|
1870
|
+
)
|
|
1871
|
+
|
|
1872
|
+
# # For other actions use the event loop
|
|
1873
|
+
for event in self.pygame.event.get(): # User did something.
|
|
1874
|
+
selection = dict([])
|
|
1875
|
+
if "joy" in event.dict:
|
|
1876
|
+
selection.update(dict(joy=event.joy))
|
|
1877
|
+
if event.type == self.pygame.JOYBUTTONDOWN:
|
|
1878
|
+
selection.update(dict(button=event.button))
|
|
1879
|
+
elif event.type == self.pygame.JOYAXISMOTION:
|
|
1880
|
+
selection.update(dict(axis=event.axis, value=event.value))
|
|
1881
|
+
elif event.type == self.pygame.JOYHATMOTION:
|
|
1882
|
+
selection.update(dict(hat=event.hat, value=event.value))
|
|
1883
|
+
if len(selection) > 1:
|
|
1884
|
+
for action_dict in self.joysticks.values():
|
|
1885
|
+
if action_dict["activated"]:
|
|
1886
|
+
module = self.modules_manager.get_mod_from_name(
|
|
1887
|
+
action_dict["module_name"], mod=action_dict["module_type"]
|
|
1888
|
+
)
|
|
1889
|
+
if action_dict["module_type"] == "det":
|
|
1890
|
+
action = getattr(module, action_dict["action"])
|
|
1891
|
+
else:
|
|
1892
|
+
action = getattr(module, action_dict["action"])
|
|
1893
|
+
|
|
1894
|
+
if action_dict["joystickID"] == selection["joy"]:
|
|
1895
|
+
if (
|
|
1896
|
+
action_dict["actionner_type"].lower() == "button"
|
|
1897
|
+
and "button" in selection
|
|
1898
|
+
):
|
|
1899
|
+
if action_dict["actionnerID"] == selection["button"]:
|
|
1900
|
+
action()
|
|
1901
|
+
elif (
|
|
1902
|
+
action_dict["actionner_type"].lower() == "hat"
|
|
1903
|
+
and "hat" in selection
|
|
1904
|
+
):
|
|
1905
|
+
if action_dict["actionnerID"] == selection["hat"]:
|
|
1906
|
+
action(selection["value"])
|
|
1907
|
+
|
|
1908
|
+
QtWidgets.QApplication.processEvents()
|
|
1909
|
+
|
|
1910
|
+
def activate_shortcut(self, shortcut, action=None, activate=True):
|
|
1911
|
+
"""
|
|
1912
|
+
action = dict(shortcut=action.child(('shortcut')).value(), activated=True,
|
|
1913
|
+
name=f'action{ind:02d}',
|
|
1914
|
+
action=action.child(('action')).value(), module_name=module)
|
|
1915
|
+
Parameters
|
|
1916
|
+
----------
|
|
1917
|
+
shortcut
|
|
1918
|
+
action
|
|
1919
|
+
activate
|
|
1920
|
+
|
|
1921
|
+
Returns
|
|
1922
|
+
-------
|
|
1923
|
+
|
|
1924
|
+
"""
|
|
1925
|
+
if activate:
|
|
1926
|
+
shortcut.activated.connect(self.create_activated_shortcut(action))
|
|
1927
|
+
else:
|
|
1928
|
+
try:
|
|
1929
|
+
shortcut.activated.disconnect()
|
|
1930
|
+
except Exception:
|
|
1931
|
+
pass
|
|
1932
|
+
|
|
1933
|
+
def create_activated_shortcut(self, action):
|
|
1934
|
+
module = self.modules_manager.get_mod_from_name(
|
|
1935
|
+
action["module_name"], mod=action["module_type"]
|
|
1936
|
+
)
|
|
1937
|
+
if action["module_type"] == "det":
|
|
1938
|
+
return lambda: getattr(module, action["action"])()
|
|
1939
|
+
else:
|
|
1940
|
+
return lambda: getattr(module, action["action"])()
|
|
1941
|
+
|
|
1942
|
+
def set_overshoot_configuration(self, filename):
|
|
1943
|
+
try:
|
|
1944
|
+
if not isinstance(filename, Path):
|
|
1945
|
+
filename = Path(filename)
|
|
1946
|
+
|
|
1947
|
+
if filename.suffix == ".xml":
|
|
1948
|
+
file = filename.stem
|
|
1949
|
+
self.settings.child("loaded_files", "overshoot_file").setValue(file)
|
|
1950
|
+
self.update_status(
|
|
1951
|
+
"Overshoot configuration ({}) has been loaded".format(file),
|
|
1952
|
+
log_type="log",
|
|
1953
|
+
)
|
|
1954
|
+
self.overshoot_manager.set_file_overshoot(filename, show=False)
|
|
1955
|
+
self.set_action_enabled("activate_overshoot", True)
|
|
1956
|
+
self.set_action_checked("activate_overshoot", False)
|
|
1957
|
+
self.get_action("activate_overshoot").trigger()
|
|
1958
|
+
|
|
1959
|
+
except Exception as e:
|
|
1960
|
+
logger.exception(str(e))
|
|
1961
|
+
|
|
1962
|
+
def activate_overshoot(self, status: bool):
|
|
1963
|
+
try:
|
|
1964
|
+
self.overshoot_manager.activate_overshoot(
|
|
1965
|
+
self.detector_modules, self.actuators_modules, status
|
|
1966
|
+
)
|
|
1967
|
+
except Exception as e:
|
|
1968
|
+
logger.warning(f"Could not load the overshoot file:\n{str(e)}")
|
|
1969
|
+
self.set_action_checked("activate_overshoot", False)
|
|
1970
|
+
self.set_action_enabled("activate_overshoot", False)
|
|
1971
|
+
|
|
1972
|
+
@property
|
|
1973
|
+
def move_modules(self):
|
|
1974
|
+
"""
|
|
1975
|
+
for back compatibility
|
|
1976
|
+
"""
|
|
1977
|
+
return self.actuators_modules
|
|
1978
|
+
|
|
1979
|
+
def set_preset_mode(self, filename):
|
|
1980
|
+
"""
|
|
1981
|
+
| Set the managers mode from the given filename.
|
|
1982
|
+
|
|
|
1983
|
+
| In case of "mock" or "canon" move, set the corresponding managers calling
|
|
1984
|
+
set_(*)_preset procedure.
|
|
1985
|
+
|
|
|
1986
|
+
| Else set the managers file using set_file_preset function.
|
|
1987
|
+
| Once done connect the move and detector modules to logger to recipe/transmit
|
|
1988
|
+
informations.
|
|
1989
|
+
|
|
1990
|
+
Finally update DAQ_scan_settings tree with :
|
|
1991
|
+
* Detectors
|
|
1992
|
+
* Move
|
|
1993
|
+
* plot_form.
|
|
1994
|
+
|
|
1995
|
+
=============== =========== =============================================
|
|
1996
|
+
**Parameters** **Type** **Description**
|
|
1997
|
+
*filename* string the name of the managers file to be treated
|
|
1998
|
+
=============== =========== =============================================
|
|
1999
|
+
|
|
2000
|
+
See Also
|
|
2001
|
+
--------
|
|
2002
|
+
set_Mock_preset, set_canon_preset, set_file_preset, add_status, update_status
|
|
2003
|
+
"""
|
|
2004
|
+
try:
|
|
2005
|
+
if not isinstance(filename, Path):
|
|
2006
|
+
filename = Path(filename)
|
|
2007
|
+
|
|
2008
|
+
self.get_action("preset_list").setCurrentText(filename.stem)
|
|
2009
|
+
|
|
2010
|
+
self.mainwindow.setVisible(False)
|
|
2011
|
+
for area in self.dockarea.tempAreas:
|
|
2012
|
+
area.window().setVisible(False)
|
|
2013
|
+
|
|
2014
|
+
self.splash_sc.show()
|
|
2015
|
+
QtWidgets.QApplication.processEvents()
|
|
2016
|
+
self.splash_sc.raise_()
|
|
2017
|
+
self.splash_sc.showMessage("Loading Modules, please wait")
|
|
2018
|
+
QtWidgets.QApplication.processEvents()
|
|
2019
|
+
self.clear_move_det_controllers()
|
|
2020
|
+
QtWidgets.QApplication.processEvents()
|
|
2021
|
+
|
|
2022
|
+
logger.info(f"Loading Preset file: {filename}")
|
|
2023
|
+
|
|
2024
|
+
try:
|
|
2025
|
+
actuators_modules, detector_modules = self.set_file_preset(filename)
|
|
2026
|
+
except (ActuatorError, DetectorError, MasterSlaveError) as error:
|
|
2027
|
+
self.splash_sc.close()
|
|
2028
|
+
self.mainwindow.setVisible(True)
|
|
2029
|
+
for area in self.dockarea.tempAreas:
|
|
2030
|
+
area.window().setVisible(True)
|
|
2031
|
+
messagebox(
|
|
2032
|
+
severity="critical",
|
|
2033
|
+
title="Preset loading error",
|
|
2034
|
+
text=f"""
|
|
2035
|
+
<p>{error}</p>
|
|
2036
|
+
<p>This error may be related to:</p>
|
|
2037
|
+
<p>Saved preset file is not compatible anymore.</p>
|
|
2038
|
+
<p>Please recreate the preset at <b>{filename}</b>.</p>
|
|
2039
|
+
""",
|
|
2040
|
+
)
|
|
2041
|
+
logger.exception(str(error))
|
|
2042
|
+
|
|
2043
|
+
self.quit_fun()
|
|
2044
|
+
return
|
|
2045
|
+
|
|
2046
|
+
if not (not actuators_modules and not detector_modules):
|
|
2047
|
+
self.update_status(
|
|
2048
|
+
"Preset mode ({}) has been loaded".format(filename.name),
|
|
2049
|
+
log_type="log",
|
|
2050
|
+
)
|
|
2051
|
+
self.settings.child("loaded_files", "preset_file").setValue(
|
|
2052
|
+
filename.name
|
|
2053
|
+
)
|
|
2054
|
+
self.actuators_modules = actuators_modules
|
|
2055
|
+
self.detector_modules = detector_modules
|
|
2056
|
+
|
|
2057
|
+
self.update_module_manager()
|
|
2058
|
+
|
|
2059
|
+
#####################################################
|
|
2060
|
+
self.overshoot_manager = OvershootManager(
|
|
2061
|
+
det_modules=[det.title for det in detector_modules],
|
|
2062
|
+
actuators_modules=[move.title for move in actuators_modules],
|
|
2063
|
+
)
|
|
2064
|
+
# load overshoot if present
|
|
2065
|
+
file = filename.name
|
|
2066
|
+
path = overshoot_path.joinpath(file)
|
|
2067
|
+
if path.is_file():
|
|
2068
|
+
self.set_overshoot_configuration(path)
|
|
2069
|
+
|
|
2070
|
+
self.remote_manager = RemoteManager(
|
|
2071
|
+
actuators=[move.title for move in actuators_modules],
|
|
2072
|
+
detectors=[det.title for det in detector_modules],
|
|
2073
|
+
)
|
|
2074
|
+
# load remote file if present
|
|
2075
|
+
file = filename.name
|
|
2076
|
+
path = remote_path.joinpath(file)
|
|
2077
|
+
if path.is_file():
|
|
2078
|
+
self.set_remote_configuration(path)
|
|
2079
|
+
|
|
2080
|
+
self.roi_saver = ROISaver(det_modules=detector_modules)
|
|
2081
|
+
# load roi saver if present
|
|
2082
|
+
path = roi_path.joinpath(file)
|
|
2083
|
+
if path.is_file():
|
|
2084
|
+
self.set_roi_configuration(path)
|
|
2085
|
+
|
|
2086
|
+
# connecting to logger
|
|
2087
|
+
for mov in actuators_modules:
|
|
2088
|
+
mov.init_signal.connect(self.update_init_tree)
|
|
2089
|
+
for det in detector_modules:
|
|
2090
|
+
det.init_signal.connect(self.update_init_tree)
|
|
2091
|
+
|
|
2092
|
+
self.splash_sc.close()
|
|
2093
|
+
self.mainwindow.setVisible(True)
|
|
2094
|
+
for area in self.dockarea.tempAreas:
|
|
2095
|
+
area.window().setVisible(True)
|
|
2096
|
+
if self.pid_window is not None:
|
|
2097
|
+
self.pid_window.show()
|
|
2098
|
+
|
|
2099
|
+
self.load_preset_menu.setEnabled(False)
|
|
2100
|
+
self.set_action_enabled("load_preset", False)
|
|
2101
|
+
self.set_action_enabled("preset_list", False)
|
|
2102
|
+
self.overshoot_menu.setEnabled(True)
|
|
2103
|
+
self.roi_menu.setEnabled(True)
|
|
2104
|
+
self.remote_menu.setEnabled(True)
|
|
2105
|
+
self.extensions_menu.setEnabled(True)
|
|
2106
|
+
self.file_menu.setEnabled(True)
|
|
2107
|
+
self.settings_menu.setEnabled(True)
|
|
2108
|
+
self.update_init_tree()
|
|
2109
|
+
|
|
2110
|
+
self.preset_loaded_signal.emit(True)
|
|
2111
|
+
|
|
2112
|
+
logger.info(f"Preset file: {filename} has been loaded")
|
|
2113
|
+
|
|
2114
|
+
except Exception as e:
|
|
2115
|
+
logger.exception(str(e))
|
|
2116
|
+
|
|
2117
|
+
def update_init_tree(self):
|
|
2118
|
+
for act in self.actuators_modules:
|
|
2119
|
+
name = "".join(act.title.split()) # remove empty spaces
|
|
2120
|
+
if act.title not in [
|
|
2121
|
+
ac.title()
|
|
2122
|
+
for ac in putils.iter_children_params(
|
|
2123
|
+
self.settings.child("actuators"), []
|
|
2124
|
+
)
|
|
2125
|
+
]:
|
|
2126
|
+
self.settings.child("actuators").addChild(
|
|
2127
|
+
{"title": act.title, "name": name, "type": "led", "value": False}
|
|
2128
|
+
)
|
|
2129
|
+
QtWidgets.QApplication.processEvents()
|
|
2130
|
+
self.settings.child("actuators", name).setValue(act.initialized_state)
|
|
2131
|
+
|
|
2132
|
+
for det in self.detector_modules:
|
|
2133
|
+
name = "".join(det.title.split()) # remove empty spaces
|
|
2134
|
+
if det.title not in [
|
|
2135
|
+
de.title()
|
|
2136
|
+
for de in putils.iter_children_params(
|
|
2137
|
+
self.settings.child("detectors"), []
|
|
2138
|
+
)
|
|
2139
|
+
]:
|
|
2140
|
+
self.settings.child("detectors").addChild(
|
|
2141
|
+
{"title": det.title, "name": name, "type": "led", "value": False}
|
|
2142
|
+
)
|
|
2143
|
+
QtWidgets.QApplication.processEvents()
|
|
2144
|
+
self.settings.child("detectors", name).setValue(det.initialized_state)
|
|
2145
|
+
|
|
2146
|
+
def do_stuff_from_out_bounds(self, out_of_bounds: bool):
|
|
2147
|
+
if out_of_bounds:
|
|
2148
|
+
logger.warning(f"Some actuators reached their bounds")
|
|
2149
|
+
if self.scan_module is not None:
|
|
2150
|
+
logger.warning(f"Stopping the DAQScan for out of bounds")
|
|
2151
|
+
self.scan_module.stop_scan()
|
|
2152
|
+
|
|
2153
|
+
def stop_moves_from_overshoot(self, overshoot):
|
|
2154
|
+
self.overshoot = overshoot
|
|
2155
|
+
self.stop_moves()
|
|
2156
|
+
|
|
2157
|
+
def stop_moves(self, *args, **kwargs):
|
|
2158
|
+
"""
|
|
2159
|
+
Foreach module of the move module object list, stop motion.
|
|
2160
|
+
|
|
2161
|
+
See Also
|
|
2162
|
+
--------
|
|
2163
|
+
stop_scan, DAQ_Move_main.daq_move.stop_motion
|
|
2164
|
+
"""
|
|
2165
|
+
if self.scan_module is not None:
|
|
2166
|
+
self.scan_module.stop_scan()
|
|
2167
|
+
|
|
2168
|
+
for mod in self.actuators_modules:
|
|
2169
|
+
mod.stop_motion()
|
|
2170
|
+
|
|
2171
|
+
def show_log(self):
|
|
2172
|
+
import webbrowser
|
|
2173
|
+
|
|
2174
|
+
webbrowser.open(logging.getLogger("pymodaq").handlers[0].baseFilename)
|
|
2175
|
+
|
|
2176
|
+
def show_config(self, config):
|
|
2177
|
+
from pymodaq_gui.utils.widgets.tree_toml import TreeFromToml
|
|
2178
|
+
|
|
2179
|
+
config_tree = TreeFromToml(config)
|
|
2180
|
+
config_tree.show_dialog()
|
|
2181
|
+
|
|
2182
|
+
def setup_docks(self):
|
|
2183
|
+
# %% create logger dock
|
|
2184
|
+
self.logger_dock = Dock("Logger")
|
|
2185
|
+
self.logger_list = QtWidgets.QListWidget()
|
|
2186
|
+
self.logger_list.setMinimumWidth(300)
|
|
2187
|
+
|
|
2188
|
+
splitter = QtWidgets.QSplitter(Qt.Vertical)
|
|
2189
|
+
splitter.addWidget(self.settings_tree)
|
|
2190
|
+
splitter.addWidget(self.logger_list)
|
|
2191
|
+
self.logger_dock.addWidget(splitter)
|
|
2192
|
+
|
|
2193
|
+
self.remote_dock = Dock("Remote controls")
|
|
2194
|
+
self.dockarea.addDock(self.remote_dock, "top")
|
|
2195
|
+
self.dockarea.addDock(self.logger_dock, "above", self.remote_dock)
|
|
2196
|
+
self.logger_dock.setVisible(True)
|
|
2197
|
+
|
|
2198
|
+
self.remote_dock.setVisible(False)
|
|
2199
|
+
self.preset_manager = PresetManager(
|
|
2200
|
+
path=self.preset_path, extra_params=self.extra_params
|
|
2201
|
+
)
|
|
2202
|
+
|
|
2203
|
+
@property
|
|
2204
|
+
def menubar(self):
|
|
2205
|
+
return self._menubar
|
|
2206
|
+
|
|
2207
|
+
def parameter_tree_changed(self, param, changes):
|
|
2208
|
+
"""
|
|
2209
|
+
Foreach value changed, update :
|
|
2210
|
+
* Viewer in case of **DAQ_type** parameter name
|
|
2211
|
+
* visibility of button in case of **show_averaging** parameter name
|
|
2212
|
+
* visibility of naverage in case of **live_averaging** parameter name
|
|
2213
|
+
* scale of axis **else** (in 2D pymodaq type)
|
|
2214
|
+
|
|
2215
|
+
Once done emit the update settings signal to link the commit.
|
|
2216
|
+
|
|
2217
|
+
|
|
2218
|
+
"""
|
|
2219
|
+
|
|
2220
|
+
for param, change, data in changes:
|
|
2221
|
+
path = self.settings.childPath(param)
|
|
2222
|
+
if path is not None:
|
|
2223
|
+
childName = ".".join(path)
|
|
2224
|
+
else:
|
|
2225
|
+
childName = param.name()
|
|
2226
|
+
if change == "childAdded":
|
|
2227
|
+
pass
|
|
2228
|
+
elif change == "value":
|
|
2229
|
+
if param.name() == "log_level":
|
|
2230
|
+
logger.setLevel(param.value())
|
|
2231
|
+
elif change == "parent":
|
|
2232
|
+
pass
|
|
2233
|
+
|
|
2234
|
+
def show_about(self):
|
|
2235
|
+
self.splash_sc.setVisible(True)
|
|
2236
|
+
self.splash_sc.showMessage(
|
|
2237
|
+
f"PyMoDAQ version {get_version('pymodaq')}\n"
|
|
2238
|
+
f"Modular Acquisition with Python\n"
|
|
2239
|
+
f"Written by Sébastien Weber"
|
|
2240
|
+
)
|
|
2241
|
+
|
|
2242
|
+
def check_update(self, show=True):
|
|
2243
|
+
try:
|
|
2244
|
+
packages = ["pymodaq_utils", "pymodaq_data", "pymodaq_gui", "pymodaq"]
|
|
2245
|
+
current_versions = [version_mod.parse(get_version(p)) for p in packages]
|
|
2246
|
+
available_versions = [
|
|
2247
|
+
version_mod.parse(get_pypi_pymodaq(p)["version"]) for p in packages
|
|
2248
|
+
]
|
|
2249
|
+
new_versions = np.greater(available_versions, current_versions)
|
|
2250
|
+
# Combine package and version information and select only the ones with a newer version available
|
|
2251
|
+
|
|
2252
|
+
packages_data = np.array(
|
|
2253
|
+
list(zip(packages, current_versions, available_versions))
|
|
2254
|
+
)[new_versions]
|
|
2255
|
+
|
|
2256
|
+
if len(packages_data) > 0:
|
|
2257
|
+
# Create a QDialog window and different graphical components
|
|
2258
|
+
dialog = QtWidgets.QDialog()
|
|
2259
|
+
dialog.setWindowTitle("Update check")
|
|
2260
|
+
|
|
2261
|
+
vlayout = QtWidgets.QVBoxLayout()
|
|
2262
|
+
|
|
2263
|
+
message_label = QLabel(
|
|
2264
|
+
"New versions of PyMoDAQ packages available!\nUse your package manager to update."
|
|
2265
|
+
)
|
|
2266
|
+
message_label.setAlignment(Qt.AlignCenter)
|
|
2267
|
+
|
|
2268
|
+
table = PymodaqUpdateTableWidget()
|
|
2269
|
+
table.setRowCount(len(packages_data))
|
|
2270
|
+
table.setColumnCount(3)
|
|
2271
|
+
table.setHorizontalHeaderLabels(
|
|
2272
|
+
["Package", "Current version", "New version"]
|
|
2273
|
+
)
|
|
2274
|
+
|
|
2275
|
+
for p in packages_data:
|
|
2276
|
+
table.append_row(p[0], p[1], p[2])
|
|
2277
|
+
|
|
2278
|
+
# The vlayout contains the message, the table and the buttons
|
|
2279
|
+
# and is connected to the dialog window
|
|
2280
|
+
vlayout.addWidget(message_label)
|
|
2281
|
+
vlayout.addWidget(table)
|
|
2282
|
+
dialog.setLayout(vlayout)
|
|
2283
|
+
|
|
2284
|
+
ret = dialog.exec()
|
|
2285
|
+
|
|
2286
|
+
else:
|
|
2287
|
+
if show:
|
|
2288
|
+
msgBox = QMessageBox()
|
|
2289
|
+
msgBox.setWindowTitle("Update check")
|
|
2290
|
+
msgBox.setText("Everything is up to date!")
|
|
2291
|
+
ret = msgBox.exec()
|
|
2292
|
+
except Exception as e:
|
|
2293
|
+
logger.exception("Error while checking the available PyMoDAQ version")
|
|
2294
|
+
|
|
2295
|
+
return False
|
|
2296
|
+
|
|
2297
|
+
def show_file_attributes(self, type_info="dataset"):
|
|
2298
|
+
"""
|
|
2299
|
+
Switch the type_info value.
|
|
2300
|
+
|
|
2301
|
+
In case of :
|
|
2302
|
+
* *scan* : Set parameters showing top false
|
|
2303
|
+
* *dataset* : Set parameters showing top false
|
|
2304
|
+
* *managers* : Set parameters showing top false.
|
|
2305
|
+
Add the save/cancel buttons to the accept/reject dialog
|
|
2306
|
+
(to save managers parameters in a xml file).
|
|
2307
|
+
|
|
2308
|
+
Finally, in case of accepted managers type info,
|
|
2309
|
+
save the managers parameters in a xml file.
|
|
2310
|
+
|
|
2311
|
+
=============== =========== ====================================
|
|
2312
|
+
**Parameters** **Type** **Description**
|
|
2313
|
+
*type_info* string The file type information between
|
|
2314
|
+
* scan
|
|
2315
|
+
* dataset
|
|
2316
|
+
* managers
|
|
2317
|
+
=============== =========== ====================================
|
|
2318
|
+
"""
|
|
2319
|
+
dialog = QtWidgets.QDialog()
|
|
2320
|
+
vlayout = QtWidgets.QVBoxLayout()
|
|
2321
|
+
tree = ParameterTree()
|
|
2322
|
+
tree.setMinimumWidth(400)
|
|
2323
|
+
tree.setMinimumHeight(500)
|
|
2324
|
+
if type_info == "scan":
|
|
2325
|
+
tree.setParameters(self.scan_attributes, showTop=False)
|
|
2326
|
+
elif type_info == "dataset":
|
|
2327
|
+
tree.setParameters(self.dataset_attributes, showTop=False)
|
|
2328
|
+
|
|
2329
|
+
vlayout.addWidget(tree)
|
|
2330
|
+
dialog.setLayout(vlayout)
|
|
2331
|
+
buttonBox = QDialogButtonBox(parent=dialog)
|
|
2332
|
+
buttonBox.addButton("Cancel", QDialogButtonBox.ButtonRole.RejectRole)
|
|
2333
|
+
buttonBox.addButton("Apply", QDialogButtonBox.ButtonRole.AcceptRole)
|
|
2334
|
+
buttonBox.rejected.connect(dialog.reject)
|
|
2335
|
+
buttonBox.accepted.connect(dialog.accept)
|
|
2336
|
+
|
|
2337
|
+
vlayout.addWidget(buttonBox)
|
|
2338
|
+
dialog.setWindowTitle("Fill in information about this {}".format(type_info))
|
|
2339
|
+
res = dialog.exec()
|
|
2340
|
+
return res
|
|
2341
|
+
|
|
2342
|
+
def show_help(self):
|
|
2343
|
+
QtGui.QDesktopServices.openUrl(QtCore.QUrl("http://pymodaq.cnrs.fr"))
|
|
2344
|
+
|
|
2345
|
+
def update_status(self, txt, wait_time=0, log_type=None):
|
|
2346
|
+
"""
|
|
2347
|
+
Show the txt message in the status bar with a delay of wait_time ms.
|
|
2348
|
+
|
|
2349
|
+
=============== =========== =======================
|
|
2350
|
+
**Parameters** **Type** **Description**
|
|
2351
|
+
*txt* string The message to show
|
|
2352
|
+
*wait_time* int the delay of showing
|
|
2353
|
+
*log_type* string the type of the log
|
|
2354
|
+
=============== =========== =======================
|
|
2355
|
+
"""
|
|
2356
|
+
try:
|
|
2357
|
+
if log_type is not None:
|
|
2358
|
+
self.status_signal.emit(txt)
|
|
2359
|
+
logging.info(txt)
|
|
2360
|
+
except Exception as e:
|
|
2361
|
+
pass
|
|
2362
|
+
|
|
2363
|
+
|
|
2364
|
+
def main():
|
|
2365
|
+
from pymodaq_gui.utils.utils import mkQApp
|
|
2366
|
+
from pymodaq.utils.gui_utils.loader_utils import load_dashboard_with_preset
|
|
2367
|
+
|
|
2368
|
+
# Create application and main window
|
|
2369
|
+
app = mkQApp('Dashboard')
|
|
2370
|
+
|
|
2371
|
+
win = QtWidgets.QMainWindow()
|
|
2372
|
+
area = DockArea()
|
|
2373
|
+
win.setCentralWidget(area)
|
|
2374
|
+
win.resize(1000, 500)
|
|
2375
|
+
win.setWindowTitle("PyMoDAQ Dashboard")
|
|
2376
|
+
|
|
2377
|
+
# Command-line argument parsing
|
|
2378
|
+
parser = argparse.ArgumentParser(prog="dashboard", description="PyMoDAQ dashboard")
|
|
2379
|
+
parser.add_argument("-p", "--preset", metavar="PRESET_NAME", help="preset name to load")
|
|
2380
|
+
args = parser.parse_args()
|
|
2381
|
+
|
|
2382
|
+
# If preset name is supplied, load dashboard with this preset
|
|
2383
|
+
if args.preset:
|
|
2384
|
+
load_dashboard_with_preset(args.preset)
|
|
2385
|
+
|
|
2386
|
+
# If no command-line arguments are supplied, start empty
|
|
2387
|
+
else:
|
|
2388
|
+
prog = DashBoard(area)
|
|
2389
|
+
win.show()
|
|
2390
|
+
|
|
2391
|
+
# Run application
|
|
2392
|
+
app.exec()
|
|
2393
|
+
|
|
2394
|
+
|
|
2395
|
+
if __name__ == "__main__":
|
|
2396
|
+
main()
|