122 lines
3.4 KiB
Python
122 lines
3.4 KiB
Python
"""
|
|
Metrics card widget for displaying statistics.
|
|
"""
|
|
|
|
from typing import List, Optional
|
|
from dataclasses import dataclass
|
|
|
|
try:
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGridLayout
|
|
)
|
|
from PyQt6.QtCore import Qt
|
|
from PyQt6.QtGui import QFont
|
|
HAS_QT = True
|
|
except ImportError:
|
|
HAS_QT = False
|
|
QWidget = object
|
|
|
|
|
|
@dataclass
|
|
class StatItem:
|
|
"""Single statistic item."""
|
|
label: str
|
|
value: str
|
|
change: Optional[str] = None
|
|
positive: bool = True
|
|
|
|
|
|
class MetricsCard(QWidget if HAS_QT else object):
|
|
"""Card displaying multiple metrics."""
|
|
|
|
def __init__(self, title: str = "Metrics", parent=None):
|
|
if not HAS_QT:
|
|
return
|
|
super().__init__(parent)
|
|
self._stats: List[StatItem] = []
|
|
self._setup_ui(title)
|
|
|
|
def _setup_ui(self, title: str):
|
|
self.setStyleSheet("""
|
|
MetricsCard {
|
|
background-color: #2d2d2d;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
}
|
|
QLabel {
|
|
color: #ffffff;
|
|
}
|
|
.metric-label {
|
|
color: #888888;
|
|
font-size: 12px;
|
|
}
|
|
.metric-value {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
.metric-positive {
|
|
color: #4caf50;
|
|
}
|
|
.metric-negative {
|
|
color: #f44336;
|
|
}
|
|
""")
|
|
|
|
layout = QVBoxLayout(self)
|
|
layout.setContentsMargins(15, 15, 15, 15)
|
|
layout.setSpacing(15)
|
|
|
|
# Title
|
|
self.title_label = QLabel(title)
|
|
font = QFont()
|
|
font.setBold(True)
|
|
font.setPointSize(14)
|
|
self.title_label.setFont(font)
|
|
layout.addWidget(self.title_label)
|
|
|
|
# Stats grid
|
|
self.stats_layout = QGridLayout()
|
|
self.stats_layout.setSpacing(10)
|
|
layout.addLayout(self.stats_layout)
|
|
|
|
layout.addStretch()
|
|
|
|
def set_stats(self, stats: List[StatItem]):
|
|
"""Update displayed statistics."""
|
|
if not HAS_QT:
|
|
return
|
|
|
|
self._stats = stats
|
|
|
|
# Clear existing
|
|
while self.stats_layout.count():
|
|
item = self.stats_layout.takeAt(0)
|
|
if item.widget():
|
|
item.widget().deleteLater()
|
|
|
|
# Add stats
|
|
for i, stat in enumerate(stats):
|
|
row = i // 2
|
|
col = (i % 2) * 2
|
|
|
|
# Label
|
|
label = QLabel(stat.label)
|
|
label.setStyleSheet("color: #888888; font-size: 11px;")
|
|
self.stats_layout.addWidget(label, row * 2, col)
|
|
|
|
# Value with optional change
|
|
value_text = stat.value
|
|
if stat.change:
|
|
value_text += f" <span style='color: {'#4caf50' if stat.positive else '#f44336'}'>{stat.change}</span>"
|
|
|
|
value_label = QLabel(value_text)
|
|
value_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffffff;")
|
|
self.stats_layout.addWidget(value_label, row * 2 + 1, col)
|
|
|
|
def update_stat(self, index: int, value: str, change: Optional[str] = None):
|
|
"""Update a single statistic."""
|
|
if 0 <= index < len(self._stats):
|
|
self._stats[index].value = value
|
|
self._stats[index].change = change
|
|
self.set_stats(self._stats)
|