321 lines
10 KiB
Python
321 lines
10 KiB
Python
"""
|
|
Application settings and configuration management.
|
|
|
|
This module handles application-wide settings, preferences, and configuration
|
|
data. It provides a centralized location for managing user preferences,
|
|
default values, and application state.
|
|
|
|
Main Components:
|
|
- Settings class for configuration management
|
|
- Default configuration values
|
|
- Settings persistence and loading
|
|
- Configuration validation
|
|
|
|
Usage:
|
|
from cluster4npu_ui.config.settings import Settings
|
|
|
|
settings = Settings()
|
|
recent_files = settings.get_recent_files()
|
|
settings.add_recent_file('/path/to/pipeline.mflow')
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
from typing import Dict, Any, List, Optional
|
|
from pathlib import Path
|
|
|
|
|
|
class Settings:
|
|
"""
|
|
Application settings and configuration management.
|
|
|
|
Handles loading, saving, and managing application settings including
|
|
user preferences, recent files, and default configurations.
|
|
"""
|
|
|
|
def __init__(self, config_file: Optional[str] = None):
|
|
"""
|
|
Initialize settings manager.
|
|
|
|
Args:
|
|
config_file: Optional path to configuration file
|
|
"""
|
|
self.config_file = config_file or self._get_default_config_path()
|
|
self._settings = self._load_default_settings()
|
|
self.load()
|
|
|
|
def _get_default_config_path(self) -> str:
|
|
"""Get the default configuration file path."""
|
|
home_dir = Path.home()
|
|
config_dir = home_dir / '.cluster4npu'
|
|
config_dir.mkdir(exist_ok=True)
|
|
return str(config_dir / 'settings.json')
|
|
|
|
def _load_default_settings(self) -> Dict[str, Any]:
|
|
"""Load default application settings."""
|
|
return {
|
|
'general': {
|
|
'auto_save': True,
|
|
'auto_save_interval': 300, # seconds
|
|
'check_for_updates': True,
|
|
'theme': 'harmonious_dark',
|
|
'language': 'en'
|
|
},
|
|
'recent_files': [],
|
|
'window': {
|
|
'main_window_geometry': None,
|
|
'main_window_state': None,
|
|
'splitter_sizes': None,
|
|
'recent_window_size': [1200, 800]
|
|
},
|
|
'pipeline': {
|
|
'default_project_location': str(Path.home() / 'Documents' / 'Cluster4NPU'),
|
|
'auto_layout': True,
|
|
'show_grid': True,
|
|
'snap_to_grid': False,
|
|
'grid_size': 20,
|
|
'auto_connect': True,
|
|
'validate_on_save': True
|
|
},
|
|
'performance': {
|
|
'max_undo_steps': 50,
|
|
'render_quality': 'high',
|
|
'enable_animations': True,
|
|
'cache_size_mb': 100
|
|
},
|
|
'hardware': {
|
|
'auto_detect_dongles': True,
|
|
'preferred_dongle_series': '720',
|
|
'max_dongles_per_stage': 4,
|
|
'power_management': 'balanced'
|
|
},
|
|
'export': {
|
|
'default_format': 'JSON',
|
|
'include_metadata': True,
|
|
'compress_exports': False,
|
|
'export_location': str(Path.home() / 'Downloads')
|
|
},
|
|
'debugging': {
|
|
'log_level': 'INFO',
|
|
'enable_profiling': False,
|
|
'save_debug_logs': False,
|
|
'max_log_files': 10
|
|
}
|
|
}
|
|
|
|
def load(self) -> bool:
|
|
"""
|
|
Load settings from file.
|
|
|
|
Returns:
|
|
True if settings were loaded successfully, False otherwise
|
|
"""
|
|
try:
|
|
if os.path.exists(self.config_file):
|
|
with open(self.config_file, 'r', encoding='utf-8') as f:
|
|
saved_settings = json.load(f)
|
|
self._merge_settings(saved_settings)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error loading settings: {e}")
|
|
return False
|
|
|
|
def save(self) -> bool:
|
|
"""
|
|
Save current settings to file.
|
|
|
|
Returns:
|
|
True if settings were saved successfully, False otherwise
|
|
"""
|
|
try:
|
|
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
|
with open(self.config_file, 'w', encoding='utf-8') as f:
|
|
json.dump(self._settings, f, indent=2, ensure_ascii=False)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error saving settings: {e}")
|
|
return False
|
|
|
|
def _merge_settings(self, saved_settings: Dict[str, Any]):
|
|
"""Merge saved settings with defaults."""
|
|
def merge_dict(default: dict, saved: dict) -> dict:
|
|
result = default.copy()
|
|
for key, value in saved.items():
|
|
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
result[key] = merge_dict(result[key], value)
|
|
else:
|
|
result[key] = value
|
|
return result
|
|
|
|
self._settings = merge_dict(self._settings, saved_settings)
|
|
|
|
def get(self, key: str, default: Any = None) -> Any:
|
|
"""
|
|
Get a setting value using dot notation.
|
|
|
|
Args:
|
|
key: Setting key (e.g., 'general.auto_save')
|
|
default: Default value if key not found
|
|
|
|
Returns:
|
|
Setting value or default
|
|
"""
|
|
keys = key.split('.')
|
|
value = self._settings
|
|
|
|
try:
|
|
for k in keys:
|
|
value = value[k]
|
|
return value
|
|
except (KeyError, TypeError):
|
|
return default
|
|
|
|
def set(self, key: str, value: Any):
|
|
"""
|
|
Set a setting value using dot notation.
|
|
|
|
Args:
|
|
key: Setting key (e.g., 'general.auto_save')
|
|
value: Value to set
|
|
"""
|
|
keys = key.split('.')
|
|
setting = self._settings
|
|
|
|
# Navigate to the parent dictionary
|
|
for k in keys[:-1]:
|
|
if k not in setting:
|
|
setting[k] = {}
|
|
setting = setting[k]
|
|
|
|
# Set the final value
|
|
setting[keys[-1]] = value
|
|
|
|
def get_recent_files(self) -> List[str]:
|
|
"""Get list of recent files."""
|
|
return self.get('recent_files', [])
|
|
|
|
def add_recent_file(self, file_path: str, max_files: int = 10):
|
|
"""
|
|
Add a file to recent files list.
|
|
|
|
Args:
|
|
file_path: Path to the file
|
|
max_files: Maximum number of recent files to keep
|
|
"""
|
|
recent_files = self.get_recent_files()
|
|
|
|
# Remove if already exists
|
|
if file_path in recent_files:
|
|
recent_files.remove(file_path)
|
|
|
|
# Add to beginning
|
|
recent_files.insert(0, file_path)
|
|
|
|
# Limit list size
|
|
recent_files = recent_files[:max_files]
|
|
|
|
self.set('recent_files', recent_files)
|
|
self.save()
|
|
|
|
def remove_recent_file(self, file_path: str):
|
|
"""Remove a file from recent files list."""
|
|
recent_files = self.get_recent_files()
|
|
if file_path in recent_files:
|
|
recent_files.remove(file_path)
|
|
self.set('recent_files', recent_files)
|
|
self.save()
|
|
|
|
def clear_recent_files(self):
|
|
"""Clear all recent files."""
|
|
self.set('recent_files', [])
|
|
self.save()
|
|
|
|
def get_default_project_location(self) -> str:
|
|
"""Get default project location."""
|
|
return self.get('pipeline.default_project_location', str(Path.home() / 'Documents' / 'Cluster4NPU'))
|
|
|
|
def set_window_geometry(self, geometry: bytes):
|
|
"""Save window geometry."""
|
|
# Convert bytes to base64 string for JSON serialization
|
|
import base64
|
|
geometry_str = base64.b64encode(geometry).decode('utf-8')
|
|
self.set('window.main_window_geometry', geometry_str)
|
|
self.save()
|
|
|
|
def get_window_geometry(self) -> Optional[bytes]:
|
|
"""Get saved window geometry."""
|
|
geometry_str = self.get('window.main_window_geometry')
|
|
if geometry_str:
|
|
import base64
|
|
return base64.b64decode(geometry_str.encode('utf-8'))
|
|
return None
|
|
|
|
def set_window_state(self, state: bytes):
|
|
"""Save window state."""
|
|
import base64
|
|
state_str = base64.b64encode(state).decode('utf-8')
|
|
self.set('window.main_window_state', state_str)
|
|
self.save()
|
|
|
|
def get_window_state(self) -> Optional[bytes]:
|
|
"""Get saved window state."""
|
|
state_str = self.get('window.main_window_state')
|
|
if state_str:
|
|
import base64
|
|
return base64.b64decode(state_str.encode('utf-8'))
|
|
return None
|
|
|
|
def reset_to_defaults(self):
|
|
"""Reset all settings to default values."""
|
|
self._settings = self._load_default_settings()
|
|
self.save()
|
|
|
|
def export_settings(self, file_path: str) -> bool:
|
|
"""
|
|
Export settings to a file.
|
|
|
|
Args:
|
|
file_path: Path to export file
|
|
|
|
Returns:
|
|
True if export was successful, False otherwise
|
|
"""
|
|
try:
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
json.dump(self._settings, f, indent=2, ensure_ascii=False)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error exporting settings: {e}")
|
|
return False
|
|
|
|
def import_settings(self, file_path: str) -> bool:
|
|
"""
|
|
Import settings from a file.
|
|
|
|
Args:
|
|
file_path: Path to import file
|
|
|
|
Returns:
|
|
True if import was successful, False otherwise
|
|
"""
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
imported_settings = json.load(f)
|
|
self._merge_settings(imported_settings)
|
|
self.save()
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error importing settings: {e}")
|
|
return False
|
|
|
|
|
|
# Global settings instance
|
|
_settings_instance = None
|
|
|
|
|
|
def get_settings() -> Settings:
|
|
"""Get the global settings instance."""
|
|
global _settings_instance
|
|
if _settings_instance is None:
|
|
_settings_instance = Settings()
|
|
return _settings_instance |