""" Base widget components for the dashboard. """ from typing import Any, Optional, Callable try: from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QSizePolicy ) from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QFont HAS_QT = True except ImportError: HAS_QT = False QWidget = object pyqtSignal = lambda *a, **k: None class WidgetHeader(QFrame if HAS_QT else object): """Header component for widgets with title and controls.""" collapsed_changed = pyqtSignal(bool) if HAS_QT else None close_requested = pyqtSignal() if HAS_QT else None def __init__(self, title: str = "Widget", icon: str = "📦", parent=None): if not HAS_QT: return super().__init__(parent) self._collapsed = False self._setup_ui(title, icon) def _setup_ui(self, title: str, icon: str): self.setFrameShape(QFrame.Shape.StyledPanel) self.setStyleSheet(""" WidgetHeader { background-color: #2d2d2d; border-radius: 8px 8px 0 0; padding: 8px; } """) layout = QHBoxLayout(self) layout.setContentsMargins(10, 5, 10, 5) layout.setSpacing(10) # Icon self.icon_label = QLabel(icon) self.icon_label.setStyleSheet("font-size: 16px;") layout.addWidget(self.icon_label) # Title self.title_label = QLabel(title) font = QFont() font.setBold(True) self.title_label.setFont(font) self.title_label.setStyleSheet("color: #ffffff;") layout.addWidget(self.title_label) layout.addStretch() # Collapse button self.collapse_btn = QPushButton("▼") self.collapse_btn.setFixedSize(24, 24) self.collapse_btn.setStyleSheet(""" QPushButton { background-color: #3d3d3d; color: #ffffff; border: none; border-radius: 4px; } QPushButton:hover { background-color: #4d4d4d; } """) self.collapse_btn.clicked.connect(self._toggle_collapse) layout.addWidget(self.collapse_btn) # Close button self.close_btn = QPushButton("×") self.close_btn.setFixedSize(24, 24) self.close_btn.setStyleSheet(""" QPushButton { background-color: #3d3d3d; color: #ff6b6b; border: none; border-radius: 4px; font-size: 16px; font-weight: bold; } QPushButton:hover { background-color: #ff6b6b; color: #ffffff; } """) self.close_btn.clicked.connect(lambda: self.close_requested.emit()) layout.addWidget(self.close_btn) def _toggle_collapse(self): self._collapsed = not self._collapsed self.collapse_btn.setText("▶" if self._collapsed else "▼") self.collapsed_changed.emit(self._collapsed) def set_collapsed(self, collapsed: bool): self._collapsed = collapsed self.collapse_btn.setText("▶" if collapsed else "▼") class WidgetContent(QFrame if HAS_QT else object): """Content container for widgets.""" def __init__(self, parent=None): if not HAS_QT: return super().__init__(parent) self.setFrameShape(QFrame.Shape.StyledPanel) self.setStyleSheet(""" WidgetContent { background-color: #1e1e1e; border-radius: 0 0 8px 8px; padding: 10px; } """) self._layout = QVBoxLayout(self) self._layout.setContentsMargins(10, 10, 10, 10) self._layout.setSpacing(10) def add_widget(self, widget): if HAS_QT: self._layout.addWidget(widget) def layout(self): return self._layout if HAS_QT else None class DashboardWidget(QFrame if HAS_QT else object): """Complete dashboard widget with header and content.""" def __init__( self, title: str = "Widget", icon: str = "📦", size: tuple = (300, 200), parent=None ): if not HAS_QT: return super().__init__(parent) self._size = size self._setup_ui(title, icon) def _setup_ui(self, title: str, icon: str): self.setStyleSheet(""" DashboardWidget { background-color: transparent; border: 1px solid #3d3d3d; border-radius: 8px; } """) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) # Header self.header = WidgetHeader(title, icon, self) self.header.collapsed_changed.connect(self._on_collapse_changed) layout.addWidget(self.header) # Content self.content = WidgetContent(self) layout.addWidget(self.content) self.setFixedSize(*self._size) def _on_collapse_changed(self, collapsed: bool): self.content.setVisible(not collapsed) if collapsed: self.setFixedHeight(self.header.height()) else: self.setFixedHeight(self._size[1]) def set_content_widget(self, widget: QWidget): """Set the main content widget.""" if HAS_QT: # Clear existing while self.content.layout().count(): item = self.content.layout().takeAt(0) if item.widget(): item.widget().deleteLater() self.content.layout().addWidget(widget)