mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
refactor: do not flush selection upon receiving config update; allow widgetIO to receive kwargs to be able to use get_value to receive string instead of int for QComboBox
This commit is contained in:
@ -1,11 +1,9 @@
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from bec_lib.client import BECClient
|
|
||||||
from bec_lib.device import Device as BECDevice
|
from bec_lib.device import Device as BECDevice
|
||||||
from bec_lib.device import Positioner as BECPositioner
|
from bec_lib.device import Positioner as BECPositioner
|
||||||
from bec_lib.device import ReadoutPriority
|
from bec_lib.device import ReadoutPriority
|
||||||
from bec_lib.devicemanager import DeviceContainer
|
from bec_lib.devicemanager import DeviceContainer
|
||||||
from bec_lib.redis_connector import RedisConnector
|
|
||||||
|
|
||||||
|
|
||||||
class FakeDevice(BECDevice):
|
class FakeDevice(BECDevice):
|
||||||
@ -80,6 +78,8 @@ class FakePositioner(BECPositioner):
|
|||||||
super().__init__(name=name)
|
super().__init__(name=name)
|
||||||
# self.limits = limits if limits is not None else [0.0, 0.0]
|
# self.limits = limits if limits is not None else [0.0, 0.0]
|
||||||
self.read_value = read_value
|
self.read_value = read_value
|
||||||
|
self.setpoint_value = read_value
|
||||||
|
self.motor_is_moving_value = 0
|
||||||
self._enabled = enabled
|
self._enabled = enabled
|
||||||
self._limits = limits
|
self._limits = limits
|
||||||
self._readout_priority = readout_priority
|
self._readout_priority = readout_priority
|
||||||
@ -101,6 +101,11 @@ class FakePositioner(BECPositioner):
|
|||||||
"velocity": {"kind_str": "2"}, # config
|
"velocity": {"kind_str": "2"}, # config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.signals = {
|
||||||
|
self.name: {"value": self.read_value},
|
||||||
|
f"{self.name}_setpoint": {"value": self.setpoint_value},
|
||||||
|
f"{self.name}_motor_is_moving": {"value": self.motor_is_moving_value},
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def readout_priority(self):
|
def readout_priority(self):
|
||||||
@ -139,7 +144,7 @@ class FakePositioner(BECPositioner):
|
|||||||
Args:
|
Args:
|
||||||
fake_value(float): Desired fake value
|
fake_value(float): Desired fake value
|
||||||
"""
|
"""
|
||||||
self.signals[self.name]["value"] = fake_value
|
self.read_value = fake_value
|
||||||
|
|
||||||
def describe(self) -> dict:
|
def describe(self) -> dict:
|
||||||
"""
|
"""
|
||||||
@ -157,11 +162,7 @@ class FakePositioner(BECPositioner):
|
|||||||
self.read_value = value
|
self.read_value = value
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
return {
|
return self.signals
|
||||||
self.name: {"value": self.read_value},
|
|
||||||
f"{self.name}_setpoint": {"value": self.read_value},
|
|
||||||
f"{self.name}_motor_is_moving": {"value": 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
def set_limits(self, limits):
|
def set_limits(self, limits):
|
||||||
self.limits = limits
|
self.limits = limits
|
@ -1,5 +1,6 @@
|
|||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
from qtpy.QtWidgets import (
|
from qtpy.QtWidgets import (
|
||||||
QApplication,
|
QApplication,
|
||||||
@ -20,7 +21,7 @@ class WidgetHandler(ABC):
|
|||||||
"""Abstract base class for all widget handlers."""
|
"""Abstract base class for all widget handlers."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_value(self, widget: QWidget):
|
def get_value(self, widget: QWidget, **kwargs):
|
||||||
"""Retrieve value from the widget instance."""
|
"""Retrieve value from the widget instance."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@ -31,7 +32,7 @@ class WidgetHandler(ABC):
|
|||||||
class LineEditHandler(WidgetHandler):
|
class LineEditHandler(WidgetHandler):
|
||||||
"""Handler for QLineEdit widgets."""
|
"""Handler for QLineEdit widgets."""
|
||||||
|
|
||||||
def get_value(self, widget: QLineEdit) -> str:
|
def get_value(self, widget: QLineEdit, **kwargs) -> str:
|
||||||
return widget.text()
|
return widget.text()
|
||||||
|
|
||||||
def set_value(self, widget: QLineEdit, value: str) -> None:
|
def set_value(self, widget: QLineEdit, value: str) -> None:
|
||||||
@ -41,7 +42,9 @@ class LineEditHandler(WidgetHandler):
|
|||||||
class ComboBoxHandler(WidgetHandler):
|
class ComboBoxHandler(WidgetHandler):
|
||||||
"""Handler for QComboBox widgets."""
|
"""Handler for QComboBox widgets."""
|
||||||
|
|
||||||
def get_value(self, widget: QComboBox) -> int:
|
def get_value(self, widget: QComboBox, as_string: bool = False, **kwargs) -> int | str:
|
||||||
|
if as_string is True:
|
||||||
|
return widget.currentText()
|
||||||
return widget.currentIndex()
|
return widget.currentIndex()
|
||||||
|
|
||||||
def set_value(self, widget: QComboBox, value: int | str) -> None:
|
def set_value(self, widget: QComboBox, value: int | str) -> None:
|
||||||
@ -54,7 +57,7 @@ class ComboBoxHandler(WidgetHandler):
|
|||||||
class TableWidgetHandler(WidgetHandler):
|
class TableWidgetHandler(WidgetHandler):
|
||||||
"""Handler for QTableWidget widgets."""
|
"""Handler for QTableWidget widgets."""
|
||||||
|
|
||||||
def get_value(self, widget: QTableWidget) -> list:
|
def get_value(self, widget: QTableWidget, **kwargs) -> list:
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
widget.item(row, col).text() if widget.item(row, col) else ""
|
widget.item(row, col).text() if widget.item(row, col) else ""
|
||||||
@ -73,7 +76,7 @@ class TableWidgetHandler(WidgetHandler):
|
|||||||
class SpinBoxHandler(WidgetHandler):
|
class SpinBoxHandler(WidgetHandler):
|
||||||
"""Handler for QSpinBox and QDoubleSpinBox widgets."""
|
"""Handler for QSpinBox and QDoubleSpinBox widgets."""
|
||||||
|
|
||||||
def get_value(self, widget):
|
def get_value(self, widget, **kwargs):
|
||||||
return widget.value()
|
return widget.value()
|
||||||
|
|
||||||
def set_value(self, widget, value):
|
def set_value(self, widget, value):
|
||||||
@ -83,7 +86,7 @@ class SpinBoxHandler(WidgetHandler):
|
|||||||
class CheckBoxHandler(WidgetHandler):
|
class CheckBoxHandler(WidgetHandler):
|
||||||
"""Handler for QCheckBox widgets."""
|
"""Handler for QCheckBox widgets."""
|
||||||
|
|
||||||
def get_value(self, widget):
|
def get_value(self, widget, **kwargs):
|
||||||
return widget.isChecked()
|
return widget.isChecked()
|
||||||
|
|
||||||
def set_value(self, widget, value):
|
def set_value(self, widget, value):
|
||||||
@ -93,7 +96,7 @@ class CheckBoxHandler(WidgetHandler):
|
|||||||
class LabelHandler(WidgetHandler):
|
class LabelHandler(WidgetHandler):
|
||||||
"""Handler for QLabel widgets."""
|
"""Handler for QLabel widgets."""
|
||||||
|
|
||||||
def get_value(self, widget):
|
def get_value(self, widget, **kwargs):
|
||||||
return widget.text()
|
return widget.text()
|
||||||
|
|
||||||
def set_value(self, widget, value):
|
def set_value(self, widget, value):
|
||||||
@ -114,7 +117,7 @@ class WidgetIO:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_value(widget, ignore_errors=False):
|
def get_value(widget, ignore_errors=False, **kwargs):
|
||||||
"""
|
"""
|
||||||
Retrieve value from the widget instance.
|
Retrieve value from the widget instance.
|
||||||
|
|
||||||
@ -124,7 +127,7 @@ class WidgetIO:
|
|||||||
"""
|
"""
|
||||||
handler_class = WidgetIO._find_handler(widget)
|
handler_class = WidgetIO._find_handler(widget)
|
||||||
if handler_class:
|
if handler_class:
|
||||||
return handler_class().get_value(widget) # Instantiate the handler
|
return handler_class().get_value(widget, **kwargs) # Instantiate the handler
|
||||||
if not ignore_errors:
|
if not ignore_errors:
|
||||||
raise ValueError(f"No handler for widget type: {type(widget)}")
|
raise ValueError(f"No handler for widget type: {type(widget)}")
|
||||||
return None
|
return None
|
||||||
|
@ -2,10 +2,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import enum
|
import enum
|
||||||
|
|
||||||
from bec_lib.callback_handler import EventType
|
from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority
|
||||||
from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority, Signal
|
from bec_lib.device import Signal as BECSignal
|
||||||
from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
from qtpy.QtCore import Property, Slot
|
from qtpy.QtCore import Property, Signal, Slot
|
||||||
|
|
||||||
from bec_widgets.utils import ConnectionConfig
|
from bec_widgets.utils import ConnectionConfig
|
||||||
from bec_widgets.utils.bec_widget import BECWidget
|
from bec_widgets.utils.bec_widget import BECWidget
|
||||||
@ -43,7 +43,7 @@ class DeviceInputBase(BECWidget):
|
|||||||
_device_handler = {
|
_device_handler = {
|
||||||
BECDeviceFilter.DEVICE: Device,
|
BECDeviceFilter.DEVICE: Device,
|
||||||
BECDeviceFilter.POSITIONER: Positioner,
|
BECDeviceFilter.POSITIONER: Positioner,
|
||||||
BECDeviceFilter.SIGNAL: Signal,
|
BECDeviceFilter.SIGNAL: BECSignal,
|
||||||
BECDeviceFilter.COMPUTED_SIGNAL: ComputedSignal,
|
BECDeviceFilter.COMPUTED_SIGNAL: ComputedSignal,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +72,6 @@ class DeviceInputBase(BECWidget):
|
|||||||
self._device_filter = []
|
self._device_filter = []
|
||||||
self._readout_filter = []
|
self._readout_filter = []
|
||||||
self._devices = []
|
self._devices = []
|
||||||
self.bec_dispatcher.client.callbacks.register(
|
|
||||||
EventType.DEVICE_UPDATE, self.update_devices_from_filters
|
|
||||||
)
|
|
||||||
|
|
||||||
### QtSlots ###
|
### QtSlots ###
|
||||||
|
|
||||||
@ -92,15 +89,13 @@ class DeviceInputBase(BECWidget):
|
|||||||
else:
|
else:
|
||||||
logger.warning(f"Device {device} is not in the filtered selection.")
|
logger.warning(f"Device {device} is not in the filtered selection.")
|
||||||
|
|
||||||
@Slot(dict, dict)
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def update_devices_from_filters(
|
def update_devices_from_filters(self):
|
||||||
self, content: dict | None = None, metadata: dict | None = None
|
|
||||||
):
|
|
||||||
"""Update the devices based on the current filter selection
|
"""Update the devices based on the current filter selection
|
||||||
in self.device_filter and self.readout_filter. If apply_filter is False,
|
in self.device_filter and self.readout_filter. If apply_filter is False,
|
||||||
it will not apply the filters, store the filter settings and return.
|
it will not apply the filters, store the filter settings and return.
|
||||||
"""
|
"""
|
||||||
|
current_device = WidgetIO.get_value(widget=self, as_string=True)
|
||||||
self.config.device_filter = self.device_filter
|
self.config.device_filter = self.device_filter
|
||||||
self.config.readout_filter = self.readout_filter
|
self.config.readout_filter = self.readout_filter
|
||||||
if self.apply_filter is False:
|
if self.apply_filter is False:
|
||||||
@ -111,6 +106,7 @@ class DeviceInputBase(BECWidget):
|
|||||||
# Filter based on readout priority
|
# Filter based on readout priority
|
||||||
devs = [dev for dev in devs if self._check_readout_filter(dev)]
|
devs = [dev for dev in devs if self._check_readout_filter(dev)]
|
||||||
self.devices = [device.name for device in devs]
|
self.devices = [device.name for device in devs]
|
||||||
|
self.set_device(current_device)
|
||||||
|
|
||||||
@Slot(list)
|
@Slot(list)
|
||||||
def set_available_devices(self, devices: list[str]):
|
def set_available_devices(self, devices: list[str]):
|
||||||
@ -153,6 +149,8 @@ class DeviceInputBase(BECWidget):
|
|||||||
def default(self, value: str):
|
def default(self, value: str):
|
||||||
if self.validate_device(value) is False:
|
if self.validate_device(value) is False:
|
||||||
return
|
return
|
||||||
|
self.config.default = value
|
||||||
|
WidgetIO.set_value(widget=self, value=value)
|
||||||
|
|
||||||
@Property(bool)
|
@Property(bool)
|
||||||
def apply_filter(self):
|
def apply_filter(self):
|
||||||
@ -343,7 +341,9 @@ class DeviceInputBase(BECWidget):
|
|||||||
for entry in filters:
|
for entry in filters:
|
||||||
setattr(self, entry, True)
|
setattr(self, entry, True)
|
||||||
|
|
||||||
def _check_device_filter(self, device: Device | Signal | ComputedSignal | Positioner) -> bool:
|
def _check_device_filter(
|
||||||
|
self, device: Device | BECSignal | ComputedSignal | Positioner
|
||||||
|
) -> bool:
|
||||||
"""Check if filter for device type is applied or not.
|
"""Check if filter for device type is applied or not.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -351,7 +351,9 @@ class DeviceInputBase(BECWidget):
|
|||||||
"""
|
"""
|
||||||
return all(isinstance(device, self._device_handler[entry]) for entry in self.device_filter)
|
return all(isinstance(device, self._device_handler[entry]) for entry in self.device_filter)
|
||||||
|
|
||||||
def _check_readout_filter(self, device: Device | Signal | ComputedSignal | Positioner) -> bool:
|
def _check_readout_filter(
|
||||||
|
self, device: Device | BECSignal | ComputedSignal | Positioner
|
||||||
|
) -> bool:
|
||||||
"""Check if filter for readout priority is applied or not.
|
"""Check if filter for readout priority is applied or not.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -373,7 +375,7 @@ class DeviceInputBase(BECWidget):
|
|||||||
dev = getattr(self.dev, device.lower(), None)
|
dev = getattr(self.dev, device.lower(), None)
|
||||||
if dev is None:
|
if dev is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Device {device} is not found in devicemanager {self.dev} as enabled device."
|
f"Device {device} is not found in the device manager {self.dev} as enabled device."
|
||||||
)
|
)
|
||||||
return dev
|
return dev
|
||||||
|
|
||||||
@ -384,6 +386,7 @@ class DeviceInputBase(BECWidget):
|
|||||||
Args:
|
Args:
|
||||||
device(str): Device to validate.
|
device(str): Device to validate.
|
||||||
"""
|
"""
|
||||||
if device in self.devices:
|
all_devs = [dev.name for dev in self.dev.enabled_devices]
|
||||||
|
if device in self.devices and device in all_devs:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
from bec_lib.callback_handler import EventType
|
||||||
from bec_lib.device import ReadoutPriority
|
from bec_lib.device import ReadoutPriority
|
||||||
from qtpy.QtCore import QSize
|
from qtpy.QtCore import QSize, Signal, Slot
|
||||||
|
from qtpy.QtGui import QPainter, QPaintEvent, QPen
|
||||||
from qtpy.QtWidgets import QComboBox, QSizePolicy
|
from qtpy.QtWidgets import QComboBox, QSizePolicy
|
||||||
|
|
||||||
from bec_widgets.utils.colors import get_accent_colors
|
from bec_widgets.utils.colors import get_accent_colors
|
||||||
@ -26,6 +28,9 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
|||||||
|
|
||||||
ICON_NAME = "list_alt"
|
ICON_NAME = "list_alt"
|
||||||
|
|
||||||
|
device_selected = Signal(str)
|
||||||
|
device_config_update = Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parent=None,
|
parent=None,
|
||||||
@ -47,6 +52,7 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
|||||||
self.arg_name = arg_name
|
self.arg_name = arg_name
|
||||||
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
|
||||||
self.setMinimumSize(QSize(100, 0))
|
self.setMinimumSize(QSize(100, 0))
|
||||||
|
self._callback_id = None
|
||||||
self._is_valid_input = False
|
self._is_valid_input = False
|
||||||
self._accent_colors = get_accent_colors()
|
self._accent_colors = get_accent_colors()
|
||||||
# We do not consider the config that is passed here, this produced problems
|
# We do not consider the config that is passed here, this produced problems
|
||||||
@ -74,6 +80,28 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
|||||||
# Set default device if passed
|
# Set default device if passed
|
||||||
if default is not None:
|
if default is not None:
|
||||||
self.set_device(default)
|
self.set_device(default)
|
||||||
|
self._callback_id = self.bec_dispatcher.client.callbacks.register(
|
||||||
|
EventType.DEVICE_UPDATE, self.on_device_update
|
||||||
|
)
|
||||||
|
self.device_config_update.connect(self.update_devices_from_filters)
|
||||||
|
self.currentTextChanged.connect(self.check_validity)
|
||||||
|
self.check_validity(self.currentText())
|
||||||
|
|
||||||
|
def on_device_update(self, action: str, content: dict) -> None:
|
||||||
|
"""
|
||||||
|
Callback for device update events. Triggers the device_update signal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
action (str): The action that triggered the event.
|
||||||
|
content (dict): The content of the config update.
|
||||||
|
"""
|
||||||
|
if action in ["add", "remove", "reload"]:
|
||||||
|
self.device_config_update.emit()
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
"""Cleanup the widget."""
|
||||||
|
if self._callback_id is not None:
|
||||||
|
self.bec_dispatcher.client.callbacks.remove(self._callback_id)
|
||||||
|
|
||||||
def get_current_device(self) -> object:
|
def get_current_device(self) -> object:
|
||||||
"""
|
"""
|
||||||
@ -85,6 +113,36 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
|||||||
dev_name = self.currentText()
|
dev_name = self.currentText()
|
||||||
return self.get_device_object(dev_name)
|
return self.get_device_object(dev_name)
|
||||||
|
|
||||||
|
def paintEvent(self, event: QPaintEvent) -> None:
|
||||||
|
"""Extend the paint event to set the border color based on the validity of the input.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event (PySide6.QtGui.QPaintEvent) : Paint event.
|
||||||
|
"""
|
||||||
|
# logger.info(f"Received paint event: {event} in {self.__class__}")
|
||||||
|
super().paintEvent(event)
|
||||||
|
|
||||||
|
if self._is_valid_input is False and self.isEnabled() is True:
|
||||||
|
painter = QPainter(self)
|
||||||
|
pen = QPen()
|
||||||
|
pen.setWidth(2)
|
||||||
|
pen.setColor(self._accent_colors.emergency)
|
||||||
|
painter.setPen(pen)
|
||||||
|
painter.drawRect(self.rect().adjusted(1, 1, -1, -1))
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
@Slot(str)
|
||||||
|
def check_validity(self, input_text: str) -> None:
|
||||||
|
"""
|
||||||
|
Check if the current value is a valid device name.
|
||||||
|
"""
|
||||||
|
if self.validate_device(input_text) is True:
|
||||||
|
self._is_valid_input = True
|
||||||
|
self.device_selected.emit(input_text.lower())
|
||||||
|
else:
|
||||||
|
self._is_valid_input = False
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
@ -99,6 +157,7 @@ if __name__ == "__main__": # pragma: no cover
|
|||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
widget.setLayout(layout)
|
widget.setLayout(layout)
|
||||||
combo = DeviceComboBox()
|
combo = DeviceComboBox()
|
||||||
|
combo.devices = ["samx", "dev1", "dev2", "dev3", "dev4"]
|
||||||
layout.addWidget(combo)
|
layout.addWidget(combo)
|
||||||
widget.show()
|
widget.show()
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from bec_lib.callback_handler import EventType
|
||||||
from bec_lib.device import ReadoutPriority
|
from bec_lib.device import ReadoutPriority
|
||||||
from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
from qtpy.QtCore import QSize, Signal, Slot
|
from qtpy.QtCore import QSize, Signal, Slot
|
||||||
@ -29,6 +30,7 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
device_selected = Signal(str)
|
device_selected = Signal(str)
|
||||||
|
device_config_update = Signal()
|
||||||
|
|
||||||
ICON_NAME = "edit_note"
|
ICON_NAME = "edit_note"
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
|||||||
default: str | None = None,
|
default: str | None = None,
|
||||||
arg_name: str | None = None,
|
arg_name: str | None = None,
|
||||||
):
|
):
|
||||||
|
self._callback_id = None
|
||||||
self._is_valid_input = False
|
self._is_valid_input = False
|
||||||
self._accent_colors = get_accent_colors()
|
self._accent_colors = get_accent_colors()
|
||||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||||
@ -84,7 +87,28 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
|||||||
# Set default device if passed
|
# Set default device if passed
|
||||||
if default is not None:
|
if default is not None:
|
||||||
self.set_device(default)
|
self.set_device(default)
|
||||||
|
self._callback_id = self.bec_dispatcher.client.callbacks.register(
|
||||||
|
EventType.DEVICE_UPDATE, self.on_device_update
|
||||||
|
)
|
||||||
|
self.device_config_update.connect(self.update_devices_from_filters)
|
||||||
self.textChanged.connect(self.check_validity)
|
self.textChanged.connect(self.check_validity)
|
||||||
|
self.check_validity(self.text())
|
||||||
|
|
||||||
|
def on_device_update(self, action: str, content: dict) -> None:
|
||||||
|
"""
|
||||||
|
Callback for device update events. Triggers the device_update signal.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
action (str): The action that triggered the event.
|
||||||
|
content (dict): The content of the config update.
|
||||||
|
"""
|
||||||
|
if action in ["add", "remove", "reload"]:
|
||||||
|
self.device_config_update.emit()
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
"""Cleanup the widget."""
|
||||||
|
if self._callback_id is not None:
|
||||||
|
self.bec_dispatcher.client.callbacks.remove(self._callback_id)
|
||||||
|
|
||||||
def get_current_device(self) -> object:
|
def get_current_device(self) -> object:
|
||||||
"""
|
"""
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from bec_lib.device import Positioner
|
||||||
from ophyd import Kind
|
from ophyd import Kind
|
||||||
from qtpy.QtCore import QSize, Signal, Slot
|
from qtpy.QtCore import QSize, Signal, Slot
|
||||||
from qtpy.QtGui import QPainter, QPaintEvent, QPen
|
from qtpy.QtGui import QPainter, QPaintEvent, QPen
|
||||||
@ -61,7 +62,8 @@ class SignalLineEdit(DeviceSignalInputBase, QLineEdit):
|
|||||||
self.set_device(device)
|
self.set_device(device)
|
||||||
if default is not None:
|
if default is not None:
|
||||||
self.set_signal(default)
|
self.set_signal(default)
|
||||||
self.textChanged.connect(self.check_validity)
|
self.textChanged.connect(self.validate_device)
|
||||||
|
self.validate_device(self.text())
|
||||||
|
|
||||||
def get_current_device(self) -> object:
|
def get_current_device(self) -> object:
|
||||||
"""
|
"""
|
||||||
@ -96,12 +98,30 @@ class SignalLineEdit(DeviceSignalInputBase, QLineEdit):
|
|||||||
"""
|
"""
|
||||||
if self.validate_signal(input_text) is True:
|
if self.validate_signal(input_text) is True:
|
||||||
self._is_valid_input = True
|
self._is_valid_input = True
|
||||||
if self.validate_device(self.device) is True:
|
self.on_text_changed(input_text)
|
||||||
self.device_signal_changed.emit(input_text)
|
|
||||||
else:
|
else:
|
||||||
self._is_valid_input = False
|
self._is_valid_input = False
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
@Slot(str)
|
||||||
|
def on_text_changed(self, text: str):
|
||||||
|
"""Slot for text changed. If a device is selected and the signal is changed and valid it emits a signal.
|
||||||
|
For a positioner, the readback value has to be renamed to the device name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): Text in the combobox.
|
||||||
|
"""
|
||||||
|
print("test")
|
||||||
|
if self.validate_device(self.device) is False:
|
||||||
|
return
|
||||||
|
if self.validate_signal(text) is False:
|
||||||
|
return
|
||||||
|
if text == "readback" and isinstance(self.get_device_object(self.device), Positioner):
|
||||||
|
device_signal = self.device
|
||||||
|
else:
|
||||||
|
device_signal = f"{self.device}_{text}"
|
||||||
|
self.device_signal_changed.emit(device_signal)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
|
@ -5,7 +5,7 @@ import fakeredis
|
|||||||
import pytest
|
import pytest
|
||||||
from bec_lib.redis_connector import RedisConnector
|
from bec_lib.redis_connector import RedisConnector
|
||||||
|
|
||||||
from bec_widgets.test_utils.client_mocks import DEVICES, DMMock, FakePositioner, Positioner
|
from bec_widgets.tests.utils import DEVICES, DMMock, FakePositioner, Positioner
|
||||||
|
|
||||||
|
|
||||||
def fake_redis_server(host, port):
|
def fake_redis_server(host, port):
|
||||||
|
@ -4,7 +4,7 @@ import pytest
|
|||||||
|
|
||||||
from bec_widgets.cli.client import BECFigure
|
from bec_widgets.cli.client import BECFigure
|
||||||
from bec_widgets.cli.client_utils import BECGuiClientMixin, _start_plot_process
|
from bec_widgets.cli.client_utils import BECGuiClientMixin, _start_plot_process
|
||||||
from bec_widgets.test_utils.client_mocks import FakeDevice
|
from bec_widgets.tests.utils import FakeDevice
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -3,9 +3,8 @@ from unittest import mock
|
|||||||
import pytest
|
import pytest
|
||||||
from bec_lib.device import ReadoutPriority
|
from bec_lib.device import ReadoutPriority
|
||||||
from qtpy.QtWidgets import QWidget
|
from qtpy.QtWidgets import QWidget
|
||||||
from typeguard import TypeCheckError
|
|
||||||
|
|
||||||
from bec_widgets.test_utils.client_mocks import FakePositioner
|
from bec_widgets.tests.utils import FakePositioner
|
||||||
from bec_widgets.widgets.base_classes.device_input_base import BECDeviceFilter, DeviceInputBase
|
from bec_widgets.widgets.base_classes.device_input_base import BECDeviceFilter, DeviceInputBase
|
||||||
|
|
||||||
from .client_mocks import mocked_client
|
from .client_mocks import mocked_client
|
||||||
@ -26,8 +25,9 @@ def device_input_base(qtbot, mocked_client):
|
|||||||
"""Fixture with mocked FilterIO and WidgetIO"""
|
"""Fixture with mocked FilterIO and WidgetIO"""
|
||||||
with mock.patch("bec_widgets.utils.filter_io.FilterIO.set_selection"):
|
with mock.patch("bec_widgets.utils.filter_io.FilterIO.set_selection"):
|
||||||
with mock.patch("bec_widgets.utils.widget_io.WidgetIO.set_value"):
|
with mock.patch("bec_widgets.utils.widget_io.WidgetIO.set_value"):
|
||||||
widget = create_widget(qtbot=qtbot, widget=DeviceInputWidget, client=mocked_client)
|
with mock.patch("bec_widgets.utils.widget_io.WidgetIO.get_value"):
|
||||||
yield widget
|
widget = create_widget(qtbot=qtbot, widget=DeviceInputWidget, client=mocked_client)
|
||||||
|
yield widget
|
||||||
|
|
||||||
|
|
||||||
def test_device_input_base_init(device_input_base):
|
def test_device_input_base_init(device_input_base):
|
||||||
|
@ -35,7 +35,6 @@ def test_device_input_combobox_init(device_input_combobox):
|
|||||||
assert device_input_combobox.client is not None
|
assert device_input_combobox.client is not None
|
||||||
assert isinstance(device_input_combobox, DeviceComboBox)
|
assert isinstance(device_input_combobox, DeviceComboBox)
|
||||||
assert device_input_combobox.config.widget_class == "DeviceComboBox"
|
assert device_input_combobox.config.widget_class == "DeviceComboBox"
|
||||||
assert device_input_combobox.config.default is None
|
|
||||||
assert device_input_combobox.devices == [
|
assert device_input_combobox.devices == [
|
||||||
"samx",
|
"samx",
|
||||||
"samy",
|
"samy",
|
||||||
|
@ -90,23 +90,32 @@ def test_device_signal_set_device(device_signal_base):
|
|||||||
assert device_signal_base.signals == ["readback", "setpoint", "velocity"]
|
assert device_signal_base.signals == ["readback", "setpoint", "velocity"]
|
||||||
|
|
||||||
|
|
||||||
def test_signal_combobox(device_signal_combobox):
|
def test_signal_combobox(qtbot, device_signal_combobox):
|
||||||
"""Test the signal_combobox"""
|
"""Test the signal_combobox"""
|
||||||
device_signal_combobox._signals == []
|
container = []
|
||||||
|
|
||||||
|
def test_cb(input):
|
||||||
|
container.append(input)
|
||||||
|
|
||||||
|
device_signal_combobox.device_signal_changed.connect(test_cb)
|
||||||
|
assert device_signal_combobox._signals == []
|
||||||
device_signal_combobox.include_normal_signals = True
|
device_signal_combobox.include_normal_signals = True
|
||||||
device_signal_combobox.include_hinted_signals = True
|
device_signal_combobox.include_hinted_signals = True
|
||||||
device_signal_combobox.include_config_signals = True
|
device_signal_combobox.include_config_signals = True
|
||||||
device_signal_combobox.signals == []
|
assert device_signal_combobox.signals == []
|
||||||
device_signal_combobox.set_device("samx")
|
device_signal_combobox.set_device("samx")
|
||||||
device_signal_combobox.signals == ["readback", "setpoint", "velocity"]
|
assert device_signal_combobox.signals == ["readback", "setpoint", "velocity"]
|
||||||
|
qtbot.wait(100)
|
||||||
|
assert container == ["samx"]
|
||||||
|
|
||||||
|
|
||||||
def test_signal_lineeidt(device_signal_line_edit):
|
def test_signal_lineeidt(device_signal_line_edit):
|
||||||
"""Test the signal_combobox"""
|
"""Test the signal_combobox"""
|
||||||
device_signal_line_edit._signals == []
|
|
||||||
|
assert device_signal_line_edit._signals == []
|
||||||
device_signal_line_edit.include_normal_signals = True
|
device_signal_line_edit.include_normal_signals = True
|
||||||
device_signal_line_edit.include_hinted_signals = True
|
device_signal_line_edit.include_hinted_signals = True
|
||||||
device_signal_line_edit.include_config_signals = True
|
device_signal_line_edit.include_config_signals = True
|
||||||
device_signal_line_edit.signals == []
|
assert device_signal_line_edit.signals == []
|
||||||
device_signal_line_edit.set_device("samx")
|
device_signal_line_edit.set_device("samx")
|
||||||
device_signal_line_edit.signals == ["readback", "setpoint", "velocity"]
|
assert device_signal_line_edit.signals == ["readback", "setpoint", "velocity"]
|
||||||
|
Reference in New Issue
Block a user