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

@ -14,6 +14,12 @@ class AboutDialog(QDialog):
# Remove the standard "?" help button that appears on some platforms
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
self.language_manager = language_manager
self.theme_manager = None
# Try to get theme manager from parent (MainWindow)
if parent and hasattr(parent, 'theme_manager'):
self.theme_manager = parent.theme_manager
self._setup_ui()
if self.language_manager:
self.language_manager.language_changed.connect(self.update_texts)
@ -21,49 +27,54 @@ class AboutDialog(QDialog):
def _setup_ui(self):
self.setWindowTitle("About")
# Dialog-Größe festlegen für bessere Zentrierung
self.setMinimumWidth(500)
self.setMinimumHeight(400)
# Dialog-Größe festlegen
self.setFixedSize(550, 450) # Fixed size for consistent appearance
layout = QVBoxLayout(self)
layout.setContentsMargins(30, 30, 30, 30)
layout.setSpacing(20)
layout.setAlignment(Qt.AlignCenter) # Layout auch zentrieren
layout.setContentsMargins(20, 20, 40, 40) # Less margin on top/left for logo
layout.setSpacing(25)
# Add logo
# Add logo in top-left corner
logo_label = QLabel()
logo_label.setAlignment(Qt.AlignCenter)
logo_label.setAlignment(Qt.AlignLeft) # Align left instead of center
# Get the logo path
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
logo_path = os.path.join(parent_dir, "resources", "icons", "intelsight-logo.svg")
# Get the theme-aware logo path
if self.theme_manager:
# Use theme manager to get correct logo based on current theme
logo_path = self.theme_manager.get_icon_path("intelsight-logo")
else:
# Fallback to light logo if no theme manager
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
logo_path = os.path.join(parent_dir, "resources", "icons", "intelsight-logo.svg")
if os.path.exists(logo_path):
# Load logo and display it at a larger size
# Load logo and display it at a smaller size for corner placement
logo_pixmap = QPixmap(logo_path)
# Scale the logo to a reasonable size while maintaining aspect ratio
# Scale the logo smaller for top-left corner
scaled_pixmap = logo_pixmap.scaled(
300, 120, # Etwas kleiner für bessere Proportionen
200, 60, # Smaller size for corner placement
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
logo_label.setPixmap(scaled_pixmap)
# Feste Größe für das Label setzen, um Zentrierung zu gewährleisten
logo_label.setFixedSize(scaled_pixmap.size())
else:
# Fallback if logo not found
logo_label.setText("IntelSight")
logo_label.setStyleSheet("font-size: 24px; font-weight: bold;")
logo_label.setStyleSheet("font-size: 18px; font-weight: bold;")
# Logo mit Alignment hinzufügen
layout.addWidget(logo_label, 0, Qt.AlignCenter)
# Logo in top-left corner
layout.addWidget(logo_label, 0, Qt.AlignLeft | Qt.AlignTop)
# Add some space after logo
layout.addSpacing(20)
self.info_label = QLabel()
self.info_label.setAlignment(Qt.AlignCenter)
self.info_label.setWordWrap(True)
self.info_label.setMaximumWidth(450) # Maximale Breite für bessere Lesbarkeit
layout.addWidget(self.info_label, 0, Qt.AlignCenter)
layout.addWidget(self.info_label, 1, Qt.AlignCenter) # Use stretch factor 1
# Spacer für bessere vertikale Verteilung
layout.addStretch()

7
views/base/__init__.py Normale Datei
Datei anzeigen

@ -0,0 +1,7 @@
"""
Base View Components - Theme-aware base classes for all UI elements
"""
from views.base.theme_aware_widget import ThemeAwareWidget
__all__ = ['ThemeAwareWidget']

Datei anzeigen

@ -0,0 +1,99 @@
"""
Theme-Aware Base Widget - Base class for all custom widgets
Provides automatic theme updates and color access
"""
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import pyqtSlot
import logging
logger = logging.getLogger(__name__)
class ThemeAwareWidget(QWidget):
"""
Base class for all custom widgets that need theme support.
Automatically connects to theme changes and provides helper methods.
"""
def __init__(self, parent=None):
"""
Initialize theme-aware widget.
Args:
parent: Parent widget
"""
super().__init__(parent)
self._theme_manager = None
self._setup_theme_connection()
def _setup_theme_connection(self):
"""Setup connection to theme manager for automatic updates."""
try:
# Try to find theme manager in main window
main_window = self.window()
if main_window and hasattr(main_window, 'theme_manager'):
self._theme_manager = main_window.theme_manager
# Connect to theme change signal
self._theme_manager.theme_changed.connect(self._on_theme_changed)
logger.debug(f"{self.__class__.__name__} connected to theme manager")
except Exception as e:
logger.warning(f"Could not connect to theme manager: {e}")
@pyqtSlot(str)
def _on_theme_changed(self, theme_name: str):
"""
Called when theme changes.
Override in subclasses to handle theme-specific updates.
Args:
theme_name: Name of the new theme ('light' or 'dark')
"""
# Subclasses should override this method
pass
def get_theme_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, or empty string if not found
"""
if self._theme_manager:
return self._theme_manager.get_color(color_key)
return ''
def get_current_theme(self) -> str:
"""
Get the current theme name.
Returns:
Current theme name ('light' or 'dark'), or 'light' as default
"""
if self._theme_manager:
return self._theme_manager.get_current_theme()
return 'light'
def is_dark_mode(self) -> bool:
"""
Check if dark mode is currently active.
Returns:
True if dark mode is active, False otherwise
"""
if self._theme_manager:
return self._theme_manager.is_dark_mode()
return False
def showEvent(self, event):
"""
Override showEvent to ensure theme connection is established.
Some widgets might not have access to main window during __init__.
"""
super().showEvent(event)
# Try to setup theme connection again if not established
if not self._theme_manager:
self._setup_theme_connection()

Datei anzeigen

@ -33,12 +33,7 @@ class SidebarFilter(QWidget):
def init_ui(self):
"""Initialisiert die UI nach Styleguide"""
self.setMaximumWidth(260) # Styleguide: Sidebar-Breite
self.setStyleSheet("""
QWidget {
background-color: #FFFFFF;
border-right: 1px solid #E2E8F0;
}
""")
self.setObjectName("filter_sidebar") # For QSS targeting
layout = QVBoxLayout(self)
layout.setContentsMargins(20, 20, 20, 20)
@ -70,29 +65,7 @@ class SidebarFilter(QWidget):
btn = QPushButton(f"{name} (0)")
btn.setObjectName(key)
btn.setCursor(Qt.PointingHandCursor)
btn.setStyleSheet("""
QPushButton {
background-color: transparent;
border: none;
text-align: left;
padding: 12px 16px;
border-radius: 8px;
font-size: 14px;
color: #4A5568;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
QPushButton:hover {
background-color: #F7FAFC;
color: #2D3748;
}
QPushButton[selected="true"] {
background-color: #E6F2FF;
color: #1E40AF;
font-weight: 500;
border-left: 3px solid #3182CE;
padding-left: 13px;
}
""")
btn.setObjectName("filter_button") # For QSS targeting
btn.clicked.connect(lambda: self._on_filter_clicked(key))
# Erste Option als aktiv setzen
@ -155,11 +128,7 @@ class AccountsOverviewView(QWidget):
def init_ui(self):
"""Initialisiert die UI nach Styleguide"""
self.setStyleSheet("""
QWidget {
background-color: #F8FAFC;
}
""")
self.setObjectName("accounts_overview") # For QSS targeting
# Hauptlayout
main_layout = QHBoxLayout(self)
@ -173,7 +142,7 @@ class AccountsOverviewView(QWidget):
# Content Area
content_widget = QWidget()
content_widget.setStyleSheet("background-color: #F8FAFC;")
content_widget.setObjectName("accounts_content") # For QSS targeting
content_layout = QVBoxLayout(content_widget)
content_layout.setContentsMargins(40, 30, 40, 30)
content_layout.setSpacing(20)
@ -185,10 +154,7 @@ class AccountsOverviewView(QWidget):
title_font = QFont("Poppins", 24)
title_font.setBold(True)
self.title.setFont(title_font)
self.title.setStyleSheet("""
color: #1A365D;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
self.title.setObjectName("section_title") # For QSS targeting
header_layout.addWidget(self.title)
header_layout.addStretch()
@ -198,34 +164,11 @@ class AccountsOverviewView(QWidget):
# Scroll Area für Accounts
self.scroll = QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll.setStyleSheet("""
QScrollArea {
border: none;
background-color: transparent;
}
QScrollBar:vertical {
background-color: #F1F5F9;
width: 8px;
border-radius: 4px;
}
QScrollBar::handle:vertical {
background-color: #CBD5E0;
min-height: 20px;
border-radius: 4px;
}
QScrollBar::handle:vertical:hover {
background-color: #A0AEC0;
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
border: none;
background: none;
}
""")
# No inline styles - handled by QSS
# Grid Container
self.container = QWidget()
self.container.setStyleSheet("background-color: transparent;")
self.container.setObjectName("grid_container") # For QSS targeting
self.grid_layout = QGridLayout(self.container)
self.grid_layout.setSpacing(24) # Styleguide Grid-Gap
self.grid_layout.setContentsMargins(0, 0, 0, 0)
@ -300,11 +243,7 @@ class AccountsOverviewView(QWidget):
header_font = QFont("Poppins", 18)
header_font.setWeight(QFont.DemiBold)
header.setFont(header_font)
header.setStyleSheet("""
color: #1A365D;
padding: 8px 0;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
header.setObjectName("platform_header") # For QSS targeting
self.grid_layout.addWidget(header, row, 0, 1, 3)
row += 1
@ -364,26 +303,8 @@ class AccountsOverviewView(QWidget):
cancel_button = msg_box.addButton("Abbrechen", QMessageBox.NoRole)
msg_box.setDefaultButton(cancel_button) # Abbrechen als Standard
# Explizites Styling für den Löschen-Button
delete_button.setStyleSheet("""
QPushButton {
background-color: #F44336;
color: #FFFFFF;
border: 1px solid #D32F2F;
border-radius: 4px;
padding: 6px 20px;
min-width: 80px;
min-height: 26px;
font-weight: 500;
}
QPushButton:hover {
background-color: #D32F2F;
border-color: #B71C1C;
}
QPushButton:pressed {
background-color: #B71C1C;
}
""")
# Set object name for QSS targeting
delete_button.setObjectName("delete_confirm_button")
msg_box.exec_()

Datei anzeigen

@ -45,19 +45,11 @@ class PlatformGridView(QWidget):
title_font.setBold(True)
self.title_label.setFont(title_font)
self.title_label.setStyleSheet("""
QLabel {
color: #1A365D;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
margin-bottom: 20px;
}
""")
layout.addWidget(self.title_label)
# Container für Plattform-Grid
platforms_container = QWidget()
platforms_container.setStyleSheet("background: transparent;")
platforms_container.setObjectName("platforms_container") # For QSS targeting
grid_layout = QGridLayout(platforms_container)
grid_layout.setSpacing(24) # Styleguide Grid-Gap

Datei anzeigen

@ -32,13 +32,8 @@ class TabNavigation(QWidget):
# Feste Höhe nach Styleguide
self.setFixedHeight(48)
# Basis-Styling
self.setStyleSheet("""
QWidget {
background-color: #FFFFFF;
border-bottom: 1px solid #E2E8F0;
}
""")
# Set object name for QSS targeting - NO inline styles!
self.setObjectName("tab_navigation")
# Layout
layout = QHBoxLayout(self)
@ -70,30 +65,7 @@ class TabNavigation(QWidget):
font.setWeight(QFont.Medium)
btn.setFont(font)
btn.setStyleSheet("""
QPushButton {
background: transparent;
border: none;
border-bottom: 2px solid transparent;
padding: 12px 24px;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 15px;
font-weight: 500;
color: #4A5568;
min-width: 100px;
}
QPushButton:checked {
color: #1A365D;
border-bottom-color: #3182CE;
}
QPushButton:hover:!checked {
color: #2D3748;
background-color: #F7FAFC;
}
QPushButton:pressed {
color: #1A365D;
}
""")
# No inline styles - handled by QSS generator
return btn

Datei anzeigen

@ -27,7 +27,6 @@ class MainWindow(QMainWindow):
# Signale
platform_selected = pyqtSignal(str)
back_to_selector_requested = pyqtSignal()
theme_toggled = pyqtSignal()
def __init__(self, theme_manager=None, language_manager=None, db_manager=None):
super().__init__()
@ -143,32 +142,53 @@ class MainWindow(QMainWindow):
self.add_platform_tabs(platform_controller)
def _create_menus(self):
"""Erstellt die Menüeinträge."""
"""Erstellt die Menüeinträge und Dark Mode Toggle."""
# Erstelle ein Logo-Button anstelle des Text-Menüs
logo_widget = QPushButton()
logo_widget.setIcon(QIcon(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"resources", "icons", "intelsight-logo.svg")))
logo_widget.setIconSize(QSize(120, 40))
logo_widget.setFlat(True)
logo_widget.setCursor(Qt.PointingHandCursor)
logo_widget.setStyleSheet("""
QPushButton {
background-color: transparent;
border: none;
padding: 5px;
}
QPushButton:hover {
background-color: rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
""")
logo_widget.clicked.connect(self._show_about_dialog)
self.logo_widget = QPushButton() # Store as instance variable for easier access
# Add logo to menu bar
self.menuBar().setCornerWidget(logo_widget, Qt.TopLeftCorner)
# Get the correct logo based on current theme
if self.theme_manager:
logo_path = self.theme_manager.get_icon_path("intelsight-logo")
else:
# Fallback if no theme manager
logo_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"resources", "icons", "intelsight-logo.svg")
self.logo_widget.setIcon(QIcon(logo_path))
self.logo_widget.setIconSize(QSize(120, 40))
self.logo_widget.setFlat(True)
self.logo_widget.setCursor(Qt.PointingHandCursor)
self.logo_widget.setObjectName("logo_button") # For QSS targeting
self.logo_widget.clicked.connect(self._show_about_dialog)
# Add logo to menu bar (left side)
self.menuBar().setCornerWidget(self.logo_widget, Qt.TopLeftCorner)
# Create container for dark mode toggle (right side)
right_container = QWidget()
right_container.setObjectName("menubar_right_container") # For QSS targeting
right_layout = QHBoxLayout(right_container)
right_layout.setContentsMargins(0, 5, 10, 5) # Add some right margin
# Import and create Dark Mode Toggle
from views.widgets.dark_mode_toggle import DarkModeToggle
self.dark_mode_toggle = DarkModeToggle(self)
# Set initial state based on current theme
if self.theme_manager:
initial_dark = self.theme_manager.is_dark_mode()
self.dark_mode_toggle.setChecked(initial_dark)
# Connect toggle to theme manager
self.dark_mode_toggle.toggled.connect(self._on_theme_toggled)
right_layout.addWidget(self.dark_mode_toggle)
# Add dark mode toggle to menu bar (right side)
self.menuBar().setCornerWidget(right_container, Qt.TopRightCorner)
# Store reference for language updates
self.about_action = logo_widget
self.about_action = self.logo_widget
def _show_about_dialog(self):
@ -177,6 +197,17 @@ class MainWindow(QMainWindow):
dialog.exec_()
def _on_theme_toggled(self, is_dark):
"""Handle theme toggle from the dark mode switch."""
if self.theme_manager:
if is_dark:
self.theme_manager.apply_theme(self.theme_manager.DARK_THEME)
else:
self.theme_manager.apply_theme(self.theme_manager.LIGHT_THEME)
# Explicitly update logo after theme change
self.update_logo()
def refresh_language_ui(self):
"""
Aktualisiert alle UI-Texte nach einem Sprachwechsel.
@ -238,4 +269,43 @@ class MainWindow(QMainWindow):
def add_log_widget(self, text_widget):
"""Fügt einen GUI-Handler zum Logger hinzu."""
add_gui_handler(logger, text_widget)
add_gui_handler(logger, text_widget)
def update_logo(self, theme_name: str = None):
"""
Updates the logo based on the current theme.
Args:
theme_name: Name of the theme ("light" or "dark") - not used, kept for compatibility
"""
try:
# Use stored reference to logo widget
if hasattr(self, 'logo_widget') and self.logo_widget and self.theme_manager:
# Get the new logo path from theme manager based on current theme
current_theme = self.theme_manager.get_current_theme()
logo_path = self.theme_manager.get_icon_path("intelsight-logo")
print(f"DEBUG: Updating logo for theme '{current_theme}'")
print(f"DEBUG: Logo path: {logo_path}")
print(f"DEBUG: File exists: {os.path.exists(logo_path)}")
if os.path.exists(logo_path):
icon = QIcon(logo_path)
self.logo_widget.setIcon(icon)
# Force update
self.logo_widget.update()
self.logo_widget.repaint()
logger.info(f"Logo updated to {logo_path} for theme {current_theme}")
print(f"DEBUG: Logo icon set successfully")
else:
logger.warning(f"Logo file not found: {logo_path}")
print(f"DEBUG: Logo file not found!")
else:
print(f"DEBUG: Cannot update logo - missing components:")
print(f" - has logo_widget: {hasattr(self, 'logo_widget')}")
print(f" - logo_widget exists: {hasattr(self, 'logo_widget') and self.logo_widget}")
print(f" - theme_manager exists: {self.theme_manager is not None}")
except Exception as e:
logger.error(f"Could not update logo: {e}")
print(f"DEBUG: Exception updating logo: {e}")

Datei anzeigen

@ -48,7 +48,7 @@ class PlatformSelector(QWidget):
# Content Container mit gestapelten Widgets
self.content_stack = QStackedWidget()
self.content_stack.setStyleSheet("background-color: #F8FAFC;")
self.content_stack.setObjectName("content_stack") # For QSS targeting, no inline styles!
# Platform Grid View (Tab 0)
self.platform_grid = PlatformGridView(self.language_manager)

Datei anzeigen

@ -85,10 +85,7 @@ class AccountCard(QFrame):
username_font = QFont("Poppins", 16)
username_font.setWeight(QFont.DemiBold)
username_label.setFont(username_font)
username_label.setStyleSheet("""
color: #1A365D;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
username_label.setObjectName("account_username") # For QSS targeting
info_layout.addWidget(username_label)
info_layout.addStretch()
@ -97,25 +94,7 @@ class AccountCard(QFrame):
# Login Button
self.login_btn = QPushButton("Login")
self.login_btn.setCursor(Qt.PointingHandCursor)
self.login_btn.setStyleSheet("""
QPushButton {
background-color: #3182CE;
color: #FFFFFF;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 12px;
font-weight: 500;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-width: 60px;
}
QPushButton:hover {
background-color: #2563EB;
}
QPushButton:pressed {
background-color: #1D4ED8;
}
""")
self.login_btn.setObjectName("account_login_btn") # For QSS targeting
self.login_btn.clicked.connect(lambda: self._on_login_clicked())
header_layout.addWidget(self.login_btn)
@ -136,32 +115,14 @@ class AccountCard(QFrame):
email_layout.setSpacing(8)
email_label = QLabel(self.account_data.get("email", ""))
email_label.setStyleSheet("""
color: #4A5568;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
email_label.setObjectName("account_detail_text") # For QSS targeting
email_layout.addWidget(email_label)
# Email Copy Button
email_copy_btn = QPushButton()
email_copy_btn.setToolTip("E-Mail kopieren")
email_copy_btn.setCursor(Qt.PointingHandCursor)
email_copy_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: none;
padding: 2px;
min-width: 20px;
max-width: 20px;
min-height: 20px;
max-height: 20px;
}
QPushButton:hover {
background-color: #F7FAFC;
border-radius: 4px;
}
""")
email_copy_btn.setObjectName("account_icon_btn") # For QSS targeting
self.copy_icon = IconFactory.get_icon("copy", size=16)
self.check_icon = IconFactory.get_icon("check", size=16, color="#10B981")
self.email_copy_btn = email_copy_btn
@ -183,32 +144,14 @@ class AccountCard(QFrame):
pass_layout.setSpacing(8)
self.password_label = QLabel("••••••••")
self.password_label.setStyleSheet("""
color: #4A5568;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
self.password_label.setObjectName("account_detail_text") # For QSS targeting
pass_layout.addWidget(self.password_label)
# Copy Button
copy_btn = QPushButton()
copy_btn.setToolTip("Passwort kopieren")
copy_btn.setCursor(Qt.PointingHandCursor)
copy_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: none;
padding: 2px;
min-width: 20px;
max-width: 20px;
min-height: 20px;
max-height: 20px;
}
QPushButton:hover {
background-color: #F7FAFC;
border-radius: 4px;
}
""")
copy_btn.setObjectName("account_icon_btn") # For QSS targeting
self.password_copy_btn = copy_btn
self.password_copy_btn.setIcon(self.copy_icon)
self.password_copy_btn.setIconSize(QSize(16, 16))
@ -219,21 +162,7 @@ class AccountCard(QFrame):
self.visibility_btn = QPushButton()
self.visibility_btn.setToolTip("Passwort anzeigen")
self.visibility_btn.setCursor(Qt.PointingHandCursor)
self.visibility_btn.setStyleSheet("""
QPushButton {
background: transparent;
border: none;
padding: 2px;
min-width: 20px;
max-width: 20px;
min-height: 20px;
max-height: 20px;
}
QPushButton:hover {
background-color: #F7FAFC;
border-radius: 4px;
}
""")
self.visibility_btn.setObjectName("account_icon_btn") # For QSS targeting
self.eye_icon = IconFactory.get_icon("eye", size=16)
self.eye_slash_icon = IconFactory.get_icon("eye-slash", size=16)
self.visibility_btn.setIcon(self.eye_icon)
@ -249,11 +178,7 @@ class AccountCard(QFrame):
details_grid.addWidget(date_icon, 2, 0)
date_label = QLabel(self.account_data.get("created_at", ""))
date_label.setStyleSheet("""
color: #A0AEC0;
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
""")
date_label.setObjectName("account_date_text") # For QSS targeting
details_grid.addWidget(date_label, 2, 1)
layout.addLayout(details_grid)
@ -265,27 +190,7 @@ class AccountCard(QFrame):
# Export Button
self.export_btn = QPushButton("Profil\nexportieren")
self.export_btn.setCursor(Qt.PointingHandCursor)
self.export_btn.setStyleSheet("""
QPushButton {
background-color: #10B981;
color: #FFFFFF;
border: none;
border-radius: 6px;
padding: 4px 12px;
font-size: 12px;
font-weight: 500;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-width: 120px;
min-height: 36px;
text-align: center;
}
QPushButton:hover {
background-color: #059669;
}
QPushButton:pressed {
background-color: #047857;
}
""")
self.export_btn.setObjectName("account_export_btn") # For QSS targeting
self.export_btn.clicked.connect(lambda: self.export_requested.emit(self.account_data))
actions_layout.addWidget(self.export_btn)
@ -294,25 +199,7 @@ class AccountCard(QFrame):
# Delete Button
self.delete_btn = QPushButton("Löschen")
self.delete_btn.setCursor(Qt.PointingHandCursor)
self.delete_btn.setStyleSheet("""
QPushButton {
background-color: #DC2626;
color: #FFFFFF;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 12px;
font-weight: 500;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-width: 90px;
}
QPushButton:hover {
background-color: #B91C1C;
}
QPushButton:pressed {
background-color: #991B1B;
}
""")
self.delete_btn.setObjectName("account_delete_btn") # For QSS targeting
self.delete_btn.clicked.connect(lambda: self.delete_requested.emit(self.account_data))
actions_layout.addWidget(self.delete_btn)
@ -379,40 +266,14 @@ class AccountCard(QFrame):
self.password_copy_timer.stop()
def _apply_status_styling(self):
"""Wendet Status-basiertes Styling mit Pastel-Hintergrund und farbiger Umrandung an"""
"""Wendet Status-basiertes Styling an"""
# Status aus Account-Daten lesen - Standard ist "active" (grün)
status = self.account_data.get("status", "active")
# Status-Farben definieren (nur Grün/Rot)
status_styles = {
"active": {
"background": "#F0FDF4", # Sehr helles Mintgrün
"border": "#10B981", # Kräftiges Grün
"hover_border": "#059669" # Dunkleres Grün beim Hover
},
"inactive": {
"background": "#FEF2F2", # Sehr helles Rosa
"border": "#EF4444", # Kräftiges Rot
"hover_border": "#DC2626" # Dunkleres Rot beim Hover
}
}
# Aktueller Status oder Fallback auf active (grün)
current_style = status_styles.get(status, status_styles["active"])
# CSS-Styling anwenden
self.setStyleSheet(f"""
QFrame#accountCard {{
background-color: {current_style["background"]};
border: 2px solid {current_style["border"]};
border-radius: 8px;
padding: 16px;
}}
QFrame#accountCard:hover {{
border: 2px solid {current_style["hover_border"]};
background-color: {current_style["background"]};
}}
""")
# Set a property that QSS can use for styling
self.setProperty("status", status)
# Force style refresh
self.setStyle(self.style())
def update_status(self, new_status: str):
"""Aktualisiert den Status der Account-Karte und das Styling"""

Datei anzeigen

@ -0,0 +1,233 @@
"""
Dark Mode Toggle Widget for PyQt5
Based on Corporate Design Guidelines with smooth animations
"""
from PyQt5.QtWidgets import QWidget
from PyQt5.QtCore import Qt, pyqtSignal, QPropertyAnimation, QEasingCurve, QRectF, pyqtProperty, QByteArray
from PyQt5.QtGui import QPainter, QColor, QBrush, QPen
from PyQt5.QtSvg import QSvgRenderer
class DarkModeToggle(QWidget):
"""
Animated Toggle-Switch for Dark/Light Mode with embedded SVG Icons.
Features:
- Smooth 200ms animation with InOutCubic easing
- Embedded sun/moon icons (no external files needed)
- Corporate design colors (#CBD5E0 for light, #00D4FF for dark)
- Size: 60x30 pixels
- PyQt5 compatible
Signals:
toggled(bool): Emitted when toggle state changes (True = Dark Mode)
"""
# Signal emitted when toggle state changes
toggled = pyqtSignal(bool)
def __init__(self, parent=None, initial_dark_mode=False):
"""
Initialize the Dark Mode Toggle.
Args:
parent: Parent widget
initial_dark_mode: Initial state (False = Light Mode, True = Dark Mode)
"""
super().__init__(parent)
# Fixed size as per design spec
self.setFixedSize(60, 30)
self.setCursor(Qt.PointingHandCursor)
# State management
self._checked = initial_dark_mode
# Animation property for smooth sliding
self._handle_position = 27.0 if initial_dark_mode else 3.0
# Configure animation
self._animation = QPropertyAnimation(self, b"handle_position")
self._animation.setDuration(200) # 200ms as per spec
self._animation.setEasingCurve(QEasingCurve.InOutCubic)
# Initialize SVG renderers
self.sun_svg = QSvgRenderer()
self.moon_svg = QSvgRenderer()
# Load embedded SVG content
self._load_svg_icons()
# Tooltip for accessibility
self._update_tooltip()
def _load_svg_icons(self):
"""Load embedded SVG icons for sun and moon."""
# Sun icon for Light Mode (black strokes)
sun_svg_content = b'''<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 3V4M12 20V21M4 12H3M6.31412 6.31412L5.5 5.5M17.6859 6.31412L18.5 5.5M6.31412 17.69L5.5 18.5001M17.6859 17.69L18.5 18.5001M21 12H20M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>'''
# Moon icon for Dark Mode (dark blue strokes matching primary color)
moon_svg_content = b'''<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.32031 11.6835C3.32031 16.6541 7.34975 20.6835 12.3203 20.6835C16.1075 20.6835 19.3483 18.3443 20.6768 15.032C19.6402 15.4486 18.5059 15.6834 17.3203 15.6834C12.3497 15.6834 8.32031 11.654 8.32031 6.68342C8.32031 5.50338 8.55165 4.36259 8.96453 3.32996C5.65605 4.66028 3.32031 7.89912 3.32031 11.6835Z" stroke="#232D53" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>'''
# Load SVG data into renderers
self.sun_svg.load(QByteArray(sun_svg_content))
self.moon_svg.load(QByteArray(moon_svg_content))
# Property for animation
def get_handle_position(self):
"""Get current handle position for animation."""
return self._handle_position
def set_handle_position(self, pos):
"""Set handle position and trigger repaint."""
self._handle_position = pos
self.update() # Trigger paintEvent
# Define property for QPropertyAnimation
handle_position = pyqtProperty(float, get_handle_position, set_handle_position)
def paintEvent(self, event):
"""
Custom paint event to draw the toggle switch.
Draws:
1. Track (background pill shape)
2. Handle (white circular button)
3. Icon (sun or moon in the handle)
"""
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# Draw track (pill-shaped background)
track_rect = QRectF(0, 0, self.width(), self.height())
track_radius = self.height() / 2
# Track color based on state
if self._checked:
# Dark mode active - cyan color from corporate design
track_color = QColor("#00D4FF")
else:
# Light mode active - gray color from design spec
track_color = QColor("#CBD5E0")
painter.setBrush(QBrush(track_color))
painter.setPen(QPen(Qt.NoPen))
painter.drawRoundedRect(track_rect, track_radius, track_radius)
# Draw handle (white circle that slides)
handle_diameter = self.height() - 6 # 24px (30 - 6)
handle_rect = QRectF(
self._handle_position, # Animated position
3, # 3px from top
handle_diameter,
handle_diameter
)
# White handle
painter.setBrush(QBrush(QColor("#FFFFFF")))
painter.setPen(QPen(Qt.NoPen))
painter.drawEllipse(handle_rect)
# Draw icon inside handle
icon_size = 16 # Icon size within the 24px handle
icon_rect = QRectF(
handle_rect.x() + (handle_diameter - icon_size) / 2,
handle_rect.y() + (handle_diameter - icon_size) / 2,
icon_size,
icon_size
)
# Render appropriate icon
if self._checked:
# Dark mode - show moon icon
self.moon_svg.render(painter, icon_rect)
else:
# Light mode - show sun icon
self.sun_svg.render(painter, icon_rect)
def mousePressEvent(self, event):
"""
Handle mouse click to toggle state.
Only responds to left mouse button clicks.
"""
if event.button() == Qt.LeftButton:
self.toggle()
def toggle(self):
"""Toggle between Light and Dark mode."""
self.setChecked(not self._checked)
def setChecked(self, checked):
"""
Set the toggle state programmatically.
Args:
checked: True for Dark Mode, False for Light Mode
"""
# Only proceed if state actually changes
if self._checked == checked:
return
self._checked = checked
# Stop any running animation
self._animation.stop()
# Set animation endpoints
if checked:
# Animate to dark mode position (right side)
# Handle should be at: width - handle_diameter - 3px margin
self._animation.setEndValue(self.width() - (self.height() - 6) - 3)
else:
# Animate to light mode position (left side)
self._animation.setEndValue(3.0)
# Start animation
self._animation.start()
# Update tooltip
self._update_tooltip()
# Emit signal for theme change
self.toggled.emit(checked)
def isChecked(self):
"""
Get current toggle state.
Returns:
bool: True if Dark Mode is active, False for Light Mode
"""
return self._checked
def _update_tooltip(self):
"""Update tooltip based on current state."""
if self._checked:
self.setToolTip("Zu Light Mode wechseln")
else:
self.setToolTip("Zu Dark Mode wechseln")
def isDarkMode(self):
"""
Convenience method to check if dark mode is active.
Returns:
bool: True if Dark Mode is active
"""
return self._checked
def isLightMode(self):
"""
Convenience method to check if light mode is active.
Returns:
bool: True if Light Mode is active
"""
return not self._checked

Datei anzeigen

@ -38,27 +38,8 @@ class PlatformButton(QWidget):
self.icon_button.setIconSize(QSize(120, 120)) # Größeres Icon
self.icon_button.setMinimumSize(150, 150)
# Platform button styling based on Styleguide
self.icon_button.setStyleSheet("""
QPushButton {
background-color: #F5F7FF;
border: 1px solid transparent;
border-radius: 16px;
padding: 32px;
}
QPushButton:hover {
background-color: #E8EBFF;
border: 1px solid #0099CC;
}
QPushButton:pressed {
background-color: #DCE2FF;
border: 1px solid #0099CC;
}
QPushButton:disabled {
background-color: #F0F0F0;
opacity: 0.5;
}
""")
# Set object name for QSS targeting
self.icon_button.setObjectName("platform_icon_button")
# Button-Signal verbinden
self.icon_button.clicked.connect(self.clicked)
@ -70,15 +51,8 @@ class PlatformButton(QWidget):
name_font.setPointSize(12)
name_font.setBold(True)
self.name_label.setFont(name_font)
# Name label styling based on Styleguide
self.name_label.setStyleSheet("""
QLabel {
color: #232D53;
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-weight: 600;
letter-spacing: 0.5px;
}
""")
# Set object name for QSS targeting
self.name_label.setObjectName("platform_name_label")
# Widgets zum Layout hinzufügen
layout.addWidget(self.icon_button, 0, Qt.AlignCenter)