mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 11:41:49 +02:00
WIP EXPANSION PANEL: Basic expantion panel demo
This commit is contained in:
@ -0,0 +1,175 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtWidgets import QSizePolicy
|
||||
from qtpy.QtWidgets import (
|
||||
QApplication,
|
||||
QWidget,
|
||||
QVBoxLayout,
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QPushButton,
|
||||
QLabel,
|
||||
QFormLayout,
|
||||
QComboBox,
|
||||
QLineEdit,
|
||||
QSpinBox,
|
||||
QToolBox,
|
||||
QColorDialog,
|
||||
)
|
||||
from qtpy.QtCore import Qt
|
||||
|
||||
from bec_widgets.qt_utils.error_popups import SafeProperty, SafeSlot
|
||||
from bec_widgets.utils import ConnectionConfig
|
||||
from bec_widgets.utils.bec_widget import BECWidget
|
||||
from bec_widgets.utils.colors import set_theme
|
||||
|
||||
|
||||
class ExpansionPanel(BECWidget, QWidget):
|
||||
PLUGIN = True
|
||||
RPC = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget | None = None,
|
||||
config: ConnectionConfig | None = None,
|
||||
client=None,
|
||||
gui_id: str | None = None,
|
||||
title="Panel",
|
||||
color: str | None = "#C0C0C0",
|
||||
expanded=False,
|
||||
) -> None:
|
||||
if config is None:
|
||||
config = ConnectionConfig(widget_class=self.__class__.__name__)
|
||||
super().__init__(client=client, gui_id=gui_id, config=config)
|
||||
QWidget.__init__(self, parent=parent)
|
||||
|
||||
self.setObjectName("ExpansionPanel")
|
||||
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
|
||||
# Properties
|
||||
self._expanded = expanded
|
||||
|
||||
# Main vertical layout: header + content
|
||||
self._main_layout = QVBoxLayout(self)
|
||||
self._main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self._main_layout.setSpacing(0)
|
||||
|
||||
# Header
|
||||
self.header_frame = QFrame(self)
|
||||
self.header_frame.setObjectName("headerFrame")
|
||||
self.header_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.header_frame.setStyleSheet(
|
||||
"""
|
||||
#headerFrame {
|
||||
border: 1px solid #C0C0C0;
|
||||
border-radius: 5;
|
||||
}
|
||||
"""
|
||||
) # TODO think about the colors of the header frame and rounding the corners
|
||||
|
||||
header_layout = QHBoxLayout(self.header_frame)
|
||||
header_layout.setContentsMargins(3, 2, 3, 2)
|
||||
header_layout.setSpacing(3)
|
||||
|
||||
# Toggle button (arrow)
|
||||
self.btn_toggle = QPushButton("▼" if expanded else "►", self.header_frame)
|
||||
self.btn_toggle.setFixedSize(25, 25)
|
||||
self.btn_toggle.setStyleSheet("border: none; font-weight: bold;")
|
||||
self.btn_toggle.clicked.connect(self.toggle)
|
||||
header_layout.addWidget(self.btn_toggle, alignment=Qt.AlignVCenter)
|
||||
|
||||
# Title label
|
||||
self.label_title = QLabel(title, self.header_frame)
|
||||
self.label_title.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
header_layout.addWidget(self.label_title, alignment=Qt.AlignVCenter | Qt.AlignLeft)
|
||||
|
||||
# Spacer stretch
|
||||
header_layout.addStretch()
|
||||
|
||||
self._main_layout.addWidget(self.header_frame)
|
||||
|
||||
# Content area
|
||||
self.content_frame = QFrame(self)
|
||||
self.content_frame.setObjectName("ContentFrame")
|
||||
self.content_frame.setStyleSheet(
|
||||
"""
|
||||
#ContentFrame {
|
||||
border: 1px solid #C0C0C0;
|
||||
border-radius: 5;
|
||||
}
|
||||
"""
|
||||
)
|
||||
self.content_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
self._content_layout = QVBoxLayout(self.content_frame)
|
||||
self._content_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self._content_layout.setSpacing(5)
|
||||
|
||||
self._main_layout.addWidget(self.content_frame)
|
||||
|
||||
self.content_frame.setVisible(expanded)
|
||||
|
||||
@SafeSlot()
|
||||
def toggle(self):
|
||||
"""Collapse or expand the content area."""
|
||||
self._expanded = not self._expanded
|
||||
self.content_frame.setVisible(self._expanded)
|
||||
self.btn_toggle.setText("▼" if self._expanded else "►")
|
||||
|
||||
@SafeProperty(bool)
|
||||
def expanded(self) -> bool:
|
||||
return self._expanded
|
||||
|
||||
@expanded.setter
|
||||
def expanded(self, value: bool):
|
||||
if value != self._expanded:
|
||||
self.toggle()
|
||||
|
||||
@SafeProperty(str)
|
||||
def title(self):
|
||||
return self.label_title.text()
|
||||
|
||||
@title.setter
|
||||
def title(self, value: str):
|
||||
self.label_title.setText(value)
|
||||
|
||||
@SafeProperty("QColor")
|
||||
def label_color(self):
|
||||
return self.label_title.palette().color(self.label_title.foregroundRole())
|
||||
|
||||
@label_color.setter
|
||||
def label_color(self, color):
|
||||
self.label_title.setStyleSheet(f"color: {color};")
|
||||
|
||||
@property
|
||||
def content_layout(self) -> QVBoxLayout:
|
||||
"""
|
||||
Return the layout of the content frame,
|
||||
so you can add sub-widgets at any time.
|
||||
"""
|
||||
return self._content_layout
|
||||
|
||||
|
||||
class DemoApp(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
|
||||
self.panel1 = ExpansionPanel(title="Panel 1", expanded=False)
|
||||
self.layout.addWidget(self.panel1)
|
||||
btn1 = QPushButton("Button 1")
|
||||
self.panel1._content_layout.addWidget(btn1)
|
||||
|
||||
self.panel2 = ExpansionPanel(title="Panel 2", color="#FF0000")
|
||||
self.layout.addWidget(self.panel2)
|
||||
|
||||
self.panel3 = ExpansionPanel(title="Panel 3", color="#00FF00", expanded=False)
|
||||
self.layout.addWidget(self.panel3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
set_theme("dark")
|
||||
panel = DemoApp()
|
||||
panel.show()
|
||||
sys.exit(app.exec_())
|
@ -0,0 +1 @@
|
||||
{'files': ['expansion_panel.py']}
|
@ -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 bec_widgets.utils.bec_designer import designer_material_icon
|
||||
from bec_widgets.widgets.containers.expantion_panel.expansion_panel import ExpansionPanel
|
||||
|
||||
DOM_XML = """
|
||||
<ui language='c++'>
|
||||
<widget class='ExpansionPanel' name='expansion_panel'>
|
||||
</widget>
|
||||
</ui>
|
||||
"""
|
||||
|
||||
|
||||
class ExpansionPanelPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._form_editor = None
|
||||
|
||||
def createWidget(self, parent):
|
||||
t = ExpansionPanel(parent)
|
||||
return t
|
||||
|
||||
def domXml(self):
|
||||
return DOM_XML
|
||||
|
||||
def group(self):
|
||||
return ""
|
||||
|
||||
def icon(self):
|
||||
return designer_material_icon(ExpansionPanel.ICON_NAME)
|
||||
|
||||
def includeFile(self):
|
||||
return "expansion_panel"
|
||||
|
||||
def initialize(self, form_editor):
|
||||
self._form_editor = form_editor
|
||||
|
||||
def isContainer(self):
|
||||
return True
|
||||
|
||||
def isInitialized(self):
|
||||
return self._form_editor is not None
|
||||
|
||||
def name(self):
|
||||
return "ExpansionPanel"
|
||||
|
||||
def toolTip(self):
|
||||
return "ExpansionPanel"
|
||||
|
||||
def whatsThis(self):
|
||||
return self.toolTip()
|
@ -0,0 +1,17 @@
|
||||
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.containers.expantion_panel.expansion_panel_plugin import (
|
||||
ExpansionPanelPlugin,
|
||||
)
|
||||
|
||||
QPyDesignerCustomWidgetCollection.addCustomWidget(ExpansionPanelPlugin())
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
Reference in New Issue
Block a user