Files
AccountForger-neuerUpload/utils/theme_manager.py
Claude Project Manager 2644c4e111 DarkMode ist existent yeah
2025-08-10 17:46:30 +02:00

224 Zeilen
9.0 KiB
Python

"""
Theme Manager - Verwaltet das Erscheinungsbild der Anwendung (Light & Dark Mode)
Enhanced with Dark Mode support based on Corporate Design Guidelines
Now using centralized theme configuration
"""
import os
import json
import logging
from typing import Dict, Any, Optional
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtCore import Qt, QSettings, pyqtSignal, QObject
# Import new theme system
from themes.theme_config import ThemeConfig
from themes.qss_generator import QSSGenerator
logger = logging.getLogger("theme_manager")
class ThemeManager(QObject):
"""
Verwaltet das Erscheinungsbild der Anwendung.
Supports Light and Dark themes with smooth transitions.
"""
# Signal emitted when theme changes
theme_changed = pyqtSignal(str)
# Themennamen
LIGHT_THEME = "light"
DARK_THEME = "dark"
def __init__(self, app: QApplication):
"""
Initialisiert den ThemeManager.
Args:
app: Die QApplication-Instanz
"""
super().__init__()
self.app = app
self.settings = QSettings("Chimaira", "SocialMediaAccountGenerator")
# Load saved theme preference or default to light
self.current_theme = self.settings.value("theme", self.LIGHT_THEME)
# Basisverzeichnis ermitteln
self.base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Stelle sicher, dass die Verzeichnisse existieren
os.makedirs(os.path.join(self.base_dir, "resources", "themes"), exist_ok=True)
os.makedirs(os.path.join(self.base_dir, "resources", "icons"), exist_ok=True)
# Generate QSS from theme configuration
self.qss_generator = QSSGenerator()
self.theme_stylesheets = {
self.LIGHT_THEME: self.qss_generator.generate(self.LIGHT_THEME),
self.DARK_THEME: self.qss_generator.generate(self.DARK_THEME)
}
# Apply saved theme
self.apply_theme(self.current_theme)
logger.info(f"ThemeManager initialized with theme: {self.current_theme}")
def regenerate_stylesheets(self):
"""Regenerate stylesheets from theme configuration"""
self.theme_stylesheets = {
self.LIGHT_THEME: self.qss_generator.generate(self.LIGHT_THEME),
self.DARK_THEME: self.qss_generator.generate(self.DARK_THEME)
}
def get_color(self, color_key: str) -> str:
"""Get a color from the current theme"""
return ThemeConfig.get_color(self.current_theme, color_key)
def apply_theme(self, theme_name: str) -> bool:
"""
Wendet das angegebene Theme auf die Anwendung an.
Args:
theme_name: Name des Themes ("light" oder "dark")
Returns:
bool: True, wenn das Theme erfolgreich angewendet wurde, sonst False
"""
try:
# Validate theme name
if theme_name not in [self.LIGHT_THEME, self.DARK_THEME]:
logger.warning(f"Unknown theme '{theme_name}', falling back to light theme")
theme_name = self.LIGHT_THEME
# Create palette based on theme
palette = QPalette()
# Get colors from theme configuration
theme = ThemeConfig.get_theme(theme_name)
if theme_name == self.DARK_THEME:
# Dark Theme Palette from configuration
palette.setColor(QPalette.Window, QColor(theme['bg_primary']))
palette.setColor(QPalette.WindowText, QColor(theme['text_primary']))
palette.setColor(QPalette.Base, QColor(theme['bg_tertiary']))
palette.setColor(QPalette.AlternateBase, QColor(theme['bg_secondary']))
palette.setColor(QPalette.ToolTipBase, QColor(theme['bg_tertiary']))
palette.setColor(QPalette.ToolTipText, QColor(theme['text_primary']))
palette.setColor(QPalette.Text, QColor(theme['text_primary']))
palette.setColor(QPalette.Button, QColor(theme['bg_tertiary']))
palette.setColor(QPalette.ButtonText, QColor(theme['text_primary']))
palette.setColor(QPalette.BrightText, QColor(theme['accent']))
palette.setColor(QPalette.Link, QColor(theme['accent']))
palette.setColor(QPalette.Highlight, QColor(theme['accent']))
palette.setColor(QPalette.HighlightedText, QColor(theme['btn_primary_text']))
else:
# Light Theme Palette from configuration
palette.setColor(QPalette.Window, QColor(theme['bg_primary']))
palette.setColor(QPalette.WindowText, QColor(theme['text_primary']))
palette.setColor(QPalette.Base, QColor(theme['bg_primary']))
palette.setColor(QPalette.AlternateBase, QColor(theme['bg_secondary']))
palette.setColor(QPalette.ToolTipBase, QColor(theme['bg_primary']))
palette.setColor(QPalette.ToolTipText, QColor(theme['text_primary']))
palette.setColor(QPalette.Text, QColor(theme['text_primary']))
palette.setColor(QPalette.Button, QColor(theme['bg_secondary']))
palette.setColor(QPalette.ButtonText, QColor(theme['text_primary']))
palette.setColor(QPalette.BrightText, QColor(theme['error']))
palette.setColor(QPalette.Link, QColor(theme['accent']))
palette.setColor(QPalette.Highlight, QColor(theme['accent']))
palette.setColor(QPalette.HighlightedText, QColor(theme['btn_primary_text']))
# Apply palette to application
self.app.setPalette(palette)
# Apply stylesheet
stylesheet = self.theme_stylesheets.get(theme_name, "")
self.app.setStyleSheet(stylesheet)
# Save current theme
self.current_theme = theme_name
self.settings.setValue("theme", theme_name)
# Update logo if main window is available
self._update_logo(theme_name)
# Emit signal for theme change
self.theme_changed.emit(theme_name)
logger.info(f"Theme '{theme_name}' successfully applied")
return True
except Exception as e:
logger.error(f"Error applying theme '{theme_name}': {e}")
return False
def toggle_theme(self) -> str:
"""
Toggles between Light and Dark theme.
Returns:
str: The name of the newly applied theme
"""
new_theme = self.DARK_THEME if self.current_theme == self.LIGHT_THEME else self.LIGHT_THEME
self.apply_theme(new_theme)
return new_theme
def is_dark_mode(self) -> bool:
"""
Check if dark mode is currently active.
Returns:
bool: True if dark mode is active, False otherwise
"""
return self.current_theme == self.DARK_THEME
def get_current_theme(self) -> str:
"""Gibt den Namen des aktuellen Themes zurück."""
return self.current_theme
def get_icon_path(self, icon_name: str) -> str:
"""
Gibt den Pfad zum Icon zurück (theme-aware).
Args:
icon_name: Name des Icons (ohne Dateierweiterung)
Returns:
str: Pfad zum Icon
"""
# Social Media Icons bleiben unverändert (immer farbig)
if icon_name in ["instagram", "facebook", "twitter", "tiktok", "vk", "gmail", "ok"]:
return os.path.join(self.base_dir, "resources", "icons", f"{icon_name}.svg")
# Logo is theme-specific
if icon_name == "intelsight-logo":
theme = ThemeConfig.get_theme(self.current_theme)
logo_name = theme.get('logo_path', 'intelsight-logo.svg').replace('.svg', '')
return os.path.join(self.base_dir, "resources", "icons", f"{logo_name}.svg")
# For other icons
return os.path.join(self.base_dir, "resources", "icons", f"{icon_name}.svg")
def _update_logo(self, theme_name: str):
"""
Updates the logo based on the current theme.
Args:
theme_name: Name of the theme ("light" or "dark")
"""
# Skip this method - logo will be updated directly in MainWindow._on_theme_toggled
# This avoids circular import issues
logger.debug(f"_update_logo called for theme: {theme_name} (delegating to MainWindow)")
def get_color(self, color_key: str) -> str:
"""
Get a color from the current theme.
Args:
color_key: Key of the color in theme configuration
Returns:
Color value as hex string
"""
theme = ThemeConfig.get_theme(self.current_theme)
return theme.get(color_key, '')