mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
fix(scan_control): adapted widget to scan BEC gui config
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
|
import qdarktheme
|
||||||
from bec_lib.endpoints import MessageEndpoints
|
from bec_lib.endpoints import MessageEndpoints
|
||||||
from qtpy.QtWidgets import (
|
from qtpy.QtWidgets import (
|
||||||
QApplication,
|
QApplication,
|
||||||
@ -13,6 +14,7 @@ from qtpy.QtWidgets import (
|
|||||||
QLayout,
|
QLayout,
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
QPushButton,
|
QPushButton,
|
||||||
|
QSizePolicy,
|
||||||
QSpinBox,
|
QSpinBox,
|
||||||
QTableWidget,
|
QTableWidget,
|
||||||
QTableWidgetItem,
|
QTableWidgetItem,
|
||||||
@ -20,8 +22,12 @@ from qtpy.QtWidgets import (
|
|||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
from bec_widgets.utils.bec_dispatcher import BECDispatcher
|
from bec_widgets.utils import BECConnector
|
||||||
from bec_widgets.utils.widget_io import WidgetIO
|
from bec_widgets.utils.widget_io import WidgetIO
|
||||||
|
from bec_widgets.widgets import StopButton
|
||||||
|
|
||||||
|
# TODO GENERAL
|
||||||
|
# - extract
|
||||||
|
|
||||||
|
|
||||||
class ScanArgType:
|
class ScanArgType:
|
||||||
@ -30,24 +36,52 @@ class ScanArgType:
|
|||||||
INT = "int"
|
INT = "int"
|
||||||
BOOL = "bool"
|
BOOL = "bool"
|
||||||
STR = "str"
|
STR = "str"
|
||||||
|
DEVICEBASE = "DeviceBase"
|
||||||
|
LITERALS = "dict"
|
||||||
|
|
||||||
|
|
||||||
class ScanControl(QWidget):
|
class ArgLabel(QLabel):
|
||||||
|
def __init__(self, text: str, parent=None, arg_name: str = None, *args, **kwargs):
|
||||||
|
super().__init__(text, parent=parent, *args, **kwargs)
|
||||||
|
self.arg_name = arg_name
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceLineEdit(BECConnector, QLineEdit):
|
||||||
|
def __init__(self, parent=None, client=None, gui_id: str | None = None):
|
||||||
|
super().__init__(client=client, gui_id=gui_id)
|
||||||
|
QLineEdit.__init__(self, parent=parent)
|
||||||
|
|
||||||
|
self.get_bec_shortcuts()
|
||||||
|
|
||||||
|
def get_device(self):
|
||||||
|
return getattr(self.dev, self.text().lower())
|
||||||
|
|
||||||
|
|
||||||
|
class ScanControl(BECConnector, QWidget):
|
||||||
WIDGET_HANDLER = {
|
WIDGET_HANDLER = {
|
||||||
ScanArgType.DEVICE: QLineEdit,
|
ScanArgType.DEVICE: DeviceLineEdit,
|
||||||
|
ScanArgType.DEVICEBASE: DeviceLineEdit,
|
||||||
ScanArgType.FLOAT: QDoubleSpinBox,
|
ScanArgType.FLOAT: QDoubleSpinBox,
|
||||||
ScanArgType.INT: QSpinBox,
|
ScanArgType.INT: QSpinBox,
|
||||||
ScanArgType.BOOL: QCheckBox,
|
ScanArgType.BOOL: QCheckBox,
|
||||||
ScanArgType.STR: QLineEdit,
|
ScanArgType.STR: QLineEdit,
|
||||||
|
ScanArgType.LITERALS: QComboBox, # TODO figure out combobox logic
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, parent=None, client=None, allowed_scans=None):
|
def __init__(
|
||||||
super().__init__(parent)
|
self, parent=None, client=None, gui_id: str | None = None, allowed_scans: list | None = None
|
||||||
|
):
|
||||||
|
super().__init__(client=client, gui_id=gui_id)
|
||||||
|
QWidget.__init__(self, parent=parent)
|
||||||
|
|
||||||
# Client from BEC + shortcuts to device manager and scans
|
# Client from BEC + shortcuts to device manager and scans
|
||||||
self.client = BECDispatcher().client if client is None else client
|
self.get_bec_shortcuts()
|
||||||
self.dev = self.client.device_manager.devices
|
|
||||||
self.scans = self.client.scans
|
# Main layout
|
||||||
|
self.layout = QVBoxLayout(self)
|
||||||
|
self.arg_box = None
|
||||||
|
self.kwarg_boxes = []
|
||||||
|
self.expert_mode = False # TODO implement in the future versions
|
||||||
|
|
||||||
# Scan list - allowed scans for the GUI
|
# Scan list - allowed scans for the GUI
|
||||||
self.allowed_scans = allowed_scans
|
self.allowed_scans = allowed_scans
|
||||||
@ -56,69 +90,55 @@ class ScanControl(QWidget):
|
|||||||
self._init_UI()
|
self._init_UI()
|
||||||
|
|
||||||
def _init_UI(self):
|
def _init_UI(self):
|
||||||
self.verticalLayout = QVBoxLayout(self)
|
"""
|
||||||
|
Initializes the UI of the scan control widget. Create the top box for scan selection and populate scans to main combobox.
|
||||||
|
"""
|
||||||
|
|
||||||
# Scan selection group box
|
# Scan selection group box
|
||||||
self.scan_selection_group = QGroupBox("Scan Selection", self)
|
self.scan_selection_group = self.create_scan_selection_group()
|
||||||
self.scan_selection_layout = QVBoxLayout(self.scan_selection_group)
|
self.scan_selection_group.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
||||||
self.comboBox_scan_selection = QComboBox(self.scan_selection_group)
|
self.layout.addWidget(self.scan_selection_group)
|
||||||
self.button_run_scan = QPushButton("Run Scan", self.scan_selection_group)
|
|
||||||
self.scan_selection_layout.addWidget(self.comboBox_scan_selection)
|
|
||||||
self.scan_selection_layout.addWidget(self.button_run_scan)
|
|
||||||
self.verticalLayout.addWidget(self.scan_selection_group)
|
|
||||||
|
|
||||||
# Scan control group box
|
|
||||||
self.scan_control_group = QGroupBox("Scan Control", self)
|
|
||||||
self.scan_control_layout = QVBoxLayout(self.scan_control_group)
|
|
||||||
self.verticalLayout.addWidget(self.scan_control_group)
|
|
||||||
|
|
||||||
# Kwargs layout - just placeholder
|
|
||||||
self.kwargs_layout = QGridLayout()
|
|
||||||
self.scan_control_layout.addLayout(self.kwargs_layout)
|
|
||||||
|
|
||||||
# 1st Separator
|
|
||||||
self.add_horizontal_separator(self.scan_control_layout)
|
|
||||||
|
|
||||||
# Buttons
|
|
||||||
self.button_layout = QHBoxLayout()
|
|
||||||
self.pushButton_add_bundle = QPushButton("Add Bundle", self.scan_control_group)
|
|
||||||
self.pushButton_add_bundle.clicked.connect(self.add_bundle)
|
|
||||||
self.pushButton_remove_bundle = QPushButton("Remove Bundle", self.scan_control_group)
|
|
||||||
self.pushButton_remove_bundle.clicked.connect(self.remove_bundle)
|
|
||||||
self.button_layout.addWidget(self.pushButton_add_bundle)
|
|
||||||
self.button_layout.addWidget(self.pushButton_remove_bundle)
|
|
||||||
self.scan_control_layout.addLayout(self.button_layout)
|
|
||||||
|
|
||||||
# 2nd Separator
|
|
||||||
self.add_horizontal_separator(self.scan_control_layout)
|
|
||||||
|
|
||||||
# Initialize the QTableWidget for args
|
|
||||||
self.args_table = QTableWidget()
|
|
||||||
self.args_table.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
|
|
||||||
|
|
||||||
self.scan_control_layout.addWidget(self.args_table)
|
|
||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
self.comboBox_scan_selection.currentIndexChanged.connect(self.on_scan_selected)
|
self.comboBox_scan_selection.currentIndexChanged.connect(self.on_scan_selected)
|
||||||
self.button_run_scan.clicked.connect(self.run_scan)
|
self.button_run_scan.clicked.connect(self.run_scan)
|
||||||
|
self.button_add_bundle.clicked.connect(self.add_arg_bundle)
|
||||||
|
self.button_remove_bundle.clicked.connect(self.remove_arg_bundle)
|
||||||
|
|
||||||
# Initialize scan selection
|
# Initialize scan selection
|
||||||
self.populate_scans()
|
self.populate_scans()
|
||||||
|
|
||||||
def add_horizontal_separator(self, layout) -> None:
|
def create_scan_selection_group(self) -> QGroupBox:
|
||||||
"""
|
"""
|
||||||
Adds a horizontal separator to the given layout
|
Creates the scan selection group box with combobox to select the scan and start/stop button.
|
||||||
|
|
||||||
Args:
|
Returns:
|
||||||
layout: Layout to add the separator to
|
QGroupBox: Group box containing the scan selection widgets.
|
||||||
"""
|
"""
|
||||||
separator = QFrame(self.scan_control_group)
|
|
||||||
separator.setFrameShape(QFrame.HLine)
|
scan_selection_group = QGroupBox("Scan Selection", self)
|
||||||
separator.setFrameShadow(QFrame.Sunken)
|
self.scan_selection_layout = QGridLayout(scan_selection_group)
|
||||||
layout.addWidget(separator)
|
self.comboBox_scan_selection = QComboBox(scan_selection_group)
|
||||||
|
# Run button
|
||||||
|
self.button_run_scan = QPushButton("Start", scan_selection_group)
|
||||||
|
self.button_run_scan.setStyleSheet("background-color: #559900; color: white")
|
||||||
|
# Stop button
|
||||||
|
self.button_stop_scan = StopButton(parent=scan_selection_group)
|
||||||
|
# Add bundle button
|
||||||
|
self.button_add_bundle = QPushButton("Add Bundle", scan_selection_group)
|
||||||
|
# Remove bundle button
|
||||||
|
self.button_remove_bundle = QPushButton("Remove Bundle", scan_selection_group)
|
||||||
|
|
||||||
|
self.scan_selection_layout.addWidget(self.comboBox_scan_selection, 0, 0, 1, 2)
|
||||||
|
self.scan_selection_layout.addWidget(self.button_run_scan, 1, 0)
|
||||||
|
self.scan_selection_layout.addWidget(self.button_stop_scan, 1, 1)
|
||||||
|
self.scan_selection_layout.addWidget(self.button_add_bundle, 2, 0)
|
||||||
|
self.scan_selection_layout.addWidget(self.button_remove_bundle, 2, 1)
|
||||||
|
|
||||||
|
return scan_selection_group
|
||||||
|
|
||||||
def populate_scans(self):
|
def populate_scans(self):
|
||||||
"""Populates the scan selection combo box with available scans"""
|
"""Populates the scan selection combo box with available scans from BEC session."""
|
||||||
self.available_scans = self.client.connector.get(
|
self.available_scans = self.client.connector.get(
|
||||||
MessageEndpoints.available_scans()
|
MessageEndpoints.available_scans()
|
||||||
).resource
|
).resource
|
||||||
@ -132,38 +152,141 @@ class ScanControl(QWidget):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
allowed_scans = self.allowed_scans
|
allowed_scans = self.allowed_scans
|
||||||
# TODO check parent class is ScanBase -> filter out the scans not relevant for GUI
|
|
||||||
self.comboBox_scan_selection.addItems(allowed_scans)
|
self.comboBox_scan_selection.addItems(allowed_scans)
|
||||||
|
|
||||||
def on_scan_selected(self):
|
def on_scan_selected(self):
|
||||||
"""Callback for scan selection combo box"""
|
"""Callback for scan selection combo box"""
|
||||||
|
self.reset_layout()
|
||||||
selected_scan_name = self.comboBox_scan_selection.currentText()
|
selected_scan_name = self.comboBox_scan_selection.currentText()
|
||||||
selected_scan_info = self.available_scans.get(selected_scan_name, {})
|
selected_scan_info = self.available_scans.get(selected_scan_name, {})
|
||||||
|
|
||||||
print(selected_scan_info) # TODO remove when widget will be more mature
|
gui_config = selected_scan_info.get("gui_config", {})
|
||||||
# Generate kwargs input
|
self.arg_group = gui_config.get("arg_group", None)
|
||||||
self.generate_kwargs_input_fields(selected_scan_info)
|
self.kwarg_groups = gui_config.get("kwarg_groups", None)
|
||||||
|
|
||||||
# Args section
|
if len(self.arg_group["arg_inputs"]) > 0:
|
||||||
self.generate_args_input_fields(selected_scan_info)
|
self.add_arg_group(self.arg_group)
|
||||||
|
if len(self.kwarg_groups) > 0:
|
||||||
|
self.add_kwargs_boxes(self.kwarg_groups)
|
||||||
|
|
||||||
def add_labels_to_layout(self, labels: list, grid_layout: QGridLayout) -> None:
|
self.update()
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
def add_input_labels(self, group_inputs: dict, grid_layout: QGridLayout, row: int) -> None:
|
||||||
"""
|
"""
|
||||||
Adds labels to the given grid layout as a separate row.
|
Adds the given arg_group from arg_bundle to the scan control layout. The input labels are always added to the first row.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
labels (list): List of label names to add.
|
group(dict): Dictionary containing the arg_group information.
|
||||||
grid_layout (QGridLayout): The grid layout to which labels will be added.
|
grid_layout(QGridLayout): The grid layout to which the arg_group will be added.
|
||||||
"""
|
"""
|
||||||
row_index = grid_layout.rowCount() # Get the next available row
|
for column_index, item in enumerate(group_inputs):
|
||||||
for column_index, label_name in enumerate(labels):
|
arg_name = item.get("name", None)
|
||||||
label = QLabel(label_name["name"].capitalize(), self.scan_control_group)
|
display_name = item.get("display_name", arg_name)
|
||||||
# Add the label to the grid layout at the calculated row and current column
|
tooltip = item.get("tooltip", None)
|
||||||
grid_layout.addWidget(label, row_index, column_index)
|
label = ArgLabel(text=display_name, arg_name=arg_name)
|
||||||
|
if tooltip is not None:
|
||||||
|
label.setToolTip(item["tooltip"])
|
||||||
|
grid_layout.addWidget(label, row, column_index)
|
||||||
|
|
||||||
|
def add_input_widgets(self, group_inputs: dict, grid_layout: QGridLayout, row) -> None:
|
||||||
|
for column_index, item in enumerate(group_inputs):
|
||||||
|
widget = self.WIDGET_HANDLER.get(item["type"], None)
|
||||||
|
if widget is None:
|
||||||
|
print(f"Unsupported annotation '{item['type']}' for parameter '{item['name']}'")
|
||||||
|
continue
|
||||||
|
if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
|
||||||
|
widget.setRange(-9999, 9999)
|
||||||
|
# if item["default"] != "_empty": # TODO fix for comboboxes and spinboxes
|
||||||
|
# WidgetIO.set_value(widget, item["default"])
|
||||||
|
# widget.setValue(item["default"])
|
||||||
|
grid_layout.addWidget(widget(), row, column_index)
|
||||||
|
|
||||||
|
def add_input_box(self, group: dict, rows: int = 1):
|
||||||
|
"""
|
||||||
|
Adds the given gui_group to the scan control layout.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
group(dict): Dictionary containing the gui_group information.
|
||||||
|
rows(int): Number of input rows to add to the layout.
|
||||||
|
"""
|
||||||
|
input_box = QGroupBox(group["name"])
|
||||||
|
group_layout = QGridLayout(input_box)
|
||||||
|
self.add_input_labels(group["inputs"], group_layout, 0)
|
||||||
|
for i in range(rows):
|
||||||
|
self.add_input_widgets(group["inputs"], group_layout, i + 1)
|
||||||
|
input_box.setLayout(group_layout)
|
||||||
|
self.layout.addWidget(input_box)
|
||||||
|
input_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
||||||
|
return input_box
|
||||||
|
|
||||||
|
def add_kwargs_boxes(self, groups: list):
|
||||||
|
"""
|
||||||
|
Adds the given gui_groups to the scan control layout.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
groups(list): List of dictionaries containing the gui_group information.
|
||||||
|
"""
|
||||||
|
for group in groups:
|
||||||
|
box = self.add_input_box(group)
|
||||||
|
self.layout.addWidget(box)
|
||||||
|
self.kwarg_boxes.append(box)
|
||||||
|
|
||||||
|
def add_arg_group(self, group: dict):
|
||||||
|
"""
|
||||||
|
Adds the given gui_groups to the scan control layout.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
"""
|
||||||
|
self.arg_box = self.add_input_box(group, rows=group["min"])
|
||||||
|
self.real_arg_box_row_count = group["min"]
|
||||||
|
self.layout.addWidget(self.arg_box)
|
||||||
|
|
||||||
|
def add_arg_bundle(self):
|
||||||
|
if self.arg_box is not None:
|
||||||
|
current_row_count = self.arg_box.layout().rowCount()
|
||||||
|
if self.arg_group["max"] is not None and current_row_count >= self.arg_group["max"]:
|
||||||
|
return
|
||||||
|
self.add_input_widgets(
|
||||||
|
self.arg_group["inputs"], self.arg_box.layout(), self.arg_box.layout().rowCount()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.real_arg_box_row_count += 1
|
||||||
|
|
||||||
|
print(f"row count REAL: {self.real_arg_box_row_count}")
|
||||||
|
print(f"row count QT: {self.arg_box.layout().rowCount()}")
|
||||||
|
|
||||||
|
def remove_arg_bundle(self):
|
||||||
|
if self.arg_box is not None:
|
||||||
|
current_row_count = self.real_arg_box_row_count # self.arg_box.layout().rowCount()
|
||||||
|
layout = self.arg_box.layout()
|
||||||
|
if current_row_count > self.arg_group["min"]:
|
||||||
|
for i in range(layout.columnCount()):
|
||||||
|
widget = layout.itemAtPosition(current_row_count, i).widget()
|
||||||
|
layout.removeWidget(widget)
|
||||||
|
widget.deleteLater()
|
||||||
|
self.real_arg_box_row_count -= 1
|
||||||
|
print(f"row count REAL: {self.real_arg_box_row_count}")
|
||||||
|
print(f"row count QT: {self.arg_box.layout().rowCount()}")
|
||||||
|
# self.arg_box.layout().removeRow(current_row_count - 1)
|
||||||
|
|
||||||
|
def reset_layout(self):
|
||||||
|
"""Clears the scan control layout from GuiGroups and ArgGroups boxes."""
|
||||||
|
if self.arg_box is not None:
|
||||||
|
self.layout.removeWidget(self.arg_box)
|
||||||
|
self.arg_box = None
|
||||||
|
if self.kwarg_boxes != []:
|
||||||
|
self.remove_kwarg_boxes()
|
||||||
|
|
||||||
|
def remove_kwarg_boxes(self):
|
||||||
|
for box in self.kwarg_boxes:
|
||||||
|
self.layout.removeWidget(box)
|
||||||
|
box.deleteLater()
|
||||||
|
self.kwarg_boxes = []
|
||||||
|
|
||||||
def add_labels_to_table(
|
def add_labels_to_table(
|
||||||
self, labels: list, table: QTableWidget
|
self, labels: list, table: QTableWidget
|
||||||
) -> None: # TODO could be moved to BECTable
|
) -> None: # TODO could be moved to BECTable -> not needed
|
||||||
"""
|
"""
|
||||||
Adds labels to the given table widget as a header row.
|
Adds labels to the given table widget as a header row.
|
||||||
|
|
||||||
@ -174,7 +297,9 @@ class ScanControl(QWidget):
|
|||||||
table.setColumnCount(len(labels))
|
table.setColumnCount(len(labels))
|
||||||
table.setHorizontalHeaderLabels(labels)
|
table.setHorizontalHeaderLabels(labels)
|
||||||
|
|
||||||
def generate_args_input_fields(self, scan_info: dict) -> None:
|
def generate_args_input_fields(
|
||||||
|
self, scan_info: dict
|
||||||
|
) -> None: # TODO decide how to deal with arg bundles
|
||||||
"""
|
"""
|
||||||
Generates input fields for args.
|
Generates input fields for args.
|
||||||
|
|
||||||
@ -196,7 +321,9 @@ class ScanControl(QWidget):
|
|||||||
for i in range(self.arg_size_min):
|
for i in range(self.arg_size_min):
|
||||||
self.add_bundle()
|
self.add_bundle()
|
||||||
|
|
||||||
def generate_kwargs_input_fields(self, scan_info: dict) -> None:
|
def generate_kwargs_input_fields(
|
||||||
|
self, scan_info: dict, inputs: dict = {}, hidden: list = []
|
||||||
|
) -> None: # TODO can be removed
|
||||||
"""
|
"""
|
||||||
Generates input fields for kwargs
|
Generates input fields for kwargs
|
||||||
|
|
||||||
@ -212,7 +339,11 @@ class ScanControl(QWidget):
|
|||||||
signature = scan_info.get("signature", [])
|
signature = scan_info.get("signature", [])
|
||||||
|
|
||||||
# Extract kwargs from the converted signature
|
# Extract kwargs from the converted signature
|
||||||
parameters = [param for param in signature if param["annotation"] != "_empty"]
|
parameters = [
|
||||||
|
param
|
||||||
|
for param in signature
|
||||||
|
if param["annotation"] != "_empty" and param["name"] not in hidden
|
||||||
|
]
|
||||||
|
|
||||||
# Add labels
|
# Add labels
|
||||||
self.add_labels_to_layout(parameters, self.kwargs_layout)
|
self.add_labels_to_layout(parameters, self.kwargs_layout)
|
||||||
@ -222,7 +353,25 @@ class ScanControl(QWidget):
|
|||||||
|
|
||||||
self.add_widgets_row_to_layout(self.kwargs_layout, widgets)
|
self.add_widgets_row_to_layout(self.kwargs_layout, widgets)
|
||||||
|
|
||||||
def generate_widgets_from_signature(self, parameters: list) -> list:
|
def create_widget_group(self, title: str, widgets: list) -> QGroupBox: # TODO to be removed
|
||||||
|
"""
|
||||||
|
Creates a group box containing the given widgets.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title(str): Title of the group box.
|
||||||
|
widgets(list): List of widgets to add to the group box.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
QGroupBox: Group box containing the given widgets.
|
||||||
|
"""
|
||||||
|
group_box = QGroupBox(title)
|
||||||
|
group_layout = QVBoxLayout(group_box)
|
||||||
|
for widget in widgets:
|
||||||
|
group_layout.addWidget(widget)
|
||||||
|
group_box.setLayout(group_layout)
|
||||||
|
return group_box
|
||||||
|
|
||||||
|
def generate_widgets_from_signature(self, parameters: list) -> list: # TODO to be removed
|
||||||
"""
|
"""
|
||||||
Generates widgets from the given list of items.
|
Generates widgets from the given list of items.
|
||||||
|
|
||||||
@ -259,15 +408,17 @@ class ScanControl(QWidget):
|
|||||||
# set high default range for spin boxes #TODO can be linked to motor/device limits from BEC
|
# set high default range for spin boxes #TODO can be linked to motor/device limits from BEC
|
||||||
if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
|
if isinstance(widget, (QSpinBox, QDoubleSpinBox)):
|
||||||
widget.setRange(-9999, 9999)
|
widget.setRange(-9999, 9999)
|
||||||
if item_default is not None:
|
# if item_default is not None:
|
||||||
WidgetIO.set_value(widget, item_default)
|
# WidgetIO.set_value(widget, item_default)
|
||||||
|
|
||||||
# Add the widget to the list
|
# Add the widget to the list
|
||||||
widgets.append(widget)
|
widgets.append(widget)
|
||||||
|
|
||||||
return widgets
|
return widgets
|
||||||
|
|
||||||
def set_args_table_limits(self, table: QTableWidget, scan_info: dict) -> None:
|
def set_args_table_limits(
|
||||||
|
self, table: QTableWidget, scan_info: dict
|
||||||
|
) -> None: # TODO can be removed
|
||||||
# Get bundle info
|
# Get bundle info
|
||||||
arg_bundle_size = scan_info.get("arg_bundle_size", {})
|
arg_bundle_size = scan_info.get("arg_bundle_size", {})
|
||||||
self.arg_size_min = arg_bundle_size.get("min", 1)
|
self.arg_size_min = arg_bundle_size.get("min", 1)
|
||||||
@ -278,7 +429,7 @@ class ScanControl(QWidget):
|
|||||||
|
|
||||||
def add_widgets_row_to_layout(
|
def add_widgets_row_to_layout(
|
||||||
self, grid_layout: QGridLayout, widgets: list, row_index: int = None
|
self, grid_layout: QGridLayout, widgets: list, row_index: int = None
|
||||||
) -> None:
|
) -> None: # TODO to be removed
|
||||||
"""
|
"""
|
||||||
Adds a row of widgets to the given grid layout.
|
Adds a row of widgets to the given grid layout.
|
||||||
|
|
||||||
@ -297,7 +448,7 @@ class ScanControl(QWidget):
|
|||||||
|
|
||||||
def add_widgets_row_to_table(
|
def add_widgets_row_to_table(
|
||||||
self, table_widget: QTableWidget, widgets: list, row_index: int = None
|
self, table_widget: QTableWidget, widgets: list, row_index: int = None
|
||||||
) -> None:
|
) -> None: # TODO to be removed
|
||||||
"""
|
"""
|
||||||
Adds a row of widgets to the given QTableWidget.
|
Adds a row of widgets to the given QTableWidget.
|
||||||
|
|
||||||
@ -329,7 +480,7 @@ class ScanControl(QWidget):
|
|||||||
max(widget.sizeHint().height() for widget in widgets if isinstance(widget, QWidget)),
|
max(widget.sizeHint().height() for widget in widgets if isinstance(widget, QWidget)),
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove_last_row_from_table(self, table_widget: QTableWidget) -> None:
|
def remove_last_row_from_table(self, table_widget: QTableWidget) -> None: # TODO to be removed
|
||||||
"""
|
"""
|
||||||
Removes the last row from the given QTableWidget until only one row is left.
|
Removes the last row from the given QTableWidget until only one row is left.
|
||||||
|
|
||||||
@ -342,12 +493,12 @@ class ScanControl(QWidget):
|
|||||||
): # Check to ensure there is a minimum number of rows remaining
|
): # Check to ensure there is a minimum number of rows remaining
|
||||||
table_widget.removeRow(row_count - 1)
|
table_widget.removeRow(row_count - 1)
|
||||||
|
|
||||||
def create_new_grid_layout(self):
|
def create_new_grid_layout(self): # TODO to be removed
|
||||||
new_layout = QGridLayout()
|
new_layout = QGridLayout()
|
||||||
# TODO maybe setup other layouts properties here?
|
# TODO maybe setup other layouts properties here?
|
||||||
return new_layout
|
return new_layout
|
||||||
|
|
||||||
def clear_and_delete_layout(self, layout: QLayout):
|
def clear_and_delete_layout(self, layout: QLayout): # TODO can be removed
|
||||||
"""
|
"""
|
||||||
Clears and deletes the given layout and all its child widgets.
|
Clears and deletes the given layout and all its child widgets.
|
||||||
|
|
||||||
@ -366,7 +517,7 @@ class ScanControl(QWidget):
|
|||||||
self.clear_and_delete_layout(sub_layout)
|
self.clear_and_delete_layout(sub_layout)
|
||||||
layout.deleteLater()
|
layout.deleteLater()
|
||||||
|
|
||||||
def add_bundle(self) -> None:
|
def add_bundle(self) -> None: # TODO can be removed
|
||||||
"""Adds a new bundle to the scan control layout"""
|
"""Adds a new bundle to the scan control layout"""
|
||||||
# Get widgets used for particular scan and save them to be able to use for adding bundles
|
# Get widgets used for particular scan and save them to be able to use for adding bundles
|
||||||
args_widgets = self.generate_widgets_from_signature(
|
args_widgets = self.generate_widgets_from_signature(
|
||||||
@ -376,7 +527,7 @@ class ScanControl(QWidget):
|
|||||||
# Add first widgets row to the table
|
# Add first widgets row to the table
|
||||||
self.add_widgets_row_to_table(self.args_table, args_widgets)
|
self.add_widgets_row_to_table(self.args_table, args_widgets)
|
||||||
|
|
||||||
def remove_bundle(self) -> None:
|
def remove_bundle(self) -> None: # TODO can be removed
|
||||||
"""Removes the last bundle from the scan control layout"""
|
"""Removes the last bundle from the scan control layout"""
|
||||||
self.remove_last_row_from_table(self.args_table)
|
self.remove_last_row_from_table(self.args_table)
|
||||||
|
|
||||||
@ -424,35 +575,65 @@ class ScanControl(QWidget):
|
|||||||
args.extend(row_args)
|
args.extend(row_args)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def extract_kwargs(self, box: QGroupBox) -> dict:
|
||||||
|
"""
|
||||||
|
Extracts the parameters from the given group box.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
box(QGroupBox): Group box from which to extract the parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Dictionary containing the extracted parameters.
|
||||||
|
"""
|
||||||
|
parameters = {}
|
||||||
|
keys = [label.arg_name for label in box.findChildren(ArgLabel)]
|
||||||
|
layout = box.layout()
|
||||||
|
for i in range(layout.columnCount()):
|
||||||
|
key = keys[i]
|
||||||
|
widget = layout.itemAtPosition(1, i).widget()
|
||||||
|
if isinstance(widget, DeviceLineEdit):
|
||||||
|
value = widget.get_device()
|
||||||
|
else:
|
||||||
|
value = WidgetIO.get_value(widget)
|
||||||
|
parameters[key] = value
|
||||||
|
return parameters
|
||||||
|
|
||||||
|
def extract_args(self, box):
|
||||||
|
args = []
|
||||||
|
layout = box.layout()
|
||||||
|
for i in range(layout.columnCount()):
|
||||||
|
widget = layout.itemAtPosition(1, i).widget()
|
||||||
|
if isinstance(widget, DeviceLineEdit):
|
||||||
|
value = widget.get_device()
|
||||||
|
else:
|
||||||
|
value = WidgetIO.get_value(widget)
|
||||||
|
args.append(value)
|
||||||
|
return args
|
||||||
|
|
||||||
def run_scan(self):
|
def run_scan(self):
|
||||||
# Extract kwargs for the scan
|
args = []
|
||||||
kwargs = {
|
kwargs = {}
|
||||||
k.lower(): v
|
if self.arg_box is not None:
|
||||||
for k, v in self.extract_kwargs_from_grid_row(self.kwargs_layout, 1).items()
|
args = self.extract_args(self.arg_box)
|
||||||
}
|
for box in self.kwarg_boxes:
|
||||||
|
box_kwargs = self.extract_kwargs(box)
|
||||||
# Extract args from the table
|
kwargs.update(box_kwargs)
|
||||||
args = self.extract_args_from_table(self.args_table)
|
print(kwargs)
|
||||||
|
|
||||||
# Convert args to lowercase if they are strings
|
|
||||||
args = [arg.lower() if isinstance(arg, str) else arg for arg in args]
|
|
||||||
|
|
||||||
# Execute the scan
|
|
||||||
scan_function = getattr(self.scans, self.comboBox_scan_selection.currentText())
|
scan_function = getattr(self.scans, self.comboBox_scan_selection.currentText())
|
||||||
print(f"called with args: {args} and kwargs: {kwargs}")
|
print(f"called with args: {args} and kwargs: {kwargs}")
|
||||||
if callable(scan_function):
|
if callable(scan_function):
|
||||||
scan_function(*args, **kwargs)
|
scan_function(*args, **kwargs)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
super().close()
|
||||||
|
|
||||||
|
|
||||||
# Application example
|
# Application example
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
# BECclient global variables
|
|
||||||
client = BECDispatcher().client
|
|
||||||
client.start()
|
|
||||||
|
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
scan_control = ScanControl(client=client, allowed_scans=["line_scan", "grid_scan"])
|
scan_control = ScanControl(allowed_scans=["fermat_scan", "round_scan", "line_scan"])
|
||||||
|
|
||||||
|
qdarktheme.setup_theme("auto")
|
||||||
window = scan_control
|
window = scan_control
|
||||||
window.show()
|
window.show()
|
||||||
app.exec()
|
app.exec()
|
||||||
|
Reference in New Issue
Block a user