0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-14 11:41:49 +02:00

WIP EXPANSION PANEL: Color works correct with qcolor and hex

This commit is contained in:
2025-01-31 20:29:46 +01:00
parent 4fb5922b4a
commit d4397f0b1a

View File

@ -1,6 +1,7 @@
import sys
from qtpy.QtCore import QEvent, Qt
from qtpy.QtGui import QColor
from qtpy.QtWidgets import (
QWidget,
QVBoxLayout,
@ -18,10 +19,12 @@ from bec_widgets.utils.bec_widget import BECWidget
class ExpansionPanel(BECWidget, QWidget):
"""
A collapsible container widget for use in Qt Designer.
This version avoids the traceback by deferring eventFilter installation
until after header_frame and content_frame exist, and by checking they're
not None before referencing them.
A collapsible container widget for Qt Designer.
Key improvements in this version:
- A "title_color" property (type "QColor") that accepts either
a QColor or a string (hex/rgb/named color).
- The label text color is updated accordingly.
"""
PLUGIN = True
@ -46,24 +49,23 @@ class ExpansionPanel(BECWidget, QWidget):
# Properties
self._expanded = expanded
self._title_color = QColor("black") # Default text color
self.header_frame = None
self.content_frame = None
# Setup the main layout
# Main layout
self._main_layout = QVBoxLayout(self)
self._main_layout.setContentsMargins(0, 0, 0, 0)
self._main_layout.setSpacing(0)
# Create the header
# Create header / content
self._init_header(title)
# Create the content
self._init_content()
# Make sure the content is initially visible or hidden
# Visible or hidden
self.content_frame.setVisible(expanded)
# Defer installing the event filter until everything is ready
# Defer event filter after constructing frames
self.installEventFilter(self)
def _init_header(self, title):
@ -100,19 +102,10 @@ class ExpansionPanel(BECWidget, QWidget):
self._main_layout.addWidget(self.header_frame)
def _init_content(self):
"""
Create the collapsible content frame, with its own QVBoxLayout.
"""
"""Create the collapsible content frame (with its own layout)."""
self.content_frame = QFrame(self)
self.content_frame.setObjectName("ContentFrame")
self.content_frame.setStyleSheet(
"""
#ContentFrame {
border: 1px solid #C0C0C0;
border-radius: 4px;
}
"""
)
self.content_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
self._content_layout = QVBoxLayout(self.content_frame)
self._content_layout.setContentsMargins(5, 5, 5, 5)
@ -139,7 +132,10 @@ class ExpansionPanel(BECWidget, QWidget):
@SafeProperty(str)
def title(self):
return self.label_title.text() if self.label_title else ""
"""Property for the header text."""
if self.label_title:
return self.label_title.text()
return ""
@title.setter
def title(self, value: str):
@ -147,33 +143,50 @@ class ExpansionPanel(BECWidget, QWidget):
self.label_title.setText(value)
@SafeProperty("QColor")
def label_color(self):
return self.label_title.palette().color(self.label_title.foregroundRole())
def title_color(self):
"""
A 'QColor' property recognized by Designer. We also accept
strings for convenience (e.g. "#FF0000", "rgb(0,255,0)", "blue").
"""
return self._title_color
@label_color.setter
def label_color(self, color):
self.label_title.setStyleSheet(f"color: {color};")
@title_color.setter
def title_color(self, color):
# TODO set bec widget color validator here
# If user passes a string instead of a QColor, parse it
if isinstance(color, str):
new_col = QColor(color)
if not new_col.isValid():
return # ignore invalid color string
self._title_color = new_col
elif isinstance(color, QColor):
self._title_color = color
else:
# unknown type, ignore
return
# Update label's style
color_hex = self._title_color.name() # e.g. "#RRGGBB"
self.label_title.setStyleSheet(f"color: {color_hex};")
@property
def content_layout(self) -> QVBoxLayout:
"""Return the layout of the content frame for programmatic additions."""
"""Layout of the content frame for programmatic additions."""
return self._content_layout
def event(self, e):
"""
Override event() to detect when child widgets are added by Designer,
so we can place them into the content layout if they are not the header frame
or content frame themselves.
If Designer adds child widgets, re-parent them into _content_layout
unless they're our known frames.
"""
if e.type() == QEvent.ChildAdded:
child_obj = e.child()
# Only process if we have a valid child widget
if child_obj is not None and isinstance(child_obj, QWidget):
# Also check if we have valid references to header_frame & content_frame
if self.header_frame is not None and self.content_frame is not None:
# If it's not our known frames, place it inside the content layout
if child_obj not in (self.header_frame, self.content_frame):
self._content_layout.addWidget(child_obj)
return super().event(e)
@ -183,13 +196,22 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
panel = ExpansionPanel(title="Test Panel", expanded=True)
panel = ExpansionPanel(title="Test Panel", expanded=False)
panel2 = ExpansionPanel(title="Test Panel 2", expanded=False)
# You can set the color via hex string
panel.title_color = "#FF00FF"
panel.content_layout.addWidget(QPushButton("Test Button 1"))
panel.content_layout.addWidget(QPushButton("Test Button 2"))
panel2.content_layout.addWidget(QPushButton("Test Button 1"))
panel2.content_layout.addWidget(QPushButton("Test Button 2"))
container = QWidget()
lay = QVBoxLayout(container)
lay.addWidget(panel)
lay.addWidget(panel2)
container.resize(400, 300)
container.show()