mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat(widgets): added device_browser widget
This commit is contained in:
0
bec_widgets/widgets/device_browser/__init__.py
Normal file
0
bec_widgets/widgets/device_browser/__init__.py
Normal file
107
bec_widgets/widgets/device_browser/device_browser.py
Normal file
107
bec_widgets/widgets/device_browser/device_browser.py
Normal file
@ -0,0 +1,107 @@
|
||||
import os
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from bec_lib.callback_handler import EventType
|
||||
from pyqtgraph import SignalProxy
|
||||
from qtpy.QtCore import Signal, Slot
|
||||
from qtpy.QtWidgets import QLineEdit, QListWidgetItem, QVBoxLayout, QWidget
|
||||
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.ui_loader import UILoader
|
||||
from bec_widgets.widgets.device_browser.device_item import DeviceItem
|
||||
|
||||
|
||||
class DeviceBrowser(BECWidget, QWidget):
|
||||
device_update: Signal = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: Optional[QWidget] = None,
|
||||
config=None,
|
||||
client=None,
|
||||
gui_id: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(client=client, config=config, gui_id=gui_id)
|
||||
QWidget.__init__(self, parent)
|
||||
|
||||
self.get_bec_shortcuts()
|
||||
self.ui = None
|
||||
self.ini_ui()
|
||||
|
||||
self.proxy_device_update = SignalProxy(
|
||||
self.ui.filter_input.textChanged, rateLimit=500, slot=self.update_device_list
|
||||
)
|
||||
self.bec_dispatcher.client.callbacks.register(
|
||||
EventType.DEVICE_UPDATE, self.on_device_update
|
||||
)
|
||||
self.device_update.connect(self.update_device_list)
|
||||
|
||||
self.update_device_list()
|
||||
|
||||
def ini_ui(self) -> None:
|
||||
"""
|
||||
Initialize the UI by loading the UI file and setting the layout.
|
||||
"""
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
ui_file_path = os.path.join(os.path.dirname(__file__), "device_browser.ui")
|
||||
self.ui = UILoader(self).loader(ui_file_path)
|
||||
layout.addWidget(self.ui)
|
||||
self.setLayout(layout)
|
||||
|
||||
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_update.emit()
|
||||
|
||||
@Slot()
|
||||
def update_device_list(self) -> None:
|
||||
"""
|
||||
Update the device list based on the filter input.
|
||||
There are two ways to trigger this function:
|
||||
1. By changing the text in the filter input.
|
||||
2. By emitting the device_update signal.
|
||||
|
||||
Either way, the function will filter the devices based on the filter input text and update the device list.
|
||||
"""
|
||||
filter_text = self.ui.filter_input.text()
|
||||
try:
|
||||
regex = re.compile(filter_text, re.IGNORECASE)
|
||||
except re.error:
|
||||
regex = None # Invalid regex, disable filtering
|
||||
|
||||
dev_list = self.ui.device_list
|
||||
dev_list.clear()
|
||||
for device in self.dev:
|
||||
if regex is None or regex.search(device):
|
||||
item = QListWidgetItem(dev_list)
|
||||
device_item = DeviceItem(device)
|
||||
|
||||
# pylint: disable=protected-access
|
||||
tooltip = self.dev[device]._config.get("description", "")
|
||||
device_item.setToolTip(tooltip)
|
||||
item.setSizeHint(device_item.sizeHint())
|
||||
dev_list.setItemWidget(item, device_item)
|
||||
dev_list.addItem(item)
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
import sys
|
||||
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
from bec_widgets.utils.colors import apply_theme
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
apply_theme("light")
|
||||
widget = DeviceBrowser()
|
||||
widget.show()
|
||||
sys.exit(app.exec_())
|
@ -0,0 +1 @@
|
||||
{'files': ['device_browser.py']}
|
44
bec_widgets/widgets/device_browser/device_browser.ui
Normal file
44
bec_widgets/widgets/device_browser/device_browser.ui
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>406</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="browser_group_box">
|
||||
<property name="title">
|
||||
<string>Device Browser</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="filter_layout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="filter_input">
|
||||
<property name="placeholderText">
|
||||
<string>Filter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="device_list"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
54
bec_widgets/widgets/device_browser/device_browser_plugin.py
Normal file
54
bec_widgets/widgets/device_browser/device_browser_plugin.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
|
||||
from qtpy.QtGui import QIcon
|
||||
|
||||
from bec_widgets.widgets.device_browser.device_browser import DeviceBrowser
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='DeviceBrowser' name='device_browser'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
|
||||
class DeviceBrowserPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = DeviceBrowser(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
|
||||
def icon(self):
|
||||
return QIcon()
|
||||
|
||||
def includeFile(self):
|
||||
return "device_browser"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return False
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "DeviceBrowser"
|
||||
|
||||
def toolTip(self):
|
||||
return "DeviceBrowser"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
@ -0,0 +1 @@
|
||||
from .device_item import DeviceItem
|
@ -0,0 +1,53 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from qtpy.QtCore import QMimeData, Qt
|
||||
from qtpy.QtGui import QDrag
|
||||
from qtpy.QtWidgets import QHBoxLayout, QLabel, QWidget
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qtpy.QtGui import QMouseEvent
|
||||
|
||||
|
||||
class DeviceItem(QWidget):
|
||||
def __init__(self, device: str) -> None:
|
||||
super().__init__()
|
||||
self.device = device
|
||||
layout = QHBoxLayout()
|
||||
layout.setContentsMargins(10, 2, 10, 2)
|
||||
self.label = QLabel(device)
|
||||
layout.addWidget(self.label)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.setStyleSheet(
|
||||
"""
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
"""
|
||||
)
|
||||
|
||||
def mousePressEvent(self, event: QMouseEvent) -> None:
|
||||
if event.button() == Qt.LeftButton:
|
||||
drag = QDrag(self)
|
||||
mime_data = QMimeData()
|
||||
mime_data.setText(self.device)
|
||||
drag.setMimeData(mime_data)
|
||||
drag.exec_(Qt.MoveAction)
|
||||
|
||||
def mouseDoubleClickEvent(self, event: QMouseEvent) -> None:
|
||||
print("Double Clicked")
|
||||
# TODO: Implement double click action for opening the device properties dialog
|
||||
return super().mouseDoubleClickEvent(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
from qtpy.QtWidgets import QApplication
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
widget = DeviceItem("Device")
|
||||
widget.show()
|
||||
sys.exit(app.exec_())
|
@ -0,0 +1,15 @@
|
||||
def main(): # pragma: no cover
|
||||
from qtpy import PYSIDE6
|
||||
|
||||
if not PYSIDE6:
|
||||
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
|
||||
return
|
||||
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
|
||||
|
||||
from bec_widgets.widgets.device_browser.device_browser_plugin import DeviceBrowserPlugin
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(DeviceBrowserPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
Reference in New Issue
Block a user