From ecad07821c4581d88647c6d712bfbfe0304254ef Mon Sep 17 00:00:00 2001 From: David Perl Date: Wed, 27 Aug 2025 15:58:47 +0200 Subject: [PATCH] refactor: use list of expandable for available devices --- bec_widgets/utils/expandable_frame.py | 27 +++++++-- .../utils/list_of_expandable_frames.py | 3 +- .../available_device_resources.py | 9 +-- .../available_device_resources_ui.py | 7 ++- .../device_tag_group.py | 5 +- .../device_tag_group_ui.py | 55 ++++++------------- 6 files changed, 53 insertions(+), 53 deletions(-) diff --git a/bec_widgets/utils/expandable_frame.py b/bec_widgets/utils/expandable_frame.py index 82db8bfc..138dac27 100644 --- a/bec_widgets/utils/expandable_frame.py +++ b/bec_widgets/utils/expandable_frame.py @@ -32,6 +32,7 @@ class ExpandableGroupFrame(QFrame): super().__init__(parent=parent) self._expanded = expanded + self._title_text = f"{title}" self.setFrameStyle(QFrame.Shape.StyledPanel | QFrame.Shadow.Plain) self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) self._layout = QVBoxLayout() @@ -50,21 +51,27 @@ class ExpandableGroupFrame(QFrame): def _create_title_layout(self, title: str, icon: str): self._title_layout = QHBoxLayout() self._layout.addLayout(self._title_layout) + self._internal_title_layout = QHBoxLayout() + self._title_layout.addLayout(self._internal_title_layout) - self._title = ClickableLabel(f"{title}") + self._title = ClickableLabel() + self._set_title_text(self._title_text) self._title_icon = ClickableLabel() - self._title_layout.addWidget(self._title_icon) - self._title_layout.addWidget(self._title) + self._internal_title_layout.addWidget(self._title_icon) + self._internal_title_layout.addWidget(self._title) self.icon_name = icon self._title.clicked.connect(self.switch_expanded_state) self._title_icon.clicked.connect(self.switch_expanded_state) - self._title_layout.addStretch(1) + self._internal_title_layout.addStretch(1) self._expansion_button = QToolButton() self._update_expansion_icon() self._title_layout.addWidget(self._expansion_button, stretch=1) + def get_title_layout(self) -> QHBoxLayout: + return self._internal_title_layout + def set_layout(self, layout: QLayout) -> None: self._contents.setLayout(layout) self._contents.layout().setContentsMargins(0, 0, 0, 0) # type: ignore @@ -113,6 +120,18 @@ class ExpandableGroupFrame(QFrame): else: self._title_icon.setVisible(False) + @SafeProperty(str) + def title_text(self): # type: ignore + return self._title_text + + @title_text.setter + def title_text(self, title_text: str): + self._title_text = title_text + self._set_title_text(self._title_text) + + def _set_title_text(self, title_text: str): + self._title.setText(title_text) + # Application example if __name__ == "__main__": # pragma: no cover diff --git a/bec_widgets/utils/list_of_expandable_frames.py b/bec_widgets/utils/list_of_expandable_frames.py index a6d964ad..38422827 100644 --- a/bec_widgets/utils/list_of_expandable_frames.py +++ b/bec_widgets/utils/list_of_expandable_frames.py @@ -54,14 +54,13 @@ class ListOfExpandableFrames(QListWidget, Generic[_EF]): item_widget.expansion_state_changed.connect(partial(_updatesize, item, item_widget)) item_widget.imminent_deletion.connect(partial(_remove_item, item)) - item_widget.broadcast_size_hint.connect(item.setSizeHint) - item.setSizeHint(item_widget.sizeHint()) self.setItemWidget(item, item_widget) self.addItem(item) self._item_dict[id] = self.item_tuple(item, item_widget) + item.setSizeHint(item_widget.sizeHint()) return item_widget def get_item_widget(self, id: str): diff --git a/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources.py b/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources.py index 55d927ed..a3c9b280 100644 --- a/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources.py +++ b/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources.py @@ -45,12 +45,9 @@ class AvailableDeviceResources(BECWidget, QWidget, Ui_availableDeviceResources): self._add_tag_group("Untagged devices", self._backend.untagged_devices) def _add_tag_group(self, tag_group: str, devices: set[HashableDevice]): - item = QListWidgetItem(self.tag_groups_list) - tag_group_widget = DeviceTagGroup(self.tag_groups_list, tag_group, devices) - self.tag_groups_list.setItemWidget(item, tag_group_widget) - self.tag_groups_list.addItem(item) - self._items[tag_group] = (item, tag_group_widget) - item.setSizeHint(QSize(tag_group_widget.width(), tag_group_widget.height())) + self.tag_groups_list.add_item( + tag_group, self.tag_groups_list, tag_group, devices, expanded=False + ) def _reset_devices_state(self): for _, tag_group in self._items.values(): diff --git a/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources_ui.py b/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources_ui.py index 6b14659f..4ebfc014 100644 --- a/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources_ui.py +++ b/bec_widgets/widgets/control/device_manager/components/available_device_resources/available_device_resources_ui.py @@ -1,6 +1,11 @@ from qtpy.QtCore import QMetaObject, Qt from qtpy.QtWidgets import QAbstractItemView, QListView, QListWidget, QVBoxLayout +from bec_widgets.utils.list_of_expandable_frames import ListOfExpandableFrames +from bec_widgets.widgets.control.device_manager.components.available_device_resources.device_tag_group import ( + DeviceTagGroup, +) + class Ui_availableDeviceResources(object): def setupUi(self, availableDeviceResources): @@ -8,7 +13,7 @@ class Ui_availableDeviceResources(object): availableDeviceResources.setObjectName("availableDeviceResources") self.verticalLayout = QVBoxLayout(availableDeviceResources) self.verticalLayout.setObjectName("verticalLayout") - self.tag_groups_list = QListWidget(availableDeviceResources) + self.tag_groups_list = ListOfExpandableFrames(availableDeviceResources, DeviceTagGroup) self.tag_groups_list.setObjectName("tag_groups_list") self.tag_groups_list.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection) self.tag_groups_list.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) diff --git a/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group.py b/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group.py index 5782ea1c..6c03ac26 100644 --- a/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group.py +++ b/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group.py @@ -4,6 +4,7 @@ from bec_qthemes import material_icon from qtpy.QtCore import QSize from qtpy.QtWidgets import QFrame, QHBoxLayout, QLabel, QListWidgetItem, QVBoxLayout, QWidget +from bec_widgets.utils.expandable_frame import ExpandableGroupFrame from bec_widgets.widgets.control.device_manager.components.available_device_resources.device_resource_backend import ( HashableDevice, ) @@ -98,13 +99,13 @@ class _DeviceEntry(NamedTuple): widget: _DeviceEntryWidget -class DeviceTagGroup(QWidget, Ui_DeviceTagGroup): +class DeviceTagGroup(ExpandableGroupFrame, Ui_DeviceTagGroup): def __init__( self, parent=None, name: str = "TagGroupTitle", data: set[HashableDevice] = set(), **kwargs ): super().__init__(parent=parent, **kwargs) self.setupUi(self) - self.title.setText(name) + self.title_text = name self._devices: dict[str, _DeviceEntry] = {} for device in data: self._add_item(device) diff --git a/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group_ui.py b/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group_ui.py index 10f98e2b..094bf471 100644 --- a/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group_ui.py +++ b/bec_widgets/widgets/control/device_manager/components/available_device_resources/device_tag_group_ui.py @@ -5,13 +5,10 @@ from bec_qthemes import material_icon from qtpy.QtCore import QMetaObject, QSize, Qt from qtpy.QtWidgets import ( QAbstractItemView, - QFrame, QHBoxLayout, QLabel, QListView, QListWidget, - QSizePolicy, - QSpacerItem, QToolButton, QVBoxLayout, ) @@ -64,58 +61,40 @@ class Ui_DeviceTagGroup(object): if not DeviceTagGroup.objectName(): DeviceTagGroup.setObjectName("DeviceTagGroup") DeviceTagGroup.setMinimumWidth(150) - self.verticalLayout = QVBoxLayout(DeviceTagGroup) + self.verticalLayout = QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") - self.frame = QFrame(DeviceTagGroup) - self.frame.setObjectName("frame") - self.frame.setFrameShape(QFrame.Shape.StyledPanel) - self.frame.setFrameShadow(QFrame.Shadow.Raised) - self.verticalLayout_2 = QVBoxLayout(self.frame) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.horizontalLayout = QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") + DeviceTagGroup.set_layout(self.verticalLayout) - self.title = QLabel(self.frame) - self.title.setObjectName("title") - self.horizontalLayout.addWidget(self.title) + title_layout = DeviceTagGroup.get_title_layout() - self.n_included = QLabel(self.frame, text="...") + self.n_included = QLabel(DeviceTagGroup, text="...") self.n_included.setObjectName("n_included") - self.horizontalLayout.addWidget(self.n_included) + title_layout.addWidget(self.n_included) - self.horizontalSpacer = QSpacerItem( - 40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum - ) - self.horizontalLayout.addItem(self.horizontalSpacer) - - self.delete_tag_button = QToolButton(self.frame) + self.delete_tag_button = QToolButton(DeviceTagGroup) self.delete_tag_button.setObjectName("delete_tag_button") - self.horizontalLayout.addWidget(self.delete_tag_button) + title_layout.addWidget(self.delete_tag_button) - self.remove_from_composition_button = QToolButton(self.frame) + self.remove_from_composition_button = QToolButton(DeviceTagGroup) self.remove_from_composition_button.setObjectName("remove_from_composition_button") - self.horizontalLayout.addWidget(self.remove_from_composition_button) + title_layout.addWidget(self.remove_from_composition_button) - self.add_to_composition_button = QToolButton(self.frame) + self.add_to_composition_button = QToolButton(DeviceTagGroup) self.add_to_composition_button.setObjectName("add_to_composition_button") - self.horizontalLayout.addWidget(self.add_to_composition_button) + title_layout.addWidget(self.add_to_composition_button) - self.remove_all_button = QToolButton(self.frame) + self.remove_all_button = QToolButton(DeviceTagGroup) self.remove_all_button.setObjectName("remove_all_from_composition_button") - self.horizontalLayout.addWidget(self.remove_all_button) + title_layout.addWidget(self.remove_all_button) - self.add_all_button = QToolButton(self.frame) + self.add_all_button = QToolButton(DeviceTagGroup) self.add_all_button.setObjectName("add_all_to_composition_button") - self.horizontalLayout.addWidget(self.add_all_button) + title_layout.addWidget(self.add_all_button) - self.verticalLayout_2.addLayout(self.horizontalLayout) - - self.device_list = AutoHeightListWidget(self.frame) + self.device_list = AutoHeightListWidget(DeviceTagGroup) self.device_list.setObjectName("device_list") - self.verticalLayout_2.addWidget(self.device_list) - - self.verticalLayout.addWidget(self.frame) + self.verticalLayout.addWidget(self.device_list) self.set_icons()