327 lines
11 KiB
Python
327 lines
11 KiB
Python
"""
|
|
EU-Utility - Dashboard Plugin with Customizable Widgets
|
|
|
|
Customizable start page with avatar statistics.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from datetime import datetime, timedelta
|
|
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QGridLayout, QFrame, QScrollArea,
|
|
QSizePolicy, QCheckBox, QDialog, QListWidget,
|
|
QListWidgetItem, QDialogButtonBox
|
|
)
|
|
from PyQt6.QtCore import Qt, QTimer
|
|
from PyQt6.QtGui import QColor, QFont
|
|
|
|
from core.eu_styles import EU_COLORS
|
|
from plugins.base_plugin import BasePlugin
|
|
|
|
|
|
class DashboardPlugin(BasePlugin):
|
|
"""Customizable dashboard with avatar statistics."""
|
|
|
|
name = "Dashboard"
|
|
version = "2.0.0"
|
|
author = "ImpulsiveFPS"
|
|
description = "Customizable start page with avatar stats"
|
|
hotkey = "ctrl+shift+home"
|
|
|
|
# Available widgets
|
|
AVAILABLE_WIDGETS = {
|
|
'ped_balance': {'name': 'PED Balance', 'icon': 'dollar-sign', 'default': True},
|
|
'skill_count': {'name': 'Skills Tracked', 'icon': 'trending-up', 'default': True},
|
|
'inventory_items': {'name': 'Inventory Items', 'icon': 'archive', 'default': True},
|
|
'current_dpp': {'name': 'Current DPP', 'icon': 'crosshair', 'default': True},
|
|
'total_gains_today': {'name': "Today's Skill Gains", 'icon': 'zap', 'default': True},
|
|
'professions_count': {'name': 'Professions', 'icon': 'award', 'default': False},
|
|
'missions_active': {'name': 'Active Missions', 'icon': 'map', 'default': False},
|
|
'codex_progress': {'name': 'Codex Progress', 'icon': 'book', 'default': False},
|
|
'globals_hofs': {'name': 'Globals/HOFs', 'icon': 'package', 'default': False},
|
|
'play_time': {'name': 'Session Time', 'icon': 'clock', 'default': False},
|
|
}
|
|
|
|
def initialize(self):
|
|
"""Setup dashboard."""
|
|
self.config_file = Path("data/dashboard_config.json")
|
|
self.config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
self.enabled_widgets = []
|
|
self.widget_data = {}
|
|
|
|
self._load_config()
|
|
self._load_data()
|
|
|
|
# Auto-refresh timer
|
|
self.refresh_timer = QTimer()
|
|
self.refresh_timer.timeout.connect(self._refresh_data)
|
|
self.refresh_timer.start(5000) # Refresh every 5 seconds
|
|
|
|
def _load_config(self):
|
|
"""Load widget configuration."""
|
|
if self.config_file.exists():
|
|
try:
|
|
with open(self.config_file, 'r') as f:
|
|
config = json.load(f)
|
|
self.enabled_widgets = config.get('enabled', [])
|
|
except:
|
|
pass
|
|
|
|
# Default: enable default widgets
|
|
if not self.enabled_widgets:
|
|
self.enabled_widgets = [
|
|
k for k, v in self.AVAILABLE_WIDGETS.items() if v['default']
|
|
]
|
|
|
|
def _save_config(self):
|
|
"""Save widget configuration."""
|
|
with open(self.config_file, 'w') as f:
|
|
json.dump({'enabled': self.enabled_widgets}, f)
|
|
|
|
def _load_data(self):
|
|
"""Load data from other plugins."""
|
|
# Try to get data from other plugin files
|
|
data_dir = Path("data")
|
|
|
|
# PED from inventory
|
|
inv_file = data_dir / "inventory.json"
|
|
if inv_file.exists():
|
|
try:
|
|
with open(inv_file, 'r') as f:
|
|
data = json.load(f)
|
|
items = data.get('items', [])
|
|
total_tt = sum(item.get('tt', 0) for item in items)
|
|
self.widget_data['ped_balance'] = total_tt
|
|
except:
|
|
self.widget_data['ped_balance'] = 0
|
|
|
|
# Skills
|
|
skills_file = data_dir / "skill_tracker.json"
|
|
if skills_file.exists():
|
|
try:
|
|
with open(skills_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.widget_data['skill_count'] = len(data.get('skills', {}))
|
|
self.widget_data['total_gains_today'] = len([
|
|
g for g in data.get('gains', [])
|
|
if datetime.fromisoformat(g['time']).date() == datetime.now().date()
|
|
])
|
|
except:
|
|
self.widget_data['skill_count'] = 0
|
|
self.widget_data['total_gains_today'] = 0
|
|
|
|
# Inventory count
|
|
if inv_file.exists():
|
|
try:
|
|
with open(inv_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.widget_data['inventory_items'] = len(data.get('items', []))
|
|
except:
|
|
self.widget_data['inventory_items'] = 0
|
|
|
|
# Professions
|
|
prof_file = data_dir / "professions.json"
|
|
if prof_file.exists():
|
|
try:
|
|
with open(prof_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.widget_data['professions_count'] = len(data.get('professions', {}))
|
|
except:
|
|
self.widget_data['professions_count'] = 0
|
|
|
|
def _refresh_data(self):
|
|
"""Refresh widget data."""
|
|
self._load_data()
|
|
if hasattr(self, 'widgets_container'):
|
|
self._update_widgets()
|
|
|
|
def get_ui(self):
|
|
"""Create dashboard UI."""
|
|
widget = QWidget()
|
|
layout = QVBoxLayout(widget)
|
|
layout.setSpacing(15)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
# Header with customize button
|
|
header = QHBoxLayout()
|
|
|
|
title = QLabel("Dashboard")
|
|
title.setStyleSheet("font-size: 20px; font-weight: bold; color: white;")
|
|
header.addWidget(title)
|
|
|
|
header.addStretch()
|
|
|
|
customize_btn = QPushButton("Customize")
|
|
customize_btn.setStyleSheet(f"""
|
|
QPushButton {{
|
|
background-color: {EU_COLORS['bg_secondary']};
|
|
color: {EU_COLORS['text_secondary']};
|
|
border: 1px solid {EU_COLORS['border_subtle']};
|
|
border-radius: 4px;
|
|
padding: 8px 16px;
|
|
}}
|
|
QPushButton:hover {{
|
|
background-color: {EU_COLORS['bg_hover']};
|
|
border-color: {EU_COLORS['accent_orange']};
|
|
}}
|
|
""")
|
|
customize_btn.clicked.connect(self._show_customize_dialog)
|
|
header.addWidget(customize_btn)
|
|
|
|
layout.addLayout(header)
|
|
|
|
# Scroll area for widgets
|
|
scroll = QScrollArea()
|
|
scroll.setWidgetResizable(True)
|
|
scroll.setFrameShape(QFrame.Shape.NoFrame)
|
|
scroll.setStyleSheet("background: transparent; border: none;")
|
|
|
|
self.widgets_container = QWidget()
|
|
self.widgets_layout = QGridLayout(self.widgets_container)
|
|
self.widgets_layout.setSpacing(15)
|
|
self.widgets_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
self._update_widgets()
|
|
|
|
scroll.setWidget(self.widgets_container)
|
|
layout.addWidget(scroll)
|
|
|
|
return widget
|
|
|
|
def _update_widgets(self):
|
|
"""Update widget display."""
|
|
# Clear existing
|
|
while self.widgets_layout.count():
|
|
item = self.widgets_layout.takeAt(0)
|
|
if item.widget():
|
|
item.widget().deleteLater()
|
|
|
|
# Add enabled widgets
|
|
col = 0
|
|
row = 0
|
|
for widget_id in self.enabled_widgets:
|
|
if widget_id in self.AVAILABLE_WIDGETS:
|
|
widget_info = self.AVAILABLE_WIDGETS[widget_id]
|
|
card = self._create_widget_card(
|
|
widget_id,
|
|
widget_info['name'],
|
|
widget_info['icon']
|
|
)
|
|
self.widgets_layout.addWidget(card, row, col)
|
|
|
|
col += 1
|
|
if col >= 2: # 2 columns
|
|
col = 0
|
|
row += 1
|
|
|
|
def _create_widget_card(self, widget_id, name, icon_name):
|
|
"""Create a stat widget card."""
|
|
card = QFrame()
|
|
card.setStyleSheet(f"""
|
|
QFrame {{
|
|
background-color: {EU_COLORS['bg_secondary']};
|
|
border: 1px solid {EU_COLORS['border_subtle']};
|
|
border-radius: 8px;
|
|
}}
|
|
""")
|
|
|
|
layout = QVBoxLayout(card)
|
|
layout.setContentsMargins(15, 15, 15, 15)
|
|
layout.setSpacing(8)
|
|
|
|
# Title
|
|
title = QLabel(name)
|
|
title.setStyleSheet(f"color: {EU_COLORS['text_muted']}; font-size: 11px;")
|
|
layout.addWidget(title)
|
|
|
|
# Value
|
|
value = self.widget_data.get(widget_id, 0)
|
|
|
|
if widget_id == 'ped_balance':
|
|
value_text = f"{value:.2f} PED"
|
|
elif widget_id == 'play_time':
|
|
value_text = "2h 34m" # Placeholder
|
|
elif widget_id == 'current_dpp':
|
|
value_text = "3.45"
|
|
else:
|
|
value_text = str(value)
|
|
|
|
value_label = QLabel(value_text)
|
|
value_label.setStyleSheet(f"""
|
|
color: {EU_COLORS['accent_orange']};
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
""")
|
|
layout.addWidget(value_label)
|
|
|
|
layout.addStretch()
|
|
return card
|
|
|
|
def _show_customize_dialog(self):
|
|
"""Show widget customization dialog."""
|
|
dialog = QDialog()
|
|
dialog.setWindowTitle("Customize Dashboard")
|
|
dialog.setStyleSheet(f"""
|
|
QDialog {{
|
|
background-color: {EU_COLORS['bg_dark']};
|
|
color: white;
|
|
}}
|
|
QLabel {{
|
|
color: white;
|
|
}}
|
|
""")
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
|
|
# Instructions
|
|
info = QLabel("Check widgets to display on dashboard:")
|
|
info.setStyleSheet(f"color: {EU_COLORS['text_secondary']};")
|
|
layout.addWidget(info)
|
|
|
|
# Widget list
|
|
list_widget = QListWidget()
|
|
list_widget.setStyleSheet(f"""
|
|
QListWidget {{
|
|
background-color: {EU_COLORS['bg_secondary']};
|
|
color: white;
|
|
border: 1px solid {EU_COLORS['border_subtle']};
|
|
}}
|
|
QListWidget::item {{
|
|
padding: 10px;
|
|
}}
|
|
""")
|
|
|
|
for widget_id, widget_info in self.AVAILABLE_WIDGETS.items():
|
|
item = QListWidgetItem(widget_info['name'])
|
|
item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
|
|
item.setCheckState(
|
|
Qt.CheckState.Checked if widget_id in self.enabled_widgets
|
|
else Qt.CheckState.Unchecked
|
|
)
|
|
item.setData(Qt.ItemDataRole.UserRole, widget_id)
|
|
list_widget.addItem(item)
|
|
|
|
layout.addWidget(list_widget)
|
|
|
|
# Buttons
|
|
buttons = QDialogButtonBox(
|
|
QDialogButtonBox.StandardButton.Save | QDialogButtonBox.StandardButton.Cancel
|
|
)
|
|
buttons.accepted.connect(dialog.accept)
|
|
buttons.rejected.connect(dialog.reject)
|
|
layout.addWidget(buttons)
|
|
|
|
if dialog.exec() == QDialog.DialogCode.Accepted:
|
|
# Save selection
|
|
self.enabled_widgets = []
|
|
for i in range(list_widget.count()):
|
|
item = list_widget.item(i)
|
|
if item.checkState() == Qt.CheckState.Checked:
|
|
self.enabled_widgets.append(item.data(Qt.ItemDataRole.UserRole))
|
|
|
|
self._save_config()
|
|
self._update_widgets()
|