← Back to CollapsibleSection documentation
CollapsibleSection - Source Code¶
File: Distributed_Design_Optimizer/postprocess/widgets/CollapsibleSection.py
# Copyright (C) The DistributedDesignOptimizer Contributors
# Licensed under the GNU General Public License v3.0. See LICENSE file for details.
#
# Additional Request:
# We kindly request that any modifications or changes to the source code be
# shared with us by submitting them as pull requests to our GitHub repository.
# This request is not legally binding but is made in the spirit of
# collaboration and open-source development.
"""Reusable collapsible section widget for panels with expandable groups."""
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QToolButton, QSizePolicy,
)
from PySide6.QtCore import Signal, Qt
from PySide6.QtGui import QColor
from ..styles.Theme import get_accent_color
class CollapsibleSection(QWidget):
"""A section with a clickable header that expands/collapses its content."""
user_toggled = Signal(bool)
def __init__(self, title: str, parent: QWidget | None = None,
initially_expanded: bool = True) -> None:
"""Initialize the collapsible section.
Args:
title: Header text displayed on the toggle button.
parent: Optional parent widget.
initially_expanded: Whether the section starts expanded.
"""
super().__init__(parent)
self._programmatic = False
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
# Header button
self._toggle_btn = QToolButton()
self._toggle_btn.setText(f" {title}")
self._toggle_btn.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
self._toggle_btn.setArrowType(
Qt.ArrowType.DownArrow if initially_expanded else Qt.ArrowType.RightArrow
)
self._toggle_btn.setCheckable(True)
self._toggle_btn.setChecked(initially_expanded)
self._toggle_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
self._apply_header_style()
self._toggle_btn.toggled.connect(self._on_toggle)
layout.addWidget(self._toggle_btn)
# Content area
self._content = QWidget()
self._content_layout = QVBoxLayout(self._content)
self._content_layout.setContentsMargins(8, 4, 4, 4)
self._content.setVisible(initially_expanded)
layout.addWidget(self._content)
@property
def content_layout(self) -> QVBoxLayout:
"""Return the layout for adding child widgets.
Returns:
The vertical box layout that holds the section's content.
"""
return self._content_layout
def set_expanded(self, expanded: bool) -> None:
"""Programmatically expand or collapse the section.
Args:
expanded: Whether the section should be expanded.
"""
self._programmatic = True
self._toggle_btn.setChecked(expanded)
self._programmatic = False
def showEvent(self, event) -> None: # noqa: N802
"""Re-apply header styling when the widget becomes visible.
Args:
event: The show event triggered by Qt.
"""
super().showEvent(event)
self._apply_header_style()
def _apply_header_style(self) -> None:
accent = get_accent_color()
c = QColor(accent)
hover_bg = f"rgba({c.red()}, {c.green()}, {c.blue()}, 0.08)"
self._toggle_btn.setStyleSheet(
"QToolButton { font-weight: bold; font-size: 9pt; "
"border: none; padding: 4px 6px; text-align: left; }"
f"QToolButton:hover {{ background-color: {hover_bg}; border-radius: 3px; }}"
)
def _on_toggle(self, checked: bool) -> None:
self._toggle_btn.setArrowType(
Qt.ArrowType.DownArrow if checked else Qt.ArrowType.RightArrow
)
self._content.setVisible(checked)
if not self._programmatic:
self.user_toggled.emit(checked)