0
0
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:
2024-09-04 09:58:30 +02:00
committed by wyzula_j
parent fe8dc55eb1
commit d28f9b04c4
2 changed files with 88 additions and 21 deletions

View File

@ -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."""

View File

@ -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