# Path: views/main_window.py """ Hauptfenster der AccountForger Anwendung. """ import os import logging from PyQt5.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QStackedWidget, QTabWidget, QAction, QMessageBox ) from PyQt5.QtCore import Qt, pyqtSignal, QSize, QFile from PyQt5.QtGui import QIcon, QFont, QPixmap from localization.language_manager import LanguageManager from views.platform_selector import PlatformSelector from views.about_dialog import AboutDialog from utils.logger import add_gui_handler logger = logging.getLogger("main") class MainWindow(QMainWindow): """Hauptfenster der Anwendung.""" # Signale platform_selected = pyqtSignal(str) back_to_selector_requested = pyqtSignal() def __init__(self, theme_manager=None, language_manager=None, db_manager=None): super().__init__() # Theme Manager self.theme_manager = theme_manager # Language Manager self.language_manager = language_manager self.db_manager = db_manager # Fenstereigenschaften setzen self.setWindowTitle("AccountForger") # Größere Mindest- und Startgröße, damit Plattformnamen # (z.B. "Twitter" und "VK") nicht abgeschnitten werden und # Tabelleninhalte genügend Platz haben self.setMinimumSize(1450, 700) self.resize(1450, 800) # Hauptwidget und Layout self.central_widget = QWidget() self.setCentralWidget(self.central_widget) # Haupt-Layout self.main_layout = QVBoxLayout(self.central_widget) # Gestapeltes Widget für Anzeige von Plattformwahl und Hauptfunktionen self.stacked_widget = QStackedWidget() self.main_layout.addWidget(self.stacked_widget) # Plattform-Auswahl-Widget self.platform_selector = PlatformSelector(self.language_manager, self.db_manager) self.stacked_widget.addWidget(self.platform_selector) # Container für Plattform-spezifische Tabs self.platform_container = QWidget() self.platform_layout = QVBoxLayout(self.platform_container) # Header-Bereich mit Titel und Zurück-Button self.header_widget = QWidget() self.header_layout = QHBoxLayout(self.header_widget) self.header_layout.setContentsMargins(10, 10, 10, 10) # Zurück-Button self.back_button = QPushButton("↩ Zurück") self.back_button.setMinimumWidth(120) # Breiter für den Text self.header_layout.addWidget(self.back_button) # Plattform-Titel self.platform_title = QLabel() self.platform_title.setObjectName("platform_title") # For CSS styling title_font = QFont() title_font.setPointSize(24) title_font.setBold(True) self.platform_title.setFont(title_font) self.platform_title.setAlignment(Qt.AlignCenter) self.header_layout.addWidget(self.platform_title) # Platzhalter für die rechte Seite, um die Zentrierung zu erhalten spacer = QLabel() spacer.setMinimumWidth(120) # Gleiche Breite wie der Button self.header_layout.addWidget(spacer) self.platform_layout.addWidget(self.header_widget) # Tabs für die Plattform self.tabs = QTabWidget() self.platform_layout.addWidget(self.tabs) # Stacked Widget hinzufügen self.stacked_widget.addWidget(self.platform_container) # Anfänglich Platform-Selektor anzeigen self.stacked_widget.setCurrentWidget(self.platform_selector) # Statusleiste self.statusBar().showMessage("Bereit") # "Über"-Menü erstellen if self.language_manager: self._create_menus() # Verbinde das Sprachänderungssignal mit der UI-Aktualisierung self.language_manager.language_changed.connect(self.refresh_language_ui) # Verbinde Signale self.connect_signals() def connect_signals(self): """Verbindet die internen Signale.""" # Platform-Selector-Signal verbinden self.platform_selector.platform_selected.connect(self.platform_selected) # Zurück-Button-Signal verbinden self.back_button.clicked.connect(self.back_to_selector_requested) def init_platform_ui(self, platform: str, platform_controller): """Initialisiert die plattformspezifische UI.""" # Tabs entfernen (falls vorhanden) while self.tabs.count() > 0: self.tabs.removeTab(0) # Plattform-Titel setzen - nur Platform-Name self.platform_title.setText(f"{platform.title()}") # Icon laden und anzeigen if self.theme_manager: icon_path = self.theme_manager.get_icon_path(platform.lower()) if os.path.exists(icon_path): self.setWindowTitle(f"{platform.title()}") self.setWindowIcon(QIcon(icon_path)) # Tabs von den Plattform-Controllern holen und hinzufügen self.add_platform_tabs(platform_controller) def _create_menus(self): """Erstellt die Menüeinträge und Dark Mode Toggle.""" # Erstelle ein Logo-Button anstelle des Text-Menüs self.logo_widget = QPushButton() # Store as instance variable for easier access # 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 = self.logo_widget def _show_about_dialog(self): """Öffnet den Über-Dialog.""" dialog = AboutDialog(self.language_manager, self) 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. Diese Methode wird beim Language-Changed-Signal aufgerufen. """ if not self.language_manager: return # Fenstername aktualisieren self.setWindowTitle(self.language_manager.get_text("main.title", "AccountForger")) # Status-Nachricht aktualisieren self.statusBar().showMessage(self.language_manager.get_text("status.ready", "Bereit")) # Den Zurück-Button aktualisieren self.back_button.setText(self.language_manager.get_text("buttons.back", "↩ Zurück")) # Logo-Button braucht keine Text-Aktualisierung # Die Platform Selector-View aktualisieren if hasattr(self.platform_selector, "update_texts"): self.platform_selector.update_texts() # Die aktuelle Plattform-UI aktualisieren, falls vorhanden current_platform = self.platform_title.text().lower() if self.platform_title.text() else None if current_platform: self.platform_title.setText(f"{current_platform.title()}") # Tabs sind versteckt, keine Aktualisierung nötig # Aktualisierung erzwingen self.repaint() def add_platform_tabs(self, platform_controller): """Fügt die Tabs vom Plattform-Controller hinzu.""" # Generator-Tab if hasattr(platform_controller, "get_generator_tab"): generator_tab = platform_controller.get_generator_tab() self.tabs.addTab(generator_tab, "") # Tab-Leiste verstecken, da nur ein Tab self.tabs.tabBar().hide() def show_platform_ui(self): """Zeigt die plattformspezifische UI an.""" self.stacked_widget.setCurrentWidget(self.platform_container) def show_platform_selector(self): """Zeigt den Plattform-Selektor an.""" self.stacked_widget.setCurrentWidget(self.platform_selector) self.setWindowTitle("AccountForger") # Standard-Icon zurücksetzen self.setWindowIcon(QIcon()) def set_status_message(self, message: str): """Setzt eine Nachricht in der Statusleiste.""" self.statusBar().showMessage(message) def add_log_widget(self, text_widget): """Fügt einen GUI-Handler zum Logger hinzu.""" 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}")