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.
Files changed (33) hide show
  1. CHANGELOG.md +42 -42
  2. PKG-INFO +1 -1
  3. bec_widgets/cli/client.py +30 -0
  4. bec_widgets/cli/client_utils.py +5 -4
  5. bec_widgets/cli/server.py +21 -9
  6. bec_widgets/examples/plugin_example_pyside/tictactoe.py +0 -1
  7. bec_widgets/utils/bec_connector.py +5 -4
  8. bec_widgets/utils/bec_dispatcher.py +9 -13
  9. bec_widgets/utils/bec_widget.py +5 -1
  10. bec_widgets/widgets/dap_combo_box/__init__.py +0 -0
  11. bec_widgets/widgets/dap_combo_box/dap_combo_box.py +185 -0
  12. bec_widgets/widgets/dap_combo_box/dap_combo_box.pyproject +1 -0
  13. bec_widgets/widgets/dap_combo_box/dap_combo_box_plugin.py +54 -0
  14. bec_widgets/widgets/dap_combo_box/register_dap_combo_box.py +15 -0
  15. bec_widgets/widgets/device_browser/device_item/device_item.py +4 -1
  16. bec_widgets/widgets/figure/figure.py +4 -1
  17. bec_widgets/widgets/figure/plots/image/image.py +4 -1
  18. bec_widgets/widgets/figure/plots/image/image_item.py +4 -1
  19. bec_widgets/widgets/figure/plots/motor_map/motor_map.py +5 -2
  20. bec_widgets/widgets/figure/plots/plot_base.py +4 -1
  21. bec_widgets/widgets/figure/plots/waveform/waveform.py +7 -4
  22. bec_widgets/widgets/figure/plots/waveform/waveform_curve.py +4 -2
  23. bec_widgets/widgets/ring_progress_bar/ring_progress_bar.py +8 -5
  24. bec_widgets/widgets/scan_control/scan_control.py +183 -21
  25. bec_widgets/widgets/scan_control/scan_group_box.py +40 -13
  26. bec_widgets/widgets/toggle/toggle.py +3 -3
  27. bec_widgets/widgets/waveform/waveform_widget.py +4 -2
  28. {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/METADATA +1 -1
  29. {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/RECORD +33 -28
  30. pyproject.toml +1 -1
  31. {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/WHEEL +0 -0
  32. {bec_widgets-0.103.0.dist-info → bec_widgets-0.105.0.dist-info}/entry_points.txt +0 -0
  33. {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
- print("Double Clicked")
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
- print(f"Error in applying config: {e}")
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
- print(f"Validation error when applying config to BECImageShow: {e}")
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
- print(f"Warning: '{key}' is not a recognized property.")
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
- print(f"Error in applying config: {e}")
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
- print(f"The device '{motor}' does not have defined limits.")
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
- print(f"Warning: '{key}' is not a recognized property.")
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
- print(f"Validation error when applying config to BECWaveform1D: {e}")
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
- print(f"Color: {color}")
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
- print(f"Scan index {scan_index} out of range.")
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
- print(
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
- print(f"Warning: '{key}' is not a recognized property.")
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
- print(
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
- print(
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
- print(
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
- print(f"{instruction_type} not supported yet.")
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
- print("no rings to get size from setting size to 10x10")
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, parent=None, client=None, gui_id: str | None = None, allowed_scans: list | None = None
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
- super().__init__(client=client, gui_id=gui_id)
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
- # Scan list - allowed scans for the GUI
44
- self.allowed_scans = allowed_scans
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 = QGridLayout(scan_selection_group)
130
+ self.scan_selection_layout = QVBoxLayout(scan_selection_group)
93
131
  self.comboBox_scan_selection = QComboBox(scan_selection_group)
94
132
 
95
- # Run button
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
- # Stop button
138
+ ## Stop button
99
139
  self.button_stop_scan = StopButton(parent=scan_selection_group)
100
-
101
- self.scan_selection_layout.addWidget(self.comboBox_scan_selection, 0, 0, 1, 2)
102
- self.scan_selection_layout.addWidget(self.button_run_scan, 1, 0)
103
- self.scan_selection_layout.addWidget(self.button_stop_scan, 1, 1)
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
- @Slot()
298
- def run_scan(self):
299
- """Starts the selected scan with the given parameters."""
300
- self.scan_started.emit()
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)