mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
refactor: BECWidget is a mixin based on BECConnector, for each QWidget in BEC
Handles closeEvent() and RPC registering/unregistering
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from bec_widgets.utils import BECConnector, ConnectionConfig
|
||||
from bec_widgets.utils import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
class DeviceInputConfig(ConnectionConfig):
|
||||
@ -9,7 +10,7 @@ class DeviceInputConfig(ConnectionConfig):
|
||||
arg_name: str | None = None
|
||||
|
||||
|
||||
class DeviceInputBase(BECConnector):
|
||||
class DeviceInputBase(BECWidget):
|
||||
"""
|
||||
Mixin class for device input widgets. This class provides methods to get the device list and device object based
|
||||
on the current text of the widget.
|
||||
@ -120,6 +121,3 @@ class DeviceInputBase(BECConnector):
|
||||
"""
|
||||
if device not in self.get_device_list(self.config.device_filter):
|
||||
raise ValueError(f"Device {device} is not valid.")
|
||||
|
||||
def cleanup(self):
|
||||
super().cleanup()
|
||||
|
@ -2,10 +2,11 @@ from bec_lib.endpoints import MessageEndpoints
|
||||
from qtpy.QtCore import Qt, Slot
|
||||
from qtpy.QtWidgets import QHeaderView, QTableWidget, QTableWidgetItem, QWidget
|
||||
|
||||
from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
|
||||
from bec_widgets.utils.bec_connector import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
class BECQueue(BECConnector, QTableWidget):
|
||||
class BECQueue(BECWidget, QTableWidget):
|
||||
"""
|
||||
Widget to display the BEC queue.
|
||||
"""
|
||||
|
@ -13,7 +13,7 @@ from bec_lib.utils.import_utils import lazy_import_from
|
||||
from qtpy.QtCore import QObject, QTimer, Signal, Slot
|
||||
from qtpy.QtWidgets import QHBoxLayout, QTreeWidget, QTreeWidgetItem, QWidget
|
||||
|
||||
from bec_widgets.utils.bec_connector import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.widgets.bec_status_box.status_item import StatusItem
|
||||
|
||||
@ -57,7 +57,7 @@ class BECServiceStatusMixin(QObject):
|
||||
self.services_update.emit(self.client._services_info, self.client._services_metric)
|
||||
|
||||
|
||||
class BECStatusBox(BECConnector, QWidget):
|
||||
class BECStatusBox(BECWidget, QWidget):
|
||||
"""An autonomous widget to display the status of BEC services.
|
||||
|
||||
Args:
|
||||
@ -290,15 +290,6 @@ class BECStatusBox(BECConnector, QWidget):
|
||||
if objects["item"] == item:
|
||||
objects["widget"].show_popup()
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Upon closing the widget, clean up the BECStatusBox and the QWidget.
|
||||
|
||||
Args:
|
||||
event: The close event.
|
||||
"""
|
||||
super().cleanup()
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main method to run the BECStatusBox widget."""
|
||||
|
@ -8,11 +8,11 @@ from qtpy.QtGui import QDoubleValidator
|
||||
from qtpy.QtWidgets import QDoubleSpinBox, QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.utils import UILoader
|
||||
from bec_widgets.utils.bec_connector import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
|
||||
|
||||
class DeviceBox(BECConnector, QWidget):
|
||||
class DeviceBox(BECWidget, QWidget):
|
||||
device_changed = Signal(str, str)
|
||||
|
||||
def __init__(self, parent=None, device=None, *args, **kwargs):
|
||||
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from qtpy.QtWidgets import QComboBox
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase, DeviceInputConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -82,11 +83,3 @@ class DeviceComboBox(DeviceInputBase, QComboBox):
|
||||
if device_obj is None:
|
||||
raise ValueError(f"Device {device_name} is not found.")
|
||||
return device_obj
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup the widget."""
|
||||
super().cleanup()
|
||||
|
||||
def closeEvent(self, event):
|
||||
super().cleanup()
|
||||
return QComboBox.closeEvent(self, event)
|
||||
|
@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
|
||||
from qtpy.QtCore import QSize
|
||||
from qtpy.QtWidgets import QCompleter, QLineEdit, QSizePolicy
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase, DeviceInputConfig
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -33,8 +34,8 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
||||
default: str | None = None,
|
||||
arg_name: str | None = None,
|
||||
):
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
QLineEdit.__init__(self, parent=parent)
|
||||
DeviceInputBase.__init__(self, client=client, config=config, gui_id=gui_id)
|
||||
|
||||
self.completer = QCompleter(self)
|
||||
self.setCompleter(self.completer)
|
||||
@ -94,11 +95,3 @@ class DeviceLineEdit(DeviceInputBase, QLineEdit):
|
||||
if device_obj is None:
|
||||
raise ValueError(f"Device {device_name} is not found.")
|
||||
return device_obj
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup the widget."""
|
||||
super().cleanup()
|
||||
|
||||
def closeEvent(self, event):
|
||||
super().cleanup()
|
||||
return QLineEdit.closeEvent(self, event)
|
||||
|
@ -6,7 +6,8 @@ from pydantic import Field
|
||||
from pyqtgraph.dockarea import Dock
|
||||
|
||||
from bec_widgets.cli.rpc_wigdet_handler import widget_handler
|
||||
from bec_widgets.utils import BECConnector, ConnectionConfig, GridLayoutManager
|
||||
from bec_widgets.utils import ConnectionConfig, GridLayoutManager
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qtpy.QtWidgets import QWidget
|
||||
@ -24,7 +25,7 @@ class DockConfig(ConnectionConfig):
|
||||
)
|
||||
|
||||
|
||||
class BECDock(BECConnector, Dock):
|
||||
class BECDock(BECWidget, Dock):
|
||||
USER_ACCESS = [
|
||||
"_config_dict",
|
||||
"_rpc_id",
|
||||
@ -91,7 +92,7 @@ class BECDock(BECConnector, Dock):
|
||||
super().float()
|
||||
|
||||
@property
|
||||
def widget_list(self) -> list[BECConnector]:
|
||||
def widget_list(self) -> list[BECWidget]:
|
||||
"""
|
||||
Get the widgets in the dock.
|
||||
|
||||
@ -101,7 +102,7 @@ class BECDock(BECConnector, Dock):
|
||||
return self.widgets
|
||||
|
||||
@widget_list.setter
|
||||
def widget_list(self, value: list[BECConnector]):
|
||||
def widget_list(self, value: list[BECWidget]):
|
||||
self.widgets = value
|
||||
|
||||
def hide_title_bar(self):
|
||||
@ -153,13 +154,13 @@ class BECDock(BECConnector, Dock):
|
||||
|
||||
def add_widget(
|
||||
self,
|
||||
widget: BECConnector | str,
|
||||
widget: BECWidget | str,
|
||||
row=None,
|
||||
col=0,
|
||||
rowspan=1,
|
||||
colspan=1,
|
||||
shift: Literal["down", "up", "left", "right"] = "down",
|
||||
) -> BECConnector:
|
||||
) -> BECWidget:
|
||||
"""
|
||||
Add a widget to the dock.
|
||||
|
||||
@ -236,8 +237,8 @@ class BECDock(BECConnector, Dock):
|
||||
Clean up the dock, including all its widgets.
|
||||
"""
|
||||
for widget in self.widgets:
|
||||
if hasattr(widget, "cleanup"):
|
||||
widget.cleanup()
|
||||
widget.cleanup()
|
||||
self.widgets.clear()
|
||||
super().cleanup()
|
||||
|
||||
def close(self):
|
||||
|
@ -9,7 +9,8 @@ from qtpy.QtCore import Qt
|
||||
from qtpy.QtGui import QPainter, QPaintEvent
|
||||
from qtpy.QtWidgets import QWidget
|
||||
|
||||
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
from .dock import BECDock, DockConfig
|
||||
|
||||
@ -21,7 +22,7 @@ class DockAreaConfig(ConnectionConfig):
|
||||
)
|
||||
|
||||
|
||||
class BECDockArea(BECConnector, DockArea):
|
||||
class BECDockArea(BECWidget, DockArea):
|
||||
USER_ACCESS = [
|
||||
"_config_dict",
|
||||
"panels",
|
||||
@ -227,6 +228,7 @@ class BECDockArea(BECConnector, DockArea):
|
||||
self.attach_all()
|
||||
for dock in dict(self.docks).values():
|
||||
dock.remove()
|
||||
self.docks.clear()
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
|
@ -12,7 +12,8 @@ from qtpy.QtCore import Signal as pyqtSignal
|
||||
from qtpy.QtWidgets import QWidget
|
||||
from typeguard import typechecked
|
||||
|
||||
from bec_widgets.utils import BECConnector, ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.utils import ConnectionConfig, WidgetContainerUtils
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.widgets.figure.plots.image.image import BECImageShow, ImageConfig
|
||||
from bec_widgets.widgets.figure.plots.motor_map.motor_map import BECMotorMap, MotorMapConfig
|
||||
@ -108,7 +109,7 @@ class WidgetHandler:
|
||||
return widget
|
||||
|
||||
|
||||
class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
class BECFigure(BECWidget, pg.GraphicsLayoutWidget):
|
||||
USER_ACCESS = [
|
||||
"_rpc_id",
|
||||
"_config_dict",
|
||||
@ -728,14 +729,9 @@ class BECFigure(BECConnector, pg.GraphicsLayoutWidget):
|
||||
"""Clear all widgets from the figure and reset to default state"""
|
||||
for widget in list(self._widgets.values()):
|
||||
widget.remove()
|
||||
# self.clear()
|
||||
self._widgets = defaultdict(dict)
|
||||
self._widgets.clear()
|
||||
self.grid = []
|
||||
theme = self.config.theme
|
||||
self.config = FigureConfig(
|
||||
widget_class=self.__class__.__name__, gui_id=self.gui_id, theme=theme
|
||||
)
|
||||
|
||||
# def cleanup(self):
|
||||
# self.clear_all()
|
||||
# super().cleanup()
|
||||
|
@ -596,7 +596,4 @@ class BECImageShow(BECPlotBase):
|
||||
self.bec_dispatcher.disconnect_slot(
|
||||
self.on_image_update, MessageEndpoints.device_monitor(monitor)
|
||||
)
|
||||
for image in self.images:
|
||||
image.cleanup()
|
||||
|
||||
super().cleanup()
|
||||
self.images.clear()
|
||||
|
@ -518,4 +518,3 @@ class BECMotorMap(BECPlotBase):
|
||||
def cleanup(self):
|
||||
"""Cleanup the widget."""
|
||||
self._disconnect_current_motors()
|
||||
super().cleanup()
|
||||
|
@ -296,9 +296,4 @@ class BECPlotBase(BECConnector, pg.GraphicsLayout):
|
||||
def remove(self):
|
||||
"""Remove the plot widget from the figure."""
|
||||
if self.figure is not None:
|
||||
self.cleanup()
|
||||
self.figure.remove(widget_id=self.gui_id)
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup the plot widget."""
|
||||
super().cleanup()
|
||||
|
@ -1368,6 +1368,4 @@ class BECWaveform(BECPlotBase):
|
||||
self.on_async_readback,
|
||||
MessageEndpoints.device_async_readback(self.scan_id, curve_id),
|
||||
)
|
||||
for curve in self.curves:
|
||||
curve.cleanup()
|
||||
super().cleanup()
|
||||
self.curves.clear()
|
||||
|
@ -262,4 +262,4 @@ class BECCurve(BECConnector, pg.PlotDataItem):
|
||||
"""Remove the curve from the plot."""
|
||||
# self.parent_item.removeItem(self)
|
||||
self.parent_item.remove_curve(self.name())
|
||||
self.cleanup()
|
||||
self.rpc_register.remove_rpc(self)
|
||||
|
@ -6,7 +6,7 @@ from qtpy.QtWidgets import QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.qt_utils.settings_dialog import SettingsDialog
|
||||
from bec_widgets.qt_utils.toolbar import ModularToolBar
|
||||
from bec_widgets.utils import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.figure import BECFigure
|
||||
from bec_widgets.widgets.figure.plots.motor_map.motor_map import MotorMapConfig
|
||||
from bec_widgets.widgets.motor_map.motor_map_dialog.motor_map_settings import MotorMapSettings
|
||||
@ -18,7 +18,7 @@ from bec_widgets.widgets.motor_map.motor_map_dialog.motor_map_toolbar import (
|
||||
)
|
||||
|
||||
|
||||
class BECMotorMapWidget(BECConnector, QWidget):
|
||||
class BECMotorMapWidget(BECWidget, QWidget):
|
||||
USER_ACCESS = [
|
||||
"change_motors",
|
||||
"set_max_points",
|
||||
@ -208,10 +208,6 @@ class BECMotorMapWidget(BECConnector, QWidget):
|
||||
self.toolbar.widgets["motor_y"].device_combobox.cleanup()
|
||||
return super().cleanup()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.cleanup()
|
||||
QWidget().closeEvent(event)
|
||||
|
||||
|
||||
def main(): # pragma: no cover
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
@ -288,7 +288,3 @@ class Ring(BECConnector):
|
||||
value = msg.get("signals").get(device).get("value")
|
||||
self.set_value(value)
|
||||
self.parent_progress_widget.update()
|
||||
|
||||
def cleanup(self):
|
||||
self.reset_connection()
|
||||
super().cleanup()
|
||||
|
@ -10,7 +10,8 @@ from qtpy import QtCore, QtGui
|
||||
from qtpy.QtCore import QSize, Slot
|
||||
from qtpy.QtWidgets import QSizePolicy, QWidget
|
||||
|
||||
from bec_widgets.utils import BECConnector, Colors, ConnectionConfig, EntryValidator
|
||||
from bec_widgets.utils import Colors, ConnectionConfig, EntryValidator
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.widgets.ring_progress_bar.ring import Ring, RingConfig
|
||||
|
||||
|
||||
@ -66,7 +67,7 @@ class RingProgressBarConfig(ConnectionConfig):
|
||||
_validate_colormap = field_validator("color_map")(Colors.validate_color_map)
|
||||
|
||||
|
||||
class RingProgressBar(BECConnector, QWidget):
|
||||
class RingProgressBar(BECWidget, QWidget):
|
||||
USER_ACCESS = [
|
||||
"_get_all_rpc",
|
||||
"_rpc_id",
|
||||
@ -208,7 +209,7 @@ class RingProgressBar(BECConnector, QWidget):
|
||||
index(int): Index of the progress bar to remove.
|
||||
"""
|
||||
ring = self._find_ring_by_index(index)
|
||||
ring.cleanup()
|
||||
ring.reset_connection()
|
||||
self._rings.remove(ring)
|
||||
self.config.rings.remove(ring.config)
|
||||
self.config.num_bars -= 1
|
||||
@ -622,9 +623,8 @@ class RingProgressBar(BECConnector, QWidget):
|
||||
|
||||
def clear_all(self):
|
||||
for ring in self._rings:
|
||||
ring.cleanup()
|
||||
del ring
|
||||
self._rings = []
|
||||
ring.reset_connection()
|
||||
self._rings.clear()
|
||||
self.update()
|
||||
self.initialize_bars()
|
||||
|
||||
@ -633,6 +633,6 @@ class RingProgressBar(BECConnector, QWidget):
|
||||
self.on_scan_queue_status, MessageEndpoints.scan_queue_status()
|
||||
)
|
||||
for ring in self._rings:
|
||||
ring.cleanup()
|
||||
del ring
|
||||
ring.reset_connection()
|
||||
self._rings.clear()
|
||||
super().cleanup()
|
||||
|
@ -10,13 +10,13 @@ from qtpy.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from bec_widgets.utils import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
from bec_widgets.widgets.scan_control.scan_group_box import ScanGroupBox
|
||||
from bec_widgets.widgets.stop_button.stop_button import StopButton
|
||||
|
||||
|
||||
class ScanControl(BECConnector, QWidget):
|
||||
class ScanControl(BECWidget, QWidget):
|
||||
|
||||
def __init__(
|
||||
self, parent=None, client=None, gui_id: str | None = None, allowed_scans: list | None = None
|
||||
@ -196,10 +196,6 @@ class ScanControl(BECConnector, QWidget):
|
||||
widget.cleanup()
|
||||
super().cleanup()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.cleanup()
|
||||
return QWidget.closeEvent(self, event)
|
||||
|
||||
|
||||
# Application example
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
|
@ -1,10 +1,10 @@
|
||||
from qtpy.QtCore import Slot
|
||||
from qtpy.QtWidgets import QPushButton
|
||||
|
||||
from bec_widgets.utils import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
class StopButton(BECConnector, QPushButton):
|
||||
class StopButton(BECWidget, QPushButton):
|
||||
"""A button that stops the current scan."""
|
||||
|
||||
def __init__(self, parent=None, client=None, config=None, gui_id=None):
|
||||
|
@ -3,7 +3,8 @@ import re
|
||||
from pydantic import Field, field_validator
|
||||
from qtpy.QtWidgets import QTextEdit
|
||||
|
||||
from bec_widgets.utils.bec_connector import BECConnector, ConnectionConfig
|
||||
from bec_widgets.utils.bec_connector import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import Colors
|
||||
|
||||
|
||||
@ -27,7 +28,7 @@ class TextBoxConfig(ConnectionConfig):
|
||||
_validate_background_color = field_validator("background_color")(Colors.validate_color)
|
||||
|
||||
|
||||
class TextBox(BECConnector, QTextEdit):
|
||||
class TextBox(BECWidget, QTextEdit):
|
||||
|
||||
USER_ACCESS = ["set_color", "set_text", "set_font_size"]
|
||||
|
||||
|
@ -2,7 +2,7 @@ from qtpy.QtCore import QUrl, qInstallMessageHandler
|
||||
from qtpy.QtWebEngineWidgets import QWebEngineView
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
from bec_widgets.utils import BECConnector
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
|
||||
|
||||
def suppress_qt_messages(type_, context, msg):
|
||||
@ -14,7 +14,7 @@ def suppress_qt_messages(type_, context, msg):
|
||||
qInstallMessageHandler(suppress_qt_messages)
|
||||
|
||||
|
||||
class WebsiteWidget(BECConnector, QWebEngineView):
|
||||
class WebsiteWidget(BECWidget, QWebEngineView):
|
||||
"""
|
||||
A simple widget to display a website
|
||||
"""
|
||||
|
@ -1,13 +1,19 @@
|
||||
import pytest
|
||||
from qtpy.QtWidgets import QWidget
|
||||
|
||||
from bec_widgets.widgets.base_classes.device_input_base import DeviceInputBase
|
||||
|
||||
from .client_mocks import mocked_client
|
||||
|
||||
|
||||
# DeviceInputBase is meant to be mixed in a QWidget
|
||||
class DeviceInputWidget(DeviceInputBase, QWidget):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def device_input_base(mocked_client):
|
||||
widget = DeviceInputBase(client=mocked_client)
|
||||
widget = DeviceInputWidget(client=mocked_client)
|
||||
yield widget
|
||||
|
||||
|
||||
@ -15,7 +21,7 @@ def test_device_input_base_init(device_input_base):
|
||||
assert device_input_base is not None
|
||||
assert device_input_base.client is not None
|
||||
assert isinstance(device_input_base, DeviceInputBase)
|
||||
assert device_input_base.config.widget_class == "DeviceInputBase"
|
||||
assert device_input_base.config.widget_class == "DeviceInputWidget"
|
||||
assert device_input_base.config.device_filter is None
|
||||
assert device_input_base.config.default is None
|
||||
assert device_input_base.devices == []
|
||||
@ -23,12 +29,12 @@ def test_device_input_base_init(device_input_base):
|
||||
|
||||
def test_device_input_base_init_with_config(mocked_client):
|
||||
config = {
|
||||
"widget_class": "DeviceInputBase",
|
||||
"widget_class": "DeviceInputWidget",
|
||||
"gui_id": "test_gui_id",
|
||||
"device_filter": "FakePositioner",
|
||||
"default": "samx",
|
||||
}
|
||||
widget = DeviceInputBase(client=mocked_client, config=config)
|
||||
widget = DeviceInputWidget(client=mocked_client, config=config)
|
||||
assert widget.config.gui_id == "test_gui_id"
|
||||
assert widget.config.device_filter == "FakePositioner"
|
||||
assert widget.config.default == "samx"
|
||||
|
Reference in New Issue
Block a user