350 Zeilen
14 KiB
Python
350 Zeilen
14 KiB
Python
# 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, QTimer
|
|
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()
|
|
|
|
# Timer für periodische Guard-Status-Prüfung des Zurück-Buttons
|
|
self.guard_check_timer = QTimer()
|
|
self.guard_check_timer.timeout.connect(self._update_back_button_state)
|
|
self.guard_check_timer.start(1000) # Alle 1 Sekunde prüfen
|
|
|
|
# Initiale Button-Status-Prüfung
|
|
self._update_back_button_state()
|
|
|
|
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 _update_back_button_state(self):
|
|
"""
|
|
Aktualisiert den Status des Zurück-Buttons basierend auf ProcessGuard.
|
|
|
|
Deaktiviert den Zurück-Button wenn ein Prozess läuft oder Pause aktiv ist,
|
|
um zu verhindern dass User während Account-Erstellung/Login navigieren.
|
|
"""
|
|
from utils.process_guard import get_guard
|
|
guard = get_guard()
|
|
|
|
# Prüfe ob blockiert
|
|
is_locked = guard.is_locked()
|
|
is_paused = guard.is_paused()
|
|
is_blocked = is_locked or is_paused
|
|
|
|
# Button entsprechend setzen (nur wenn auf Platform-View)
|
|
if self.stacked_widget.currentWidget() == self.platform_container:
|
|
self.back_button.setEnabled(not is_blocked)
|
|
|
|
# Tooltip setzen
|
|
if is_blocked:
|
|
status_msg = guard.get_status_message()
|
|
self.back_button.setToolTip(f"⚠ {status_msg}")
|
|
else:
|
|
# Reset Tooltip
|
|
if self.language_manager:
|
|
tooltip = self.language_manager.get_text("buttons.back", "↩ Zurück")
|
|
else:
|
|
tooltip = "↩ Zurück"
|
|
self.back_button.setToolTip(tooltip)
|
|
|
|
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}") |