224 Zeilen
9.0 KiB
Python
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, '') |