wip various fixes for csaxs testing

This commit is contained in:
2026-05-20 17:12:09 +02:00
parent 3a165b26ed
commit 19c6648763
8 changed files with 62 additions and 13 deletions
@@ -9,7 +9,7 @@ from bec_lib.device import ComputedSignal, Device, Positioner, ReadoutPriority
from bec_lib.device import Signal as BECSignal
from bec_lib.logger import bec_logger
from pydantic import Field, field_validator
from qtpy.QtCore import QSize, QStringListModel, Signal, Slot
from qtpy.QtCore import QSize, QStringListModel, Qt, Signal, Slot
from qtpy.QtWidgets import QComboBox, QCompleter, QSizePolicy
from bec_widgets.utils.bec_connector import ConnectionConfig
@@ -219,7 +219,9 @@ class DeviceComboBox(BECWidget, QComboBox):
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.device_config_update.connect(
self.update_devices_from_filters, Qt.ConnectionType.QueuedConnection
)
self.currentTextChanged.connect(self.check_validity)
self.check_validity(self.currentText())
@@ -255,6 +257,9 @@ class DeviceComboBox(BECWidget, QComboBox):
@SafeSlot()
def update_devices_from_filters(self):
"""Refresh the available device list from current device/readout/signal filters."""
if getattr(self, "_destroyed", False):
return
self.config.device_filter = [entry.value for entry in self.device_filter]
self.config.readout_filter = [entry.value for entry in self.readout_filter]
self.config.signal_class_filter = self.signal_class_filter
@@ -489,6 +494,8 @@ class DeviceComboBox(BECWidget, QComboBox):
action: Device update action emitted by BEC.
content: Device update payload. Currently unused.
"""
if getattr(self, "_destroyed", False):
return
if action in ["add", "remove", "reload"]:
self.device_config_update.emit()
@@ -496,6 +503,7 @@ class DeviceComboBox(BECWidget, QComboBox):
"""Cleanup the widget."""
if self._callback_id is not None:
self.bec_dispatcher.client.callbacks.remove(self._callback_id)
self._callback_id = None
super().cleanup()
def get_current_device(self) -> object:
@@ -77,6 +77,7 @@ class SignalComboBox(BECWidget, QComboBox):
device_signal_changed = Signal(str)
signal_reset = Signal()
device_config_update = Signal()
def __init__(
self,
@@ -138,7 +139,10 @@ class SignalComboBox(BECWidget, QComboBox):
self.autocomplete = True
self._device_update_register = self.bec_dispatcher.client.callbacks.register(
EventType.DEVICE_UPDATE, self.update_signals_from_filters
EventType.DEVICE_UPDATE, self.on_device_update
)
self.device_config_update.connect(
self.update_signals_from_filters, Qt.ConnectionType.QueuedConnection
)
self.currentTextChanged.connect(self.on_text_changed)
@@ -207,6 +211,9 @@ class SignalComboBox(BECWidget, QComboBox):
content: Optional callback payload from BEC device updates. Currently unused.
metadata: Optional callback metadata from BEC device updates. Currently unused.
"""
if getattr(self, "_destroyed", False):
return
self.config.signal_filter = [kind.name for kind in self.signal_filter]
if self._signal_class_filter:
@@ -247,6 +254,13 @@ class SignalComboBox(BECWidget, QComboBox):
),
)
def on_device_update(self, action: str, content: dict) -> None:
"""Refresh filters when BEC reports device configuration changes."""
if getattr(self, "_destroyed", False):
return
if action in ["add", "remove", "reload"]:
self.device_config_update.emit()
@Property(str)
def device(self) -> str:
"""Selected device."""
@@ -588,7 +602,9 @@ class SignalComboBox(BECWidget, QComboBox):
def cleanup(self):
"""Cleanup the widget."""
self.bec_dispatcher.client.callbacks.remove(self._device_update_register)
if self._device_update_register is not None:
self.bec_dispatcher.client.callbacks.remove(self._device_update_register)
self._device_update_register = None
super().cleanup()
@staticmethod
+8 -1
View File
@@ -907,7 +907,14 @@ class Image(ImageBase):
async_signal_name=config.async_signal_name,
)
self.subscriptions["main"].async_signal_name = None
config.async_signal_name = None
if target_device == self._config.device and target_entry == self._config.signal:
config.device = ""
config.signal = ""
config.source = None
config.monitor_type = None
self._signal_configs.pop("main", None)
self._set_connection_status("disconnected")
self.async_update = False
self._sync_device_selection()
@@ -217,14 +217,14 @@ class Ring(BECWidget, QWidget):
match mode:
case "manual":
if self.config.mode == "manual":
if self.config.mode == "manual" and self.registered_slot is None:
return
if self.registered_slot is not None:
self.bec_dispatcher.disconnect_slot(*self.registered_slot)
self.config.mode = "manual"
self.registered_slot = None
case "scan":
if self.config.mode == "scan":
if self.config.mode == "scan" and self.registered_slot is not None:
return
if self.registered_slot is not None:
self.bec_dispatcher.disconnect_slot(*self.registered_slot)
@@ -383,9 +383,9 @@ class Ring(BECWidget, QWidget):
"""
current_RID = meta.get("RID", None)
if current_RID != self.RID:
self.RID = current_RID
self.set_min_max_values(0, msg.get("max_value", 100))
self.set_value(msg.get("value", 0))
self.update()
@SafeSlot(dict, dict)
def on_device_readback(self, msg, meta):
@@ -404,7 +404,6 @@ class Ring(BECWidget, QWidget):
if value is None:
return
self.set_value(value)
self.update()
@SafeSlot(dict, dict)
def on_device_progress(self, msg, meta):
@@ -424,7 +423,6 @@ class Ring(BECWidget, QWidget):
if msg.get("done"):
value = max_val
self.set_value(value)
self.update()
def paintEvent(self, event):
if not self.progress_container:
@@ -103,7 +103,6 @@ class RingProgressContainerWidget(QWidget):
self._hovered_ring = None
self._last_hover_global_pos = None
self._hover_tooltip.hide()
ring.cleanup()
ring.close()
ring.deleteLater()
self.rings.pop(index)
@@ -373,7 +372,7 @@ class RingProgressContainerWidget(QWidget):
self._hovered_ring = None
self._last_hover_global_pos = None
self._hover_tooltip.hide()
for ring in self.rings:
for ring in list(self.rings):
ring.close()
ring.deleteLater()
self.rings = []
@@ -1,3 +1,5 @@
from unittest import mock
import pytest
from bec_lib.device import ReadoutPriority
@@ -124,6 +126,19 @@ def test_device_input_combobox_disabled_invalid_has_neutral_border(device_input_
assert "red" in device_input_combobox.styleSheet()
def test_device_input_combobox_cleanup_unregisters_callback(qtbot, mocked_client):
with mock.patch.object(mocked_client.callbacks, "remove"):
widget = DeviceComboBox(client=mocked_client)
qtbot.addWidget(widget)
callback_id = widget._callback_id
widget.close()
widget.deleteLater()
mocked_client.callbacks.remove.assert_called_once_with(callback_id)
assert widget._callback_id is None
def test_get_device_from_input_combobox_init(device_input_combobox):
device_input_combobox.setCurrentIndex(0)
device_text = device_input_combobox.currentText()
+3 -1
View File
@@ -188,10 +188,12 @@ def test_linked_device_combobox_updates_signal_combobox_on_each_text_change(
def test_device_signal_input_base_cleanup(qtbot, mocked_client):
with mock.patch.object(mocked_client.callbacks, "remove"):
widget = SignalComboBox(client=mocked_client)
callback_id = widget._device_update_register
widget.close()
widget.deleteLater()
mocked_client.callbacks.remove.assert_called_once_with(widget._device_update_register)
mocked_client.callbacks.remove.assert_called_once_with(callback_id)
assert widget._device_update_register is None
def test_signal_combobox_get_signal_name_with_item_data(qtbot, device_signal_combobox):
@@ -464,6 +464,10 @@ def test_disconnect_clears_async_state(qtbot, mocked_client, monkeypatch):
assert view.subscriptions["main"].async_signal_name is None
assert view.async_update is False
assert view.device == ""
assert view.signal == ""
assert view.subscriptions["main"].source is None
assert view.subscriptions["main"].monitor_type is None
##############################################