DarkMode ist existent yeah

Dieser Commit ist enthalten in:
Claude Project Manager
2025-08-10 17:46:30 +02:00
Ursprung 61cd1216d0
Commit 2644c4e111
24 geänderte Dateien mit 2930 neuen und 426 gelöschten Zeilen

Datei anzeigen

@ -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, '')