DarkMode ist existent yeah
Dieser Commit ist enthalten in:
@ -1,5 +1,7 @@
|
||||
"""
|
||||
Theme Manager - Verwaltet das Erscheinungsbild der Anwendung (nur Light Mode)
|
||||
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
|
||||
@ -8,17 +10,26 @@ 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
|
||||
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:
|
||||
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):
|
||||
"""
|
||||
@ -27,9 +38,12 @@ class ThemeManager:
|
||||
Args:
|
||||
app: Die QApplication-Instanz
|
||||
"""
|
||||
super().__init__()
|
||||
self.app = app
|
||||
self.settings = QSettings("Chimaira", "SocialMediaAccountGenerator")
|
||||
self.current_theme = self.LIGHT_THEME
|
||||
|
||||
# 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__)))
|
||||
@ -38,86 +52,133 @@ class ThemeManager:
|
||||
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)
|
||||
|
||||
# Lade QSS-Dateien für Themes
|
||||
# Generate QSS from theme configuration
|
||||
self.qss_generator = QSSGenerator()
|
||||
self.theme_stylesheets = {
|
||||
self.LIGHT_THEME: self._load_stylesheet("light.qss")
|
||||
self.LIGHT_THEME: self.qss_generator.generate(self.LIGHT_THEME),
|
||||
self.DARK_THEME: self.qss_generator.generate(self.DARK_THEME)
|
||||
}
|
||||
|
||||
# Wende das Light Theme an
|
||||
self.apply_theme(self.LIGHT_THEME)
|
||||
# Apply saved theme
|
||||
self.apply_theme(self.current_theme)
|
||||
|
||||
logger.info(f"ThemeManager initialisiert mit Theme: {self.current_theme}")
|
||||
logger.info(f"ThemeManager initialized with theme: {self.current_theme}")
|
||||
|
||||
def _load_stylesheet(self, filename: str) -> str:
|
||||
"""Lädt ein QSS-Stylesheet aus einer Datei."""
|
||||
try:
|
||||
stylesheet_path = os.path.join(self.base_dir, "resources", "themes", filename)
|
||||
if os.path.exists(stylesheet_path):
|
||||
with open(stylesheet_path, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
else:
|
||||
logger.warning(f"Stylesheet-Datei nicht gefunden: {stylesheet_path}")
|
||||
# Erzeuge eine leere Stylesheet-Datei, wenn sie nicht existiert
|
||||
with open(stylesheet_path, 'w', encoding='utf-8') as f:
|
||||
f.write("/* Auto-generated empty stylesheet */\n")
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden des Stylesheets {filename}: {e}")
|
||||
return ""
|
||||
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 Light Theme auf die Anwendung an.
|
||||
Wendet das angegebene Theme auf die Anwendung an.
|
||||
|
||||
Args:
|
||||
theme_name: Wird ignoriert, immer Light Theme verwendet
|
||||
theme_name: Name des Themes ("light" oder "dark")
|
||||
|
||||
Returns:
|
||||
bool: True, wenn das Theme erfolgreich angewendet wurde, sonst False
|
||||
"""
|
||||
try:
|
||||
# Palette für das Light Theme erstellen
|
||||
# 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()
|
||||
|
||||
# Light Theme Palette
|
||||
palette.setColor(QPalette.Window, QColor(240, 240, 240))
|
||||
palette.setColor(QPalette.WindowText, Qt.black)
|
||||
palette.setColor(QPalette.Base, Qt.white)
|
||||
palette.setColor(QPalette.AlternateBase, QColor(245, 245, 245))
|
||||
palette.setColor(QPalette.ToolTipBase, Qt.white)
|
||||
palette.setColor(QPalette.ToolTipText, Qt.black)
|
||||
palette.setColor(QPalette.Text, Qt.black)
|
||||
palette.setColor(QPalette.Button, QColor(240, 240, 240))
|
||||
palette.setColor(QPalette.ButtonText, Qt.black)
|
||||
palette.setColor(QPalette.BrightText, Qt.red)
|
||||
palette.setColor(QPalette.Link, QColor(0, 0, 255))
|
||||
palette.setColor(QPalette.Highlight, QColor(42, 130, 218))
|
||||
palette.setColor(QPalette.HighlightedText, Qt.white)
|
||||
# Get colors from theme configuration
|
||||
theme = ThemeConfig.get_theme(theme_name)
|
||||
|
||||
# Palette auf die Anwendung anwenden
|
||||
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)
|
||||
|
||||
# Stylesheet anwenden
|
||||
self.app.setStyleSheet(self.theme_stylesheets.get(self.LIGHT_THEME, ""))
|
||||
# Apply stylesheet
|
||||
stylesheet = self.theme_stylesheets.get(theme_name, "")
|
||||
self.app.setStyleSheet(stylesheet)
|
||||
|
||||
# Aktuelles Theme speichern
|
||||
self.current_theme = self.LIGHT_THEME
|
||||
self.settings.setValue("theme", self.LIGHT_THEME)
|
||||
# Save current theme
|
||||
self.current_theme = theme_name
|
||||
self.settings.setValue("theme", theme_name)
|
||||
|
||||
logger.info(f"Theme '{self.LIGHT_THEME}' erfolgreich angewendet")
|
||||
# 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"Fehler beim Anwenden des Themes '{self.LIGHT_THEME}': {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.LIGHT_THEME
|
||||
return self.current_theme
|
||||
|
||||
def get_icon_path(self, icon_name: str) -> str:
|
||||
"""
|
||||
Gibt den Pfad zum Icon zurück.
|
||||
Gibt den Pfad zum Icon zurück (theme-aware).
|
||||
|
||||
Args:
|
||||
icon_name: Name des Icons (ohne Dateierweiterung)
|
||||
@ -126,8 +187,38 @@ class ThemeManager:
|
||||
str: Pfad zum Icon
|
||||
"""
|
||||
# Social Media Icons bleiben unverändert (immer farbig)
|
||||
if icon_name in ["instagram", "facebook", "twitter", "tiktok", "vk"]:
|
||||
if icon_name in ["instagram", "facebook", "twitter", "tiktok", "vk", "gmail", "ok"]:
|
||||
return os.path.join(self.base_dir, "resources", "icons", f"{icon_name}.svg")
|
||||
|
||||
# Für andere Icons, die möglicherweise Theme-spezifisch sind
|
||||
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, '')
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren