285 lines
7.8 KiB
Python
285 lines
7.8 KiB
Python
"""
|
|
EU-Utility - Settings Manager
|
|
=============================
|
|
|
|
User preferences and configuration management with type safety.
|
|
|
|
Features:
|
|
- Type-safe setting access
|
|
- Automatic persistence
|
|
- Signal-based change notifications
|
|
- Plugin enablement tracking
|
|
|
|
Quick Start:
|
|
------------
|
|
from core.settings import get_settings
|
|
|
|
settings = get_settings()
|
|
|
|
# Get a setting
|
|
theme = settings.get('overlay_theme', 'dark')
|
|
|
|
# Set a setting
|
|
settings.set('overlay_theme', 'light')
|
|
|
|
# Check if plugin is enabled
|
|
if settings.is_plugin_enabled('my_plugin'):
|
|
# Load plugin
|
|
|
|
# Connect to changes
|
|
settings.setting_changed.connect(on_setting_changed)
|
|
|
|
Configuration File:
|
|
-------------------
|
|
Settings are stored in `data/settings.json` in JSON format.
|
|
The file is automatically created on first run with defaults.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
try:
|
|
from PyQt6.QtCore import QObject, pyqtSignal
|
|
HAS_QT = True
|
|
except ImportError:
|
|
# Fallback for non-Qt environments
|
|
class QObject: # type: ignore
|
|
pass
|
|
|
|
class pyqtSignal: # type: ignore
|
|
def __init__(self, *args: Any) -> None:
|
|
pass
|
|
|
|
def connect(self, slot: Any) -> None:
|
|
pass
|
|
|
|
def emit(self, *args: Any) -> None:
|
|
pass
|
|
|
|
HAS_QT = False
|
|
|
|
|
|
class Settings(QObject):
|
|
"""Application settings manager.
|
|
|
|
Provides type-safe access to user preferences with automatic
|
|
persistence and change notifications.
|
|
|
|
Attributes:
|
|
setting_changed: Qt signal emitted when a setting changes
|
|
"""
|
|
|
|
setting_changed = pyqtSignal(str, object) # key, value
|
|
|
|
# Default settings
|
|
DEFAULTS: Dict[str, Any] = {
|
|
# Overlay
|
|
'overlay_enabled': True,
|
|
'overlay_opacity': 0.9,
|
|
'overlay_theme': 'dark',
|
|
|
|
# Hotkeys
|
|
'hotkey_toggle': 'ctrl+shift+u',
|
|
'hotkey_search': 'ctrl+shift+f',
|
|
'hotkey_calculator': 'ctrl+shift+c',
|
|
'hotkey_music': 'ctrl+shift+m',
|
|
'hotkey_scan': 'ctrl+shift+r',
|
|
'hotkey_skills': 'ctrl+shift+s',
|
|
|
|
# Plugins
|
|
'enabled_plugins': [
|
|
'universal_search',
|
|
'calculator',
|
|
'spotify_controller',
|
|
'nexus_search',
|
|
'game_reader',
|
|
'skill_scanner',
|
|
],
|
|
'disabled_plugins': [],
|
|
|
|
# Dashboard
|
|
'dashboard_widgets': [
|
|
{'type': 'QuickActions', 'row': 0, 'col': 0},
|
|
{'type': 'PEDTracker', 'row': 1, 'col': 0},
|
|
{'type': 'SkillProgress', 'row': 1, 'col': 1},
|
|
],
|
|
|
|
# Overlay widgets
|
|
'overlay_widgets': {
|
|
'spotify': {'enabled': False, 'x': 0, 'y': 100},
|
|
'skillgain': {'enabled': False, 'x': 0, 'y': 200},
|
|
},
|
|
|
|
# Game Reader
|
|
'ocr_engine': 'easyocr',
|
|
'auto_capture': False,
|
|
'capture_region': 'full',
|
|
|
|
# Skill Scanner
|
|
'auto_track_gains': True,
|
|
'skill_alert_threshold': 100,
|
|
|
|
# Appearance
|
|
'icon_size': 24,
|
|
'accent_color': '#4a9eff',
|
|
'show_tooltips': True,
|
|
|
|
# Updates
|
|
'check_updates': True,
|
|
'auto_update_plugins': False,
|
|
|
|
# Data
|
|
'data_retention_days': 30,
|
|
'auto_export': False,
|
|
}
|
|
|
|
def __init__(self, config_file: str = "data/settings.json") -> None:
|
|
"""Initialize settings manager.
|
|
|
|
Args:
|
|
config_file: Path to settings JSON file
|
|
"""
|
|
super().__init__()
|
|
self.config_file = Path(config_file)
|
|
self._settings: Dict[str, Any] = {}
|
|
self._load()
|
|
|
|
def _load(self) -> None:
|
|
"""Load settings from file."""
|
|
self._settings = self.DEFAULTS.copy()
|
|
|
|
if self.config_file.exists():
|
|
try:
|
|
with open(self.config_file, 'r', encoding='utf-8') as f:
|
|
saved = json.load(f)
|
|
self._settings.update(saved)
|
|
except (json.JSONDecodeError, IOError) as e:
|
|
print(f"[Settings] Error loading settings: {e}")
|
|
|
|
def save(self) -> None:
|
|
"""Save settings to file."""
|
|
try:
|
|
self.config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(self.config_file, 'w', encoding='utf-8') as f:
|
|
json.dump(self._settings, f, indent=2)
|
|
except IOError as e:
|
|
print(f"[Settings] Error saving settings: {e}")
|
|
|
|
def get(self, key: str, default: Any = None) -> Any:
|
|
"""Get a setting value.
|
|
|
|
Args:
|
|
key: Setting key
|
|
default: Default value if key not found
|
|
|
|
Returns:
|
|
Setting value or default
|
|
"""
|
|
return self._settings.get(key, default)
|
|
|
|
def set(self, key: str, value: Any) -> None:
|
|
"""Set a setting value.
|
|
|
|
Args:
|
|
key: Setting key
|
|
value: Value to store (must be JSON serializable)
|
|
"""
|
|
old_value = self._settings.get(key)
|
|
self._settings[key] = value
|
|
self.save()
|
|
self.setting_changed.emit(key, value)
|
|
|
|
def reset(self, key: Optional[str] = None) -> None:
|
|
"""Reset setting(s) to default.
|
|
|
|
Args:
|
|
key: Specific key to reset, or None to reset all
|
|
"""
|
|
if key:
|
|
self._settings[key] = self.DEFAULTS.get(key)
|
|
self.save()
|
|
self.setting_changed.emit(key, self._settings[key])
|
|
else:
|
|
self._settings = self.DEFAULTS.copy()
|
|
self.save()
|
|
|
|
def is_plugin_enabled(self, plugin_id: str) -> bool:
|
|
"""Check if a plugin is enabled.
|
|
|
|
Args:
|
|
plugin_id: Unique plugin identifier
|
|
|
|
Returns:
|
|
True if plugin is enabled
|
|
"""
|
|
return plugin_id in self._settings.get('enabled_plugins', [])
|
|
|
|
def enable_plugin(self, plugin_id: str) -> None:
|
|
"""Enable a plugin.
|
|
|
|
Args:
|
|
plugin_id: Unique plugin identifier
|
|
"""
|
|
enabled = self._settings.get('enabled_plugins', [])
|
|
disabled = self._settings.get('disabled_plugins', [])
|
|
|
|
if plugin_id not in enabled:
|
|
enabled = enabled + [plugin_id]
|
|
if plugin_id in disabled:
|
|
disabled.remove(plugin_id)
|
|
|
|
self.set('enabled_plugins', enabled)
|
|
self.set('disabled_plugins', disabled)
|
|
|
|
def disable_plugin(self, plugin_id: str) -> None:
|
|
"""Disable a plugin.
|
|
|
|
Args:
|
|
plugin_id: Unique plugin identifier
|
|
"""
|
|
enabled = self._settings.get('enabled_plugins', [])
|
|
disabled = self._settings.get('disabled_plugins', [])
|
|
|
|
if plugin_id in enabled:
|
|
enabled.remove(plugin_id)
|
|
if plugin_id not in disabled:
|
|
disabled = disabled + [plugin_id]
|
|
|
|
self.set('enabled_plugins', enabled)
|
|
self.set('disabled_plugins', disabled)
|
|
|
|
def all_settings(self) -> Dict[str, Any]:
|
|
"""Get all settings.
|
|
|
|
Returns:
|
|
Dictionary with all settings
|
|
"""
|
|
return self._settings.copy()
|
|
|
|
|
|
# Global settings instance
|
|
_settings_instance: Optional[Settings] = None
|
|
|
|
|
|
def get_settings() -> Settings:
|
|
"""Get global settings instance.
|
|
|
|
Returns:
|
|
The singleton Settings instance
|
|
"""
|
|
global _settings_instance
|
|
if _settings_instance is None:
|
|
_settings_instance = Settings()
|
|
return _settings_instance
|
|
|
|
|
|
def reset_settings() -> None:
|
|
"""Reset settings to defaults."""
|
|
global _settings_instance
|
|
if _settings_instance is not None:
|
|
_settings_instance.reset()
|
|
|
|
|
|
__all__ = ['Settings', 'get_settings', 'reset_settings']
|