mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat(scan_control): scan control remember the previously set parameters and shares kwarg settings across scans
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from bec_lib.endpoints import MessageEndpoints
|
||||
from pydantic import BaseModel, Field
|
||||
from qtpy.QtCore import Property, Signal, Slot
|
||||
from qtpy.QtWidgets import (
|
||||
QApplication,
|
||||
@ -16,6 +16,7 @@ from qtpy.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeSlot
|
||||
from bec_widgets.utils import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
@ -30,8 +31,9 @@ class ScanParameterConfig(BaseModel):
|
||||
|
||||
|
||||
class ScanControlConfig(ConnectionConfig):
|
||||
default_scan: Optional[str] = Field(None)
|
||||
scans: Optional[dict[str, ScanParameterConfig]] = Field(None)
|
||||
# default_scan: Optional[str] = Field(None) # TODO implement later
|
||||
# allowed_scans: Optional[list] = Field(None)
|
||||
scans: Optional[dict[str, ScanParameterConfig]] = defaultdict(dict)
|
||||
|
||||
|
||||
class ScanControl(BECWidget, QWidget):
|
||||
@ -62,9 +64,11 @@ class ScanControl(BECWidget, QWidget):
|
||||
|
||||
# Main layout
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(5, 5, 5, 5)
|
||||
self.arg_box = None
|
||||
self.kwarg_boxes = []
|
||||
self.expert_mode = False # TODO implement in the future versions
|
||||
self.previous_scan = None
|
||||
|
||||
# Scan list - allowed scans for the GUI
|
||||
self.allowed_scans = allowed_scans # FIXME should be changed to config.allowed_scans
|
||||
@ -83,6 +87,7 @@ class ScanControl(BECWidget, QWidget):
|
||||
self.layout.addWidget(self.scan_selection_group)
|
||||
|
||||
# Connect signals
|
||||
self.comboBox_scan_selection.view().pressed.connect(self.save_current_scan_parameters)
|
||||
self.comboBox_scan_selection.currentIndexChanged.connect(self.on_scan_selection_changed)
|
||||
self.button_run_scan.clicked.connect(self.run_scan)
|
||||
|
||||
@ -151,6 +156,7 @@ class ScanControl(BECWidget, QWidget):
|
||||
"""Callback for scan selection combo box"""
|
||||
selected_scan_name = self.comboBox_scan_selection.currentText()
|
||||
self.scan_selected.emit(selected_scan_name)
|
||||
self.restore_scan_parameters(selected_scan_name)
|
||||
|
||||
@Property(str)
|
||||
def current_scan(self):
|
||||
@ -320,18 +326,57 @@ class ScanControl(BECWidget, QWidget):
|
||||
box.deleteLater()
|
||||
self.kwarg_boxes = []
|
||||
|
||||
@Slot()
|
||||
def get_scan_parameters(self):
|
||||
"""Returns the scan parameters for the selected scan."""
|
||||
def get_scan_parameters(self, bec_object: bool = True):
|
||||
"""
|
||||
Returns the scan parameters for the selected scan.
|
||||
|
||||
Args:
|
||||
bec_object(bool): If True, returns the BEC object for the scan parameters such as device objects.
|
||||
"""
|
||||
args = []
|
||||
kwargs = {}
|
||||
if self.arg_box is not None:
|
||||
args = self.arg_box.get_parameters()
|
||||
args = self.arg_box.get_parameters(bec_object)
|
||||
for box in self.kwarg_boxes:
|
||||
box_kwargs = box.get_parameters()
|
||||
box_kwargs = box.get_parameters(bec_object)
|
||||
kwargs.update(box_kwargs)
|
||||
return args, kwargs
|
||||
|
||||
def restore_scan_parameters(self, scan_name: str):
|
||||
"""
|
||||
Restores the scan parameters for the given scan name
|
||||
|
||||
Args:
|
||||
scan_name(str): Name of the scan to restore the parameters for.
|
||||
"""
|
||||
scan_params = self.config.scans.get(scan_name, None)
|
||||
if scan_params is None and self.previous_scan is None:
|
||||
return
|
||||
|
||||
if scan_params is None and self.previous_scan is not None:
|
||||
previous_scan_params = self.config.scans.get(self.previous_scan, None)
|
||||
self._restore_kwargs(previous_scan_params)
|
||||
return
|
||||
|
||||
if scan_params.args is not None and self.arg_box is not None:
|
||||
self.arg_box.set_parameters(scan_params.args)
|
||||
|
||||
self._restore_kwargs(scan_params)
|
||||
|
||||
def _restore_kwargs(self, scan_params: ScanParameterConfig):
|
||||
"""Restores the kwargs for the given scan parameters."""
|
||||
if scan_params.kwargs is not None and self.kwarg_boxes is not None:
|
||||
for box in self.kwarg_boxes:
|
||||
box.set_parameters(scan_params.kwargs)
|
||||
|
||||
def save_current_scan_parameters(self):
|
||||
"""Saves the current scan parameters to the scan control config for further use."""
|
||||
scan_name = self.comboBox_scan_selection.currentText()
|
||||
self.previous_scan = scan_name
|
||||
args, kwargs = self.get_scan_parameters(False)
|
||||
scan_params = ScanParameterConfig(name=scan_name, args=args, kwargs=kwargs)
|
||||
self.config.scans[scan_name] = scan_params
|
||||
|
||||
@Slot()
|
||||
def run_scan(self):
|
||||
"""Starts the selected scan with the given parameters."""
|
||||
|
@ -230,32 +230,35 @@ class ScanGroupBox(QGroupBox):
|
||||
widget.deleteLater()
|
||||
self.widgets = self.widgets[: -len(self.inputs)]
|
||||
|
||||
def get_parameters(self):
|
||||
def get_parameters(self, device_object: bool = True):
|
||||
"""
|
||||
Returns the parameters from the widgets in the scan control layout formated to run scan from BEC.
|
||||
"""
|
||||
if self.box_type == "args":
|
||||
return self._get_arg_parameterts()
|
||||
return self._get_arg_parameterts(device_object=device_object)
|
||||
elif self.box_type == "kwargs":
|
||||
return self._get_kwarg_parameters()
|
||||
return self._get_kwarg_parameters(device_object=device_object)
|
||||
|
||||
def _get_arg_parameterts(self):
|
||||
def _get_arg_parameterts(self, device_object: bool = True):
|
||||
args = []
|
||||
for i in range(1, self.layout.rowCount()):
|
||||
for j in range(self.layout.columnCount()):
|
||||
widget = self.layout.itemAtPosition(i, j).widget()
|
||||
if isinstance(widget, DeviceLineEdit):
|
||||
value = widget.get_device()
|
||||
else:
|
||||
value = WidgetIO.get_value(widget)
|
||||
args.append(value)
|
||||
try: # In case that the bundle size changes
|
||||
widget = self.layout.itemAtPosition(i, j).widget()
|
||||
if isinstance(widget, DeviceLineEdit) and device_object:
|
||||
value = widget.get_device()
|
||||
else:
|
||||
value = WidgetIO.get_value(widget)
|
||||
args.append(value)
|
||||
except AttributeError:
|
||||
continue
|
||||
return args
|
||||
|
||||
def _get_kwarg_parameters(self):
|
||||
def _get_kwarg_parameters(self, device_object: bool = True):
|
||||
kwargs = {}
|
||||
for i in range(self.layout.columnCount()):
|
||||
widget = self.layout.itemAtPosition(1, i).widget()
|
||||
if isinstance(widget, DeviceLineEdit):
|
||||
if isinstance(widget, DeviceLineEdit) and device_object:
|
||||
value = widget.get_device()
|
||||
else:
|
||||
value = WidgetIO.get_value(widget)
|
||||
@ -273,3 +276,22 @@ class ScanGroupBox(QGroupBox):
|
||||
if isinstance(widget, DeviceLineEdit):
|
||||
widget_rows += 1
|
||||
return widget_rows
|
||||
|
||||
def set_parameters(self, parameters: list | dict):
|
||||
if self.box_type == "args":
|
||||
self._set_arg_parameters(parameters)
|
||||
elif self.box_type == "kwargs":
|
||||
self._set_kwarg_parameters(parameters)
|
||||
|
||||
def _set_arg_parameters(self, parameters: list):
|
||||
while len(parameters) != len(self.widgets):
|
||||
self.add_widget_bundle()
|
||||
for i, parameter in enumerate(parameters):
|
||||
WidgetIO.set_value(self.widgets[i], parameter)
|
||||
|
||||
def _set_kwarg_parameters(self, parameters: dict):
|
||||
for widget in self.widgets:
|
||||
for key, value in parameters.items():
|
||||
if widget.arg_name == key:
|
||||
WidgetIO.set_value(widget, value)
|
||||
break
|
||||
|
Reference in New Issue
Block a user