EU-Utility/core/classy_dashboard.py

598 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
EU-Utility - Classy Dashboard UI
================================
A refined, elegant dashboard interface for second monitor use.
Features a clean layout with a proper Dashboard for widgets,
removing the sidebar for a more modern feel.
Design Philosophy:
- Clean, minimalist aesthetic
- Premium dark theme with subtle accents
- Glassmorphism effects
- Smooth animations
- Widget-focused dashboard
"""
from PyQt6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QStackedWidget, QLabel, QPushButton, QFrame,
QScrollArea, QGridLayout, QSizePolicy, QSpacerItem
)
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QSize, QPropertyAnimation, QEasingCurve
from PyQt6.QtGui import QColor, QPainter, QLinearGradient, QFont, QIcon
from core.eu_styles import get_all_colors, EU_TYPOGRAPHY, EU_SIZES
class GlassCard(QFrame):
"""Glassmorphism card widget."""
def __init__(self, parent=None):
super().__init__(parent)
self.setObjectName("glassCard")
self._setup_style()
def _setup_style(self):
c = get_all_colors()
self.setStyleSheet(f"""
QFrame#glassCard {{
background: rgba(45, 55, 72, 0.6);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
}}
QFrame#glassCard:hover {{
background: rgba(45, 55, 72, 0.75);
border: 1px solid rgba(255, 140, 66, 0.3);
}}
""")
class ElegantTabButton(QPushButton):
"""Elegant tab button with smooth hover effects."""
clicked_tab = pyqtSignal(str)
def __init__(self, text: str, icon: str = None, tab_id: str = None, parent=None):
super().__init__(text, parent)
self.tab_id = tab_id or text.lower()
self._active = False
self.setCursor(Qt.CursorShape.PointingHandCursor)
self.setFixedHeight(44)
self.clicked.connect(lambda: self.clicked_tab.emit(self.tab_id))
self._update_style()
def set_active(self, active: bool):
self._active = active
self._update_style()
def _update_style(self):
c = get_all_colors()
if self._active:
self.setStyleSheet(f"""
QPushButton {{
background: transparent;
color: {c['accent_orange']};
border: none;
border-bottom: 2px solid {c['accent_orange']};
padding: 0 24px;
font-size: 14px;
font-weight: 600;
}}
""")
else:
self.setStyleSheet(f"""
QPushButton {{
background: transparent;
color: {c['text_secondary']};
border: none;
border-bottom: 2px solid transparent;
padding: 0 24px;
font-size: 14px;
font-weight: 500;
}}
QPushButton:hover {{
color: {c['text_primary']};
}}
""")
class DashboardWidget(QFrame):
"""A widget container for the Dashboard grid."""
def __init__(self, title: str, content_widget=None, parent=None):
super().__init__(parent)
self.title = title
self._setup_ui(content_widget)
def _setup_ui(self, content_widget):
c = get_all_colors()
self.setStyleSheet(f"""
QFrame {{
background: rgba(35, 41, 54, 0.8);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 20px;
}}
""")
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(16)
# Header with title
header = QLabel(self.title)
header.setStyleSheet(f"""
color: {c['text_primary']};
font-size: 16px;
font-weight: 600;
""")
layout.addWidget(header)
# Separator line
separator = QFrame()
separator.setFixedHeight(1)
separator.setStyleSheet(f"background: rgba(255, 255, 255, 0.08);")
layout.addWidget(separator)
# Content
if content_widget:
layout.addWidget(content_widget, 1)
self.setMinimumSize(280, 200)
class ClassyDashboardWindow(QMainWindow):
"""
Refined EU-Utility main window with elegant Dashboard.
Features:
- Clean, sidebar-free layout
- Dashboard tab for widgets (second monitor friendly)
- Elegant glassmorphism design
- Smooth animations and transitions
"""
def __init__(self, plugin_manager, parent=None):
super().__init__(parent)
self.plugin_manager = plugin_manager
self.current_tab = "dashboard"
self.tab_buttons = {}
self._setup_window()
self._setup_central_widget()
self._create_header()
self._create_tabs()
self._create_content_area()
# Show Dashboard by default
self._switch_tab("dashboard")
def _setup_window(self):
"""Setup window properties."""
self.setWindowTitle("EU-Utility")
self.setMinimumSize(1200, 800)
self.resize(1400, 900)
# Center window
screen = self.screen().geometry()
self.move(
(screen.width() - self.width()) // 2,
(screen.height() - self.height()) // 2
)
# Dark, elegant background
c = get_all_colors()
self.setStyleSheet(f"""
QMainWindow {{
background: #0f1419;
}}
""")
def _setup_central_widget(self):
"""Create central widget with gradient background."""
self.central = QWidget()
self.setCentralWidget(self.central)
layout = QVBoxLayout(self.central)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
def _create_header(self):
"""Create elegant header with logo and controls."""
c = get_all_colors()
header = QFrame()
header.setFixedHeight(72)
header.setStyleSheet(f"""
QFrame {{
background: rgba(15, 20, 25, 0.95);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}}
""")
layout = QHBoxLayout(header)
layout.setContentsMargins(32, 0, 32, 0)
layout.setSpacing(24)
# Logo
logo = QLabel("◆ EU-Utility")
logo.setStyleSheet(f"""
color: {c['accent_orange']};
font-size: 22px;
font-weight: 700;
letter-spacing: 0.5px;
""")
layout.addWidget(logo)
# Spacer
layout.addStretch()
# Window controls
minimize_btn = QPushButton("")
minimize_btn.setFixedSize(40, 32)
minimize_btn.setStyleSheet(f"""
QPushButton {{
background: transparent;
color: {c['text_secondary']};
border: none;
font-size: 18px;
border-radius: 6px;
}}
QPushButton:hover {{
background: rgba(255, 255, 255, 0.1);
color: {c['text_primary']};
}}
""")
minimize_btn.clicked.connect(self.showMinimized)
close_btn = QPushButton("×")
close_btn.setFixedSize(40, 32)
close_btn.setStyleSheet(f"""
QPushButton {{
background: transparent;
color: {c['text_secondary']};
border: none;
font-size: 20px;
border-radius: 6px;
}}
QPushButton:hover {{
background: #e53e3e;
color: white;
}}
""")
close_btn.clicked.connect(self.close)
layout.addWidget(minimize_btn)
layout.addWidget(close_btn)
self.central.layout().addWidget(header)
def _create_tabs(self):
"""Create elegant tab bar."""
c = get_all_colors()
tab_bar = QFrame()
tab_bar.setFixedHeight(64)
tab_bar.setStyleSheet(f"""
QFrame {{
background: rgba(15, 20, 25, 0.9);
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}}
""")
layout = QHBoxLayout(tab_bar)
layout.setContentsMargins(32, 0, 32, 0)
layout.setSpacing(8)
layout.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
# Tab definitions
tabs = [
("Dashboard", "dashboard"),
("Plugins", "plugins"),
("Widgets", "widgets"),
("Settings", "settings"),
]
for text, tab_id in tabs:
btn = ElegantTabButton(text, tab_id=tab_id)
btn.clicked_tab.connect(self._switch_tab)
self.tab_buttons[tab_id] = btn
layout.addWidget(btn)
layout.addStretch()
self.central.layout().addWidget(tab_bar)
def _create_content_area(self):
"""Create main content stack."""
self.content_stack = QStackedWidget()
self.content_stack.setStyleSheet("background: transparent;")
# Create all tabs
self.dashboard_tab = self._create_dashboard_tab()
self.plugins_tab = self._create_plugins_tab()
self.widgets_tab = self._create_widgets_tab()
self.settings_tab = self._create_settings_tab()
self.content_stack.addWidget(self.dashboard_tab)
self.content_stack.addWidget(self.plugins_tab)
self.content_stack.addWidget(self.widgets_tab)
self.content_stack.addWidget(self.settings_tab)
self.central.layout().addWidget(self.content_stack, 1)
def _create_dashboard_tab(self) -> QWidget:
"""Create the Dashboard tab - a grid for widgets."""
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
scroll.setStyleSheet("""
QScrollArea { background: transparent; border: none; }
QScrollBar:vertical {
background: transparent;
width: 8px;
margin: 0;
}
QScrollBar::handle:vertical {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
min-height: 40px;
}
QScrollBar::handle:vertical:hover {
background: rgba(255, 255, 255, 0.2);
}
""")
container = QWidget()
container.setStyleSheet("background: transparent;")
layout = QVBoxLayout(container)
layout.setContentsMargins(32, 32, 32, 32)
layout.setSpacing(24)
# Welcome header
c = get_all_colors()
welcome = QLabel("Dashboard")
welcome.setStyleSheet(f"""
color: {c['text_primary']};
font-size: 32px;
font-weight: 700;
""")
layout.addWidget(welcome)
subtitle = QLabel("Your personal command center. Add widgets to track what's important.")
subtitle.setStyleSheet(f"""
color: {c['text_secondary']};
font-size: 14px;
margin-bottom: 16px;
""")
layout.addWidget(subtitle)
# Widget grid
grid = QGridLayout()
grid.setSpacing(20)
grid.setColumnStretch(0, 1)
grid.setColumnStretch(1, 1)
grid.setColumnStretch(2, 1)
# Add some default widgets
widgets = [
("System Status", self._create_system_widget()),
("Quick Actions", self._create_actions_widget()),
("Recent Activity", self._create_activity_widget()),
]
for i, (title, content) in enumerate(widgets):
widget = DashboardWidget(title, content)
grid.addWidget(widget, i // 3, i % 3)
layout.addLayout(grid)
layout.addStretch()
scroll.setWidget(container)
return scroll
def _create_system_widget(self) -> QWidget:
"""Create system status widget content."""
c = get_all_colors()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setSpacing(12)
status_items = [
("", "Log Reader", "Active", "#4ecca3"),
("", "OCR Service", "Ready", "#4ecca3"),
("", "Event Bus", "Running", "#4ecca3"),
("", "Nexus API", "Idle", "#ffd93d"),
]
for icon, name, status, color in status_items:
row = QHBoxLayout()
icon_label = QLabel(icon)
icon_label.setStyleSheet(f"color: {color}; font-size: 12px;")
row.addWidget(icon_label)
name_label = QLabel(name)
name_label.setStyleSheet(f"color: {c['text_primary']}; font-size: 13px;")
row.addWidget(name_label)
row.addStretch()
status_label = QLabel(status)
status_label.setStyleSheet(f"color: {c['text_secondary']}; font-size: 12px;")
row.addWidget(status_label)
layout.addLayout(row)
return widget
def _create_actions_widget(self) -> QWidget:
"""Create quick actions widget content."""
c = get_all_colors()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setSpacing(10)
actions = [
("Scan Skills", "📷"),
("Check Loot", "📦"),
("Search Nexus", "🔍"),
("Settings", "⚙️"),
]
for text, emoji in actions:
btn = QPushButton(f"{emoji} {text}")
btn.setFixedHeight(40)
btn.setStyleSheet(f"""
QPushButton {{
background: rgba(255, 255, 255, 0.05);
color: {c['text_primary']};
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
font-size: 13px;
text-align: left;
padding-left: 16px;
}}
QPushButton:hover {{
background: rgba(255, 140, 66, 0.15);
border: 1px solid rgba(255, 140, 66, 0.3);
}}
""")
layout.addWidget(btn)
return widget
def _create_activity_widget(self) -> QWidget:
"""Create recent activity widget content."""
c = get_all_colors()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setSpacing(10)
activities = [
("Plugin updated", "Clock Widget v1.0.1", "2m ago"),
("Scan completed", "Found 12 skills", "15m ago"),
("Settings changed", "Theme: Dark", "1h ago"),
]
for title, detail, time in activities:
item = QFrame()
item.setStyleSheet("background: transparent;")
row = QVBoxLayout(item)
row.setSpacing(2)
top = QHBoxLayout()
title_label = QLabel(title)
title_label.setStyleSheet(f"color: {c['text_primary']}; font-size: 13px; font-weight: 500;")
top.addWidget(title_label)
top.addStretch()
time_label = QLabel(time)
time_label.setStyleSheet(f"color: {c['text_muted']}; font-size: 11px;")
top.addWidget(time_label)
row.addLayout(top)
detail_label = QLabel(detail)
detail_label.setStyleSheet(f"color: {c['text_secondary']}; font-size: 12px;")
row.addWidget(detail_label)
layout.addWidget(item)
layout.addStretch()
return widget
def _create_plugins_tab(self) -> QWidget:
"""Create Plugins tab content."""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(32, 32, 32, 32)
c = get_all_colors()
title = QLabel("Plugins")
title.setStyleSheet(f"""
color: {c['text_primary']};
font-size: 32px;
font-weight: 700;
""")
layout.addWidget(title)
# Plugin list placeholder
placeholder = QLabel("Plugin management interface - to be implemented")
placeholder.setStyleSheet(f"color: {c['text_secondary']}; padding: 40px;")
layout.addWidget(placeholder)
layout.addStretch()
return widget
def _create_widgets_tab(self) -> QWidget:
"""Create Widgets tab content."""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(32, 32, 32, 32)
c = get_all_colors()
title = QLabel("Widgets")
title.setStyleSheet(f"""
color: {c['text_primary']};
font-size: 32px;
font-weight: 700;
""")
layout.addWidget(title)
placeholder = QLabel("Widget gallery - to be implemented")
placeholder.setStyleSheet(f"color: {c['text_secondary']}; padding: 40px;")
layout.addWidget(placeholder)
layout.addStretch()
return widget
def _create_settings_tab(self) -> QWidget:
"""Create Settings tab content."""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setContentsMargins(32, 32, 32, 32)
c = get_all_colors()
title = QLabel("Settings")
title.setStyleSheet(f"""
color: {c['text_primary']};
font-size: 32px;
font-weight: 700;
""")
layout.addWidget(title)
placeholder = QLabel("Settings interface - to be implemented")
placeholder.setStyleSheet(f"color: {c['text_secondary']}; padding: 40px;")
layout.addWidget(placeholder)
layout.addStretch()
return widget
def _switch_tab(self, tab_id: str):
"""Switch to the specified tab."""
self.current_tab = tab_id
# Update button states
for btn_id, btn in self.tab_buttons.items():
btn.set_active(btn_id == tab_id)
# Update content stack
tab_map = {
"dashboard": 0,
"plugins": 1,
"widgets": 2,
"settings": 3,
}
if tab_id in tab_map:
self.content_stack.setCurrentIndex(tab_map[tab_id])
# Factory function for creating the main window
def create_classy_dashboard(plugin_manager) -> ClassyDashboardWindow:
"""Create and return the classy dashboard window."""
return ClassyDashboardWindow(plugin_manager)