bec-widgets 0.103.0__py3-none-any.whl → 0.105.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +42 -42
- PKG-INFO +1 -1
- bec_widgets/cli/client.py +30 -0
- bec_widgets/cli/client_utils.py +5 -4
- bec_widgets/cli/server.py +21 -9
- bec_widgets/examples/plugin_example_pyside/tictactoe.py +0 -1
- bec_widgets/utils/bec_connector.py +5 -4
- bec_widgets/utils/bec_dispatcher.py +9 -13
- bec_widgets/utils/bec_widget.py +5 -1
- bec_widgets/widgets/dap_combo_box/__init__.py +0 -0
- bec_widgets/widgets/dap_combo_box/dap_combo_box.py +185 -0
- bec_widgets/widgets/dap_combo_box/dap_combo_box.pyproject +1 -0
- bec_widgets/widgets/dap_combo_box/dap_combo_box_plugin.py +54 -0
- bec_widgets/widgets/dap_combo_box/register_dap_combo_box.py +15 -0
- bec_widgets/widgets/device_browser/device_item/device_item.py +4 -1
- bec_widgets/widgets/figure/figure.py +4 -1
- bec_widgets/widgets/figure/plots/image/image.py +4 -1
- bec_widgets/widgets/figure/plots/image/image_item.py +4 -1
- bec_widgets/widgets/figure/plots/motor_map/motor_map.py +5 -2
- bec_widgets/widgets/figure/plots/plot_base.py +4 -1
- bec_widgets/widgets/figure/plots/waveform/waveform.py +7 -4
- bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +4 -2
- bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py +8 -5
- bec_widgets/widgets/scan_control/scan_control.py +183 -21
- bec_widgets/widgets/scan_control/scan_group_box.py +40 -13
- bec_widgets/widgets/toggle/toggle.py +3 -3
- bec_widgets/widgets/waveform/waveform_widget.py +4 -2
- {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/METADATA +1 -1
- {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/RECORD +33 -28
- pyproject.toml +1 -1
- {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/WHEEL +0 -0
- {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/entry_points.txt +0 -0
- {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from typing import TYPE_CHECKING
|
4
4
|
|
5
|
+
from bec_lib.logger import bec_logger
|
5
6
|
from qtpy.QtCore import QMimeData, Qt
|
6
7
|
from qtpy.QtGui import QDrag
|
7
8
|
from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
|
@@ -9,6 +10,8 @@ from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
|
|
9
10
|
if TYPE_CHECKING:
|
10
11
|
from qtpy.QtGui import QMouseEvent
|
11
12
|
|
13
|
+
logger = bec_logger.logger
|
14
|
+
|
12
15
|
|
13
16
|
class DeviceItem(QWidget):
|
14
17
|
def __init__(self, device: str) -> None:
|
@@ -37,7 +40,7 @@ class DeviceItem(QWidget):
|
|
37
40
|
drag.exec_(Qt.MoveAction)
|
38
41
|
|
39
42
|
def mouseDoubleClickEvent(self, event: QMouseEvent) -> None:
|
40
|
-
|
43
|
+
logger.debug("Double Clicked")
|
41
44
|
# TODO: Implement double click action for opening the device properties dialog
|
42
45
|
return super().mouseDoubleClickEvent(event)
|
43
46
|
|
@@ -7,6 +7,7 @@ from typing import Literal, Optional
|
|
7
7
|
|
8
8
|
import numpy as np
|
9
9
|
import pyqtgraph as pg
|
10
|
+
from bec_lib.logger import bec_logger
|
10
11
|
from pydantic import Field, ValidationError, field_validator
|
11
12
|
from qtpy.QtCore import Signal as pyqtSignal
|
12
13
|
from qtpy.QtWidgets import QWidget
|
@@ -20,6 +21,8 @@ from bec_widgets.widgets.figure.plots.motor_map.motor_map import BECMotorMap, Mo
|
|
20
21
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
21
22
|
from bec_widgets.widgets.figure.plots.waveform.waveform import BECWaveform, Waveform1DConfig
|
22
23
|
|
24
|
+
logger = bec_logger.logger
|
25
|
+
|
23
26
|
|
24
27
|
class FigureConfig(ConnectionConfig):
|
25
28
|
"""Configuration for BECFigure. Inheriting from ConnectionConfig widget_class and gui_id"""
|
@@ -179,7 +182,7 @@ class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
|
179
182
|
try:
|
180
183
|
config = FigureConfig(**config)
|
181
184
|
except ValidationError as e:
|
182
|
-
|
185
|
+
logger.error(f"Error in applying config: {e}")
|
183
186
|
return
|
184
187
|
self.config = config
|
185
188
|
|
@@ -5,6 +5,7 @@ from typing import Any, Literal, Optional
|
|
5
5
|
|
6
6
|
import numpy as np
|
7
7
|
from bec_lib.endpoints import MessageEndpoints
|
8
|
+
from bec_lib.logger import bec_logger
|
8
9
|
from pydantic import BaseModel, Field, ValidationError
|
9
10
|
from qtpy.QtCore import QThread
|
10
11
|
from qtpy.QtWidgets import QWidget
|
@@ -19,6 +20,8 @@ from bec_widgets.widgets.figure.plots.image.image_processor import (
|
|
19
20
|
)
|
20
21
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
21
22
|
|
23
|
+
logger = bec_logger.logger
|
24
|
+
|
22
25
|
|
23
26
|
class ImageConfig(SubplotConfig):
|
24
27
|
images: dict[str, ImageItemConfig] = Field(
|
@@ -130,7 +133,7 @@ class BECImageShow(BECPlotBase):
|
|
130
133
|
try:
|
131
134
|
config = ImageConfig(**config)
|
132
135
|
except ValidationError as e:
|
133
|
-
|
136
|
+
logger.error(f"Validation error when applying config to BECImageShow: {e}")
|
134
137
|
return
|
135
138
|
self.config = config
|
136
139
|
self.plot_item.clear()
|
@@ -4,6 +4,7 @@ from typing import TYPE_CHECKING, Literal, Optional
|
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
import pyqtgraph as pg
|
7
|
+
from bec_lib.logger import bec_logger
|
7
8
|
from pydantic import Field
|
8
9
|
|
9
10
|
from bec_widgets.utils import BECConnector, ConnectionConfig
|
@@ -12,6 +13,8 @@ from bec_widgets.widgets.figure.plots.image.image_processor import ImageStats, P
|
|
12
13
|
if TYPE_CHECKING:
|
13
14
|
from bec_widgets.widgets.figure.plots.image.image import BECImageShow
|
14
15
|
|
16
|
+
logger = bec_logger.logger
|
17
|
+
|
15
18
|
|
16
19
|
class ImageItemConfig(ConnectionConfig):
|
17
20
|
parent_id: Optional[str] = Field(None, description="The parent plot of the image.")
|
@@ -133,7 +136,7 @@ class BECImageItem(BECConnector, pg.ImageItem):
|
|
133
136
|
if key in method_map:
|
134
137
|
method_map[key](value)
|
135
138
|
else:
|
136
|
-
|
139
|
+
logger.warning(f"Warning: '{key}' is not a recognized property.")
|
137
140
|
|
138
141
|
def set_fft(self, enable: bool = False):
|
139
142
|
"""
|
@@ -6,6 +6,7 @@ from typing import Optional, Union
|
|
6
6
|
import numpy as np
|
7
7
|
import pyqtgraph as pg
|
8
8
|
from bec_lib.endpoints import MessageEndpoints
|
9
|
+
from bec_lib.logger import bec_logger
|
9
10
|
from pydantic import Field, ValidationError, field_validator
|
10
11
|
from pydantic_core import PydanticCustomError
|
11
12
|
from qtpy import QtCore, QtGui
|
@@ -17,6 +18,8 @@ from bec_widgets.utils import Colors, EntryValidator
|
|
17
18
|
from bec_widgets.widgets.figure.plots.plot_base import BECPlotBase, SubplotConfig
|
18
19
|
from bec_widgets.widgets.figure.plots.waveform.waveform import Signal, SignalData
|
19
20
|
|
21
|
+
logger = bec_logger.logger
|
22
|
+
|
20
23
|
|
21
24
|
class MotorMapConfig(SubplotConfig):
|
22
25
|
signals: Optional[Signal] = Field(None, description="Signals of the motor map")
|
@@ -101,7 +104,7 @@ class BECMotorMap(BECPlotBase):
|
|
101
104
|
try:
|
102
105
|
config = MotorMapConfig(**config)
|
103
106
|
except ValidationError as e:
|
104
|
-
|
107
|
+
logger.error(f"Error in applying config: {e}")
|
105
108
|
return
|
106
109
|
|
107
110
|
self.config = config
|
@@ -440,7 +443,7 @@ class BECMotorMap(BECPlotBase):
|
|
440
443
|
return limits
|
441
444
|
except AttributeError: # TODO maybe not needed, if no limits it returns [0,0]
|
442
445
|
# If the motor doesn't have a 'limits' attribute, return a default value or raise a custom exception
|
443
|
-
|
446
|
+
logger.error(f"The device '{motor}' does not have defined limits.")
|
444
447
|
return None
|
445
448
|
|
446
449
|
@Slot()
|
@@ -4,6 +4,7 @@ from typing import Literal, Optional
|
|
4
4
|
|
5
5
|
import bec_qthemes
|
6
6
|
import pyqtgraph as pg
|
7
|
+
from bec_lib.logger import bec_logger
|
7
8
|
from pydantic import BaseModel, Field
|
8
9
|
from qtpy.QtCore import Signal, Slot
|
9
10
|
from qtpy.QtWidgets import QApplication, QWidget
|
@@ -11,6 +12,8 @@ from qtpy.QtWidgets import QApplication, QWidget
|
|
11
12
|
from bec_widgets.utils import BECConnector, ConnectionConfig
|
12
13
|
from bec_widgets.utils.crosshair import Crosshair
|
13
14
|
|
15
|
+
logger = bec_logger.logger
|
16
|
+
|
14
17
|
|
15
18
|
class AxisConfig(BaseModel):
|
16
19
|
title: Optional[str] = Field(None, description="The title of the axes.")
|
@@ -164,7 +167,7 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
|
164
167
|
if key in method_map:
|
165
168
|
method_map[key](value)
|
166
169
|
else:
|
167
|
-
|
170
|
+
logger.warning(f"Warning: '{key}' is not a recognized property.")
|
168
171
|
|
169
172
|
def apply_axis_config(self):
|
170
173
|
"""Apply the axis configuration to the plot widget."""
|
@@ -8,6 +8,7 @@ import pyqtgraph as pg
|
|
8
8
|
from bec_lib import messages
|
9
9
|
from bec_lib.device import ReadoutPriority
|
10
10
|
from bec_lib.endpoints import MessageEndpoints
|
11
|
+
from bec_lib.logger import bec_logger
|
11
12
|
from pydantic import Field, ValidationError, field_validator
|
12
13
|
from pyqtgraph.exporters import MatplotlibExporter
|
13
14
|
from qtpy.QtCore import Signal as pyqtSignal
|
@@ -23,6 +24,8 @@ from bec_widgets.widgets.figure.plots.waveform.waveform_curve import (
|
|
23
24
|
SignalData,
|
24
25
|
)
|
25
26
|
|
27
|
+
logger = bec_logger.logger
|
28
|
+
|
26
29
|
|
27
30
|
class Waveform1DConfig(SubplotConfig):
|
28
31
|
color_palette: Optional[str] = Field(
|
@@ -139,7 +142,7 @@ class BECWaveform(BECPlotBase):
|
|
139
142
|
try:
|
140
143
|
config = Waveform1DConfig(**config)
|
141
144
|
except ValidationError as e:
|
142
|
-
|
145
|
+
logger.error(f"Validation error when applying config to BECWaveform1D: {e}")
|
143
146
|
return
|
144
147
|
|
145
148
|
self.config = config
|
@@ -553,7 +556,7 @@ class BECWaveform(BECPlotBase):
|
|
553
556
|
format="HEX",
|
554
557
|
)[len(self.plot_item.curves)]
|
555
558
|
)
|
556
|
-
|
559
|
+
logger.info(f"Color: {color}")
|
557
560
|
|
558
561
|
# Create curve by config
|
559
562
|
curve_config = CurveConfig(
|
@@ -1291,7 +1294,7 @@ class BECWaveform(BECPlotBase):
|
|
1291
1294
|
try:
|
1292
1295
|
self.scan_id = self.queue.scan_storage.storage[scan_index].scan_id
|
1293
1296
|
except IndexError:
|
1294
|
-
|
1297
|
+
logger.error(f"Scan index {scan_index} out of range.")
|
1295
1298
|
return
|
1296
1299
|
elif scan_id is not None:
|
1297
1300
|
self.scan_id = scan_id
|
@@ -1317,7 +1320,7 @@ class BECWaveform(BECPlotBase):
|
|
1317
1320
|
except ImportError:
|
1318
1321
|
pd = None
|
1319
1322
|
if output == "pandas":
|
1320
|
-
|
1323
|
+
logger.warning(
|
1321
1324
|
"Pandas is not installed. "
|
1322
1325
|
"Please install pandas using 'pip install pandas'."
|
1323
1326
|
"Output will be dictionary instead."
|
@@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
import pyqtgraph as pg
|
7
|
+
from bec_lib.logger import bec_logger
|
7
8
|
from pydantic import BaseModel, Field, field_validator
|
8
|
-
from pydantic_core import PydanticCustomError
|
9
9
|
from qtpy import QtCore
|
10
10
|
|
11
11
|
from bec_widgets.utils import BECConnector, Colors, ConnectionConfig
|
@@ -13,6 +13,8 @@ from bec_widgets.utils import BECConnector, Colors, ConnectionConfig
|
|
13
13
|
if TYPE_CHECKING:
|
14
14
|
from bec_widgets.widgets.figure.plots.waveform import BECWaveform1D
|
15
15
|
|
16
|
+
logger = bec_logger.logger
|
17
|
+
|
16
18
|
|
17
19
|
class SignalData(BaseModel):
|
18
20
|
"""The data configuration of a signal in the 1D waveform widget for x and y axis."""
|
@@ -177,7 +179,7 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
|
177
179
|
if key in method_map:
|
178
180
|
method_map[key](value)
|
179
181
|
else:
|
180
|
-
|
182
|
+
logger.warning(f"Warning: '{key}' is not a recognized property.")
|
181
183
|
|
182
184
|
def set_color(self, color: str, symbol_color: Optional[str] = None):
|
183
185
|
"""
|
@@ -4,6 +4,7 @@ from typing import Literal, Optional
|
|
4
4
|
|
5
5
|
import pyqtgraph as pg
|
6
6
|
from bec_lib.endpoints import MessageEndpoints
|
7
|
+
from bec_lib.logger import bec_logger
|
7
8
|
from pydantic import Field, field_validator
|
8
9
|
from pydantic_core import PydanticCustomError
|
9
10
|
from qtpy import QtCore, QtGui
|
@@ -14,6 +15,8 @@ from bec_widgets.utils import Colors, ConnectionConfig, EntryValidator
|
|
14
15
|
from bec_widgets.utils.bec_widget import BECWidget
|
15
16
|
from bec_widgets.widgets.ring_progress_bar.ring import Ring, RingConfig
|
16
17
|
|
18
|
+
logger = bec_logger.logger
|
19
|
+
|
17
20
|
|
18
21
|
class RingProgressBarConfig(ConnectionConfig):
|
19
22
|
color_map: Optional[str] = Field(
|
@@ -38,7 +41,7 @@ class RingProgressBarConfig(ConnectionConfig):
|
|
38
41
|
min_number_of_bars = values.data.get("min_number_of_bars", None)
|
39
42
|
max_number_of_bars = values.data.get("max_number_of_bars", None)
|
40
43
|
if min_number_of_bars is not None and max_number_of_bars is not None:
|
41
|
-
|
44
|
+
logger.info(
|
42
45
|
f"Number of bars adjusted to be between defined min:{min_number_of_bars} and max:{max_number_of_bars} number of bars."
|
43
46
|
)
|
44
47
|
v = max(min_number_of_bars, min(v, max_number_of_bars))
|
@@ -318,7 +321,7 @@ class RingProgressBar(BECWidget, QWidget):
|
|
318
321
|
ring = self._find_ring_by_index(ring_index)
|
319
322
|
if isinstance(values, list):
|
320
323
|
values = values[0]
|
321
|
-
|
324
|
+
logger.warning(
|
322
325
|
f"Warning: Only a single value can be set for a single progress bar. Using the first value in the list {values}"
|
323
326
|
)
|
324
327
|
ring.set_value(values)
|
@@ -380,7 +383,7 @@ class RingProgressBar(BECWidget, QWidget):
|
|
380
383
|
ring = self._find_ring_by_index(bar_index)
|
381
384
|
if isinstance(widths, list):
|
382
385
|
widths = widths[0]
|
383
|
-
|
386
|
+
logger.warning(
|
384
387
|
f"Warning: Only a single line width can be set for a single progress bar. Using the first value in the list {widths}"
|
385
388
|
)
|
386
389
|
ring.set_line_width(widths)
|
@@ -487,7 +490,7 @@ class RingProgressBar(BECWidget, QWidget):
|
|
487
490
|
for index, device in enumerate(devices):
|
488
491
|
self._hook_readback(index, device, start[index], end[index])
|
489
492
|
else:
|
490
|
-
|
493
|
+
logger.error(f"{instruction_type} not supported yet.")
|
491
494
|
|
492
495
|
# elif instruction_type == "device_progress":
|
493
496
|
# print("hook device_progress")
|
@@ -609,7 +612,7 @@ class RingProgressBar(BECWidget, QWidget):
|
|
609
612
|
Calculate the minimum size of the widget.
|
610
613
|
"""
|
611
614
|
if not self.config.rings:
|
612
|
-
|
615
|
+
logger.warning("no rings to get size from setting size to 10x10")
|
613
616
|
return QSize(10, 10)
|
614
617
|
ring_widths = [self.config.rings[i].line_width for i in range(self.config.num_bars)]
|
615
618
|
total_width = sum(ring_widths) + self.config.gap * (self.config.num_bars - 1)
|
@@ -1,21 +1,39 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
from typing import Optional
|
3
|
+
|
1
4
|
from bec_lib.endpoints import MessageEndpoints
|
5
|
+
from pydantic import BaseModel, Field
|
2
6
|
from qtpy.QtCore import Property, Signal, Slot
|
3
7
|
from qtpy.QtWidgets import (
|
4
8
|
QApplication,
|
5
9
|
QComboBox,
|
6
|
-
QGridLayout,
|
7
10
|
QGroupBox,
|
8
11
|
QHBoxLayout,
|
12
|
+
QLabel,
|
9
13
|
QPushButton,
|
10
14
|
QSizePolicy,
|
11
15
|
QVBoxLayout,
|
12
16
|
QWidget,
|
13
17
|
)
|
14
18
|
|
19
|
+
from bec_widgets.qt_utils.error_popups import SafeSlot
|
20
|
+
from bec_widgets.utils import ConnectionConfig
|
15
21
|
from bec_widgets.utils.bec_widget import BECWidget
|
16
|
-
from bec_widgets.utils.colors import apply_theme
|
17
22
|
from bec_widgets.widgets.scan_control.scan_group_box import ScanGroupBox
|
18
23
|
from bec_widgets.widgets.stop_button.stop_button import StopButton
|
24
|
+
from bec_widgets.widgets.toggle.toggle import ToggleSwitch
|
25
|
+
|
26
|
+
|
27
|
+
class ScanParameterConfig(BaseModel):
|
28
|
+
name: str
|
29
|
+
args: Optional[list] = Field(None)
|
30
|
+
kwargs: Optional[dict] = Field(None)
|
31
|
+
|
32
|
+
|
33
|
+
class ScanControlConfig(ConnectionConfig):
|
34
|
+
default_scan: Optional[str] = Field(None)
|
35
|
+
allowed_scans: Optional[list] = Field(None)
|
36
|
+
scans: Optional[dict[str, ScanParameterConfig]] = defaultdict(dict)
|
19
37
|
|
20
38
|
|
21
39
|
class ScanControl(BECWidget, QWidget):
|
@@ -26,9 +44,20 @@ class ScanControl(BECWidget, QWidget):
|
|
26
44
|
scan_selected = Signal(str)
|
27
45
|
|
28
46
|
def __init__(
|
29
|
-
self,
|
47
|
+
self,
|
48
|
+
parent=None,
|
49
|
+
client=None,
|
50
|
+
config: ScanControlConfig | dict | None = None,
|
51
|
+
gui_id: str | None = None,
|
52
|
+
allowed_scans: list | None = None,
|
53
|
+
default_scan: str | None = None,
|
30
54
|
):
|
31
|
-
|
55
|
+
|
56
|
+
if config is None:
|
57
|
+
config = ScanControlConfig(
|
58
|
+
widget_class=self.__class__.__name__, allowed_scans=allowed_scans
|
59
|
+
)
|
60
|
+
super().__init__(client=client, gui_id=gui_id, config=config)
|
32
61
|
QWidget.__init__(self, parent=parent)
|
33
62
|
|
34
63
|
# Client from BEC + shortcuts to device manager and scans
|
@@ -36,12 +65,16 @@ class ScanControl(BECWidget, QWidget):
|
|
36
65
|
|
37
66
|
# Main layout
|
38
67
|
self.layout = QVBoxLayout(self)
|
68
|
+
self.layout.setContentsMargins(5, 5, 5, 5)
|
39
69
|
self.arg_box = None
|
40
70
|
self.kwarg_boxes = []
|
41
71
|
self.expert_mode = False # TODO implement in the future versions
|
72
|
+
self.previous_scan = None
|
73
|
+
self.last_scan_found = None
|
42
74
|
|
43
|
-
#
|
44
|
-
self.
|
75
|
+
# Widget Default Parameters
|
76
|
+
self.config.default_scan = default_scan
|
77
|
+
self.config.allowed_scans = allowed_scans
|
45
78
|
|
46
79
|
# Create and set main layout
|
47
80
|
self._init_UI()
|
@@ -56,7 +89,12 @@ class ScanControl(BECWidget, QWidget):
|
|
56
89
|
self.scan_selection_group.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
57
90
|
self.layout.addWidget(self.scan_selection_group)
|
58
91
|
|
92
|
+
# Default scan from config
|
93
|
+
if self.config.default_scan is not None:
|
94
|
+
self.comboBox_scan_selection.setCurrentText(self.config.default_scan)
|
95
|
+
|
59
96
|
# Connect signals
|
97
|
+
self.comboBox_scan_selection.view().pressed.connect(self.save_current_scan_parameters)
|
60
98
|
self.comboBox_scan_selection.currentIndexChanged.connect(self.on_scan_selection_changed)
|
61
99
|
self.button_run_scan.clicked.connect(self.run_scan)
|
62
100
|
|
@@ -89,18 +127,35 @@ class ScanControl(BECWidget, QWidget):
|
|
89
127
|
"""
|
90
128
|
|
91
129
|
scan_selection_group = QGroupBox("Scan Selection", self)
|
92
|
-
self.scan_selection_layout =
|
130
|
+
self.scan_selection_layout = QVBoxLayout(scan_selection_group)
|
93
131
|
self.comboBox_scan_selection = QComboBox(scan_selection_group)
|
94
132
|
|
95
|
-
#
|
133
|
+
# Buttons
|
134
|
+
self.button_layout = QHBoxLayout()
|
135
|
+
## Run button
|
96
136
|
self.button_run_scan = QPushButton("Start", scan_selection_group)
|
97
137
|
self.button_run_scan.setStyleSheet("background-color: #559900; color: white")
|
98
|
-
|
138
|
+
## Stop button
|
99
139
|
self.button_stop_scan = StopButton(parent=scan_selection_group)
|
100
|
-
|
101
|
-
self.
|
102
|
-
self.
|
103
|
-
|
140
|
+
## Add buttons to layout
|
141
|
+
self.button_layout.addWidget(self.button_run_scan)
|
142
|
+
self.button_layout.addWidget(self.button_stop_scan)
|
143
|
+
|
144
|
+
# Label to reload the last scan parameters
|
145
|
+
self.toggle_layout = QHBoxLayout()
|
146
|
+
## Label
|
147
|
+
self.last_scan_label = QLabel("Restore last scan parameters", scan_selection_group)
|
148
|
+
## Switch toggle button
|
149
|
+
self.toggle = ToggleSwitch(parent=scan_selection_group, checked=False)
|
150
|
+
self.toggle.enabled.connect(self.request_last_executed_scan_parameters)
|
151
|
+
## Add label and switch to layout
|
152
|
+
self.toggle_layout.addWidget(self.last_scan_label)
|
153
|
+
self.toggle_layout.addWidget(self.toggle)
|
154
|
+
|
155
|
+
# Add widgets to layout
|
156
|
+
self.scan_selection_layout.addWidget(self.comboBox_scan_selection)
|
157
|
+
self.scan_selection_layout.addLayout(self.button_layout)
|
158
|
+
self.scan_selection_layout.addLayout(self.toggle_layout)
|
104
159
|
|
105
160
|
return scan_selection_group
|
106
161
|
|
@@ -109,7 +164,7 @@ class ScanControl(BECWidget, QWidget):
|
|
109
164
|
self.available_scans = self.client.connector.get(
|
110
165
|
MessageEndpoints.available_scans()
|
111
166
|
).resource
|
112
|
-
if self.allowed_scans is None:
|
167
|
+
if self.config.allowed_scans is None:
|
113
168
|
supported_scans = ["ScanBase", "SyncFlyScanBase", "AsyncFlyScanBase"]
|
114
169
|
allowed_scans = [
|
115
170
|
scan_name
|
@@ -118,13 +173,50 @@ class ScanControl(BECWidget, QWidget):
|
|
118
173
|
]
|
119
174
|
|
120
175
|
else:
|
121
|
-
allowed_scans = self.allowed_scans
|
176
|
+
allowed_scans = self.config.allowed_scans
|
122
177
|
self.comboBox_scan_selection.addItems(allowed_scans)
|
123
178
|
|
124
179
|
def on_scan_selection_changed(self, index: int):
|
125
180
|
"""Callback for scan selection combo box"""
|
126
181
|
selected_scan_name = self.comboBox_scan_selection.currentText()
|
127
182
|
self.scan_selected.emit(selected_scan_name)
|
183
|
+
self.request_last_executed_scan_parameters()
|
184
|
+
self.restore_scan_parameters(selected_scan_name)
|
185
|
+
|
186
|
+
@Slot()
|
187
|
+
def request_last_executed_scan_parameters(self):
|
188
|
+
"""
|
189
|
+
Requests the last executed scan parameters from BEC and restores them to the scan control widget.
|
190
|
+
"""
|
191
|
+
enabled = self.toggle.checked
|
192
|
+
current_scan = self.comboBox_scan_selection.currentText()
|
193
|
+
if enabled:
|
194
|
+
history = self.client.connector.lrange(MessageEndpoints.scan_queue_history(), 0, -1)
|
195
|
+
|
196
|
+
for scan in history:
|
197
|
+
scan_name = scan.content["info"]["request_blocks"][-1]["msg"].content["scan_type"]
|
198
|
+
if scan_name == current_scan:
|
199
|
+
args_dict = scan.content["info"]["request_blocks"][-1]["msg"].content[
|
200
|
+
"parameter"
|
201
|
+
]["args"]
|
202
|
+
args_list = []
|
203
|
+
for key, value in args_dict.items():
|
204
|
+
args_list.append(key)
|
205
|
+
args_list.extend(value)
|
206
|
+
if len(args_list) > 1 and self.arg_box is not None:
|
207
|
+
self.arg_box.set_parameters(args_list)
|
208
|
+
kwargs = scan.content["info"]["request_blocks"][-1]["msg"].content["parameter"][
|
209
|
+
"kwargs"
|
210
|
+
]
|
211
|
+
if kwargs and self.kwarg_boxes:
|
212
|
+
for box in self.kwarg_boxes:
|
213
|
+
box.set_parameters(kwargs)
|
214
|
+
self.last_scan_found = True
|
215
|
+
break
|
216
|
+
else:
|
217
|
+
self.last_scan_found = False
|
218
|
+
else:
|
219
|
+
self.last_scan_found = False
|
128
220
|
|
129
221
|
@Property(str)
|
130
222
|
def current_scan(self):
|
@@ -151,6 +243,29 @@ class ScanControl(BECWidget, QWidget):
|
|
151
243
|
"""
|
152
244
|
self.current_scan = scan_name
|
153
245
|
|
246
|
+
@Property(bool)
|
247
|
+
def hide_scan_remember_toggle(self):
|
248
|
+
"""Property to hide the scan remember toggle."""
|
249
|
+
return not self.toggle.isVisible()
|
250
|
+
|
251
|
+
@hide_scan_remember_toggle.setter
|
252
|
+
def hide_scan_remember_toggle(self, hide: bool):
|
253
|
+
"""Setter for the hide_scan_remember_toggle property.
|
254
|
+
|
255
|
+
Args:
|
256
|
+
hide(bool): Hide or show the scan remember toggle.
|
257
|
+
"""
|
258
|
+
self.show_scan_remember_toggle(not hide)
|
259
|
+
|
260
|
+
@Slot(bool)
|
261
|
+
def show_scan_remember_toggle(self, show: bool):
|
262
|
+
"""Shows or hides the scan control buttons."""
|
263
|
+
self.toggle.setVisible(show)
|
264
|
+
self.last_scan_label.setVisible(show)
|
265
|
+
|
266
|
+
show_group = show or self.button_run_scan.isVisible()
|
267
|
+
self.scan_selection_group.setVisible(show_group)
|
268
|
+
|
154
269
|
@Property(bool)
|
155
270
|
def hide_scan_control_buttons(self):
|
156
271
|
"""Property to hide the scan control buttons."""
|
@@ -294,17 +409,64 @@ class ScanControl(BECWidget, QWidget):
|
|
294
409
|
box.deleteLater()
|
295
410
|
self.kwarg_boxes = []
|
296
411
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
412
|
+
def get_scan_parameters(self, bec_object: bool = True):
|
413
|
+
"""
|
414
|
+
Returns the scan parameters for the selected scan.
|
415
|
+
|
416
|
+
Args:
|
417
|
+
bec_object(bool): If True, returns the BEC object for the scan parameters such as device objects.
|
418
|
+
"""
|
301
419
|
args = []
|
302
420
|
kwargs = {}
|
303
421
|
if self.arg_box is not None:
|
304
|
-
args = self.arg_box.get_parameters()
|
422
|
+
args = self.arg_box.get_parameters(bec_object)
|
305
423
|
for box in self.kwarg_boxes:
|
306
|
-
box_kwargs = box.get_parameters()
|
424
|
+
box_kwargs = box.get_parameters(bec_object)
|
307
425
|
kwargs.update(box_kwargs)
|
426
|
+
return args, kwargs
|
427
|
+
|
428
|
+
def restore_scan_parameters(self, scan_name: str):
|
429
|
+
"""
|
430
|
+
Restores the scan parameters for the given scan name
|
431
|
+
|
432
|
+
Args:
|
433
|
+
scan_name(str): Name of the scan to restore the parameters for.
|
434
|
+
"""
|
435
|
+
if self.last_scan_found is True:
|
436
|
+
return
|
437
|
+
scan_params = self.config.scans.get(scan_name, None)
|
438
|
+
if scan_params is None and self.previous_scan is None:
|
439
|
+
return
|
440
|
+
|
441
|
+
if scan_params is None and self.previous_scan is not None:
|
442
|
+
previous_scan_params = self.config.scans.get(self.previous_scan, None)
|
443
|
+
self._restore_kwargs(previous_scan_params.kwargs)
|
444
|
+
return
|
445
|
+
|
446
|
+
if scan_params.args is not None and self.arg_box is not None:
|
447
|
+
self.arg_box.set_parameters(scan_params.args)
|
448
|
+
|
449
|
+
self._restore_kwargs(scan_params.kwargs)
|
450
|
+
|
451
|
+
def _restore_kwargs(self, scan_kwargs: dict):
|
452
|
+
"""Restores the kwargs for the given scan parameters."""
|
453
|
+
if scan_kwargs is not None and self.kwarg_boxes is not None:
|
454
|
+
for box in self.kwarg_boxes:
|
455
|
+
box.set_parameters(scan_kwargs)
|
456
|
+
|
457
|
+
def save_current_scan_parameters(self):
|
458
|
+
"""Saves the current scan parameters to the scan control config for further use."""
|
459
|
+
scan_name = self.comboBox_scan_selection.currentText()
|
460
|
+
self.previous_scan = scan_name
|
461
|
+
args, kwargs = self.get_scan_parameters(False)
|
462
|
+
scan_params = ScanParameterConfig(name=scan_name, args=args, kwargs=kwargs)
|
463
|
+
self.config.scans[scan_name] = scan_params
|
464
|
+
|
465
|
+
@SafeSlot(popup_error=True)
|
466
|
+
def run_scan(self):
|
467
|
+
"""Starts the selected scan with the given parameters."""
|
468
|
+
self.scan_started.emit()
|
469
|
+
args, kwargs = self.get_scan_parameters()
|
308
470
|
scan_function = getattr(self.scans, self.comboBox_scan_selection.currentText())
|
309
471
|
if callable(scan_function):
|
310
472
|
scan_function(*args, **kwargs)
|