1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-03-04 16:02:51 +01:00

feat: save and load config from devicebrowser

This commit is contained in:
2025-07-17 15:27:20 +02:00
committed by Jan Wyzula
parent 8489ef4a69
commit 7f0098f153
4 changed files with 64 additions and 11 deletions

View File

@@ -1,7 +1,9 @@
import os
import re
from functools import partial
from typing import Callable
import bec_lib
from bec_lib.callback_handler import EventType
from bec_lib.config_helper import ConfigHelper
from bec_lib.endpoints import MessageEndpoints
@@ -10,7 +12,14 @@ from bec_lib.messages import ConfigAction, ScanStatusMessage
from bec_qthemes import material_icon
from pyqtgraph import SignalProxy
from qtpy.QtCore import QSize, QThreadPool, Signal
from qtpy.QtWidgets import QLabel, QListWidget, QListWidgetItem, QToolButton, QVBoxLayout, QWidget
from qtpy.QtWidgets import (
QFileDialog,
QListWidget,
QListWidgetItem,
QToolButton,
QVBoxLayout,
QWidget,
)
from bec_widgets.cli.rpc.rpc_register import RPCRegister
from bec_widgets.utils.bec_widget import BECWidget
@@ -61,12 +70,14 @@ class DeviceBrowser(BECWidget, QWidget):
self.bec_dispatcher.client.callbacks.register(
EventType.SCAN_STATUS, self.scan_status_changed
)
self._default_config_dir = os.path.abspath(
os.path.join(os.path.dirname(bec_lib.__file__), "./configs/")
)
self.devices_changed.connect(self.update_device_list)
self.ui.add_button.clicked.connect(self._create_add_dialog)
self.ui.add_button.setIcon(material_icon("add", size=(20, 20), convert_to_pixmap=False))
self.init_warning_label()
self.init_tool_buttons()
self.init_device_list()
self.update_device_list()
@@ -90,6 +101,18 @@ class DeviceBrowser(BECWidget, QWidget):
initial_status = scan_status.status if scan_status is not None else "closed"
self.set_editing_mode(initial_status not in ["open", "paused"])
def init_tool_buttons(self):
def _setup_button(button: QToolButton, icon: str, slot: Callable, tooltip: str = ""):
button.clicked.connect(slot)
button.setIcon(material_icon(icon, size=(20, 20), convert_to_pixmap=False))
button.setToolTip(tooltip)
_setup_button(self.ui.add_button, "add", self._create_add_dialog, "add new device")
_setup_button(self.ui.save_button, "save", self._save_to_file, "save config to file")
_setup_button(
self.ui.import_button, "input", self._load_from_file, "append/merge config from file"
)
def _create_add_dialog(self):
dialog = DeviceConfigDialog(parent=self, device=None, action="add")
dialog.open()
@@ -148,7 +171,7 @@ class DeviceBrowser(BECWidget, QWidget):
self.dev_list.addItem(item)
self._device_items[device] = item
@SafeSlot(bool)
@SafeSlot(dict, dict)
def scan_status_changed(self, scan_info: dict, _: dict):
"""disable editing when scans are running and enable editing when they are finished"""
msg = ScanStatusMessage.model_validate(scan_info)
@@ -190,6 +213,22 @@ class DeviceBrowser(BECWidget, QWidget):
for device in self.dev:
self._device_items[device].setHidden(not self.regex.search(device))
@SafeSlot()
def _load_from_file(self):
file_path, _ = QFileDialog.getOpenFileName(
self, "Update config from file", self._default_config_dir, "Config files (*.yml *.yaml)"
)
if file_path:
self._config_helper.update_session_with_file(file_path)
@SafeSlot()
def _save_to_file(self):
file_path, _ = QFileDialog.getSaveFileName(
self, "Save config to file", self._default_config_dir, "Config files (*.yml *.yaml)"
)
if file_path:
self._config_helper.save_current_session(file_path)
if __name__ == "__main__": # pragma: no cover
import sys

View File

@@ -51,6 +51,20 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="save_button">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="import_button">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -39,9 +39,10 @@ class CommunicateConfigAction(QRunnable):
raise ValueError(
"Must be updating a device or be supplied a name for a new device"
)
if "deviceConfig" not in self.config:
if "deviceConfig" not in self.config or self.action in ["add", "remove"]:
self.process_simple_action(dev_name)
else:
# updating an existing device, but need to recreate it for this change
self.process_remove_readd(dev_name)
else:
raise ValueError(f"action {self.action} is not supported")

View File

@@ -68,7 +68,7 @@ class DeviceConfigDialog(BECWidget, QDialog):
self.client.connector, self.client._service_name
)
self._device = device
self._action = action
self._action: Literal["update", "add"] = action
self._q_threadpool = threadpool or QThreadPool()
self.setWindowTitle(f"Edit config for: {device}")
self._container = QStackedLayout()
@@ -168,10 +168,9 @@ class DeviceConfigDialog(BECWidget, QDialog):
diff = {
k: v for k, v in new_config.items() if self._initial_config.get(k) != new_config.get(k)
}
if self._initial_config["deviceConfig"] in [{}, None] and new_config["deviceConfig"] in [
{},
None,
]:
if self._initial_config.get("deviceConfig") in [{}, None] and new_config.get(
"deviceConfig"
) in [{}, None]:
diff.pop("deviceConfig", None)
if diff.get("deviceConfig") is not None:
# TODO: special cased in some parts of device manager but not others, should
@@ -222,7 +221,7 @@ class DeviceConfigDialog(BECWidget, QDialog):
self._proc_device_config_change(updated_config)
def _proc_device_config_change(self, config: dict):
logger.info(f"Sending request to update device config: {config}")
logger.info(f"Sending request to {self._action} device config: {config}")
self._start_waiting_display()
communicate_update = CommunicateConfigAction(