diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 975d131..f6ec167 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -15,7 +15,10 @@ "Bash(claude mcp)", "Bash(claude mcp:*)", "Bash(sqlite3:*)", - "Bash(python -m pip install:*)" + "Bash(python -m pip install:*)", + "Bash(python -m pytest teststest_generator_tab_factory.py -v)", + "Bash(python tests:*)", + "Bash(python tests/test_generator_tab_factory.py)" ], "deny": [], "defaultMode": "acceptEdits", diff --git a/CLAUDE_PROJECT_README.md b/CLAUDE_PROJECT_README.md index 7b1bec6..a0edc0f 100644 --- a/CLAUDE_PROJECT_README.md +++ b/CLAUDE_PROJECT_README.md @@ -5,9 +5,9 @@ ## Project Overview - **Path**: `A:\GiTea\AccountForger` -- **Files**: 990 files -- **Size**: 182.0 MB -- **Last Modified**: 2025-09-14 18:54 +- **Files**: 1024 files +- **Size**: 184.1 MB +- **Last Modified**: 2025-10-03 01:25 ## Technology Stack @@ -296,6 +296,7 @@ styles/ │ ├── modal_styles.py │ └── __init__.py tests/ +│ ├── test_generator_tab_factory.py │ ├── test_method_rotation.py │ ├── bright_data/ │ │ └── logs/ @@ -383,6 +384,7 @@ views/ │ ├── accounts_tab.py │ ├── facebook_generator_tab.py │ ├── generator_tab.py + │ ├── generator_tab_factory.py │ └── settings_tab.py └── widgets/ ├── account_card.py @@ -423,3 +425,7 @@ This project is managed with Claude Project Manager. To work with this project: - README updated on 2025-09-13 20:45:18 - README updated on 2025-09-14 11:33:43 - README updated on 2025-09-14 18:54:48 +- README updated on 2025-09-17 19:46:22 +- README updated on 2025-09-21 10:31:22 +- README updated on 2025-09-24 19:25:49 +- README updated on 2025-10-03 20:29:19 diff --git a/controllers/platform_controllers/base_controller.py b/controllers/platform_controllers/base_controller.py index cfaa655..982c0b2 100644 --- a/controllers/platform_controllers/base_controller.py +++ b/controllers/platform_controllers/base_controller.py @@ -7,7 +7,7 @@ from PyQt5.QtCore import QObject from typing import Dict, Any, Optional, Tuple import random -from views.tabs.generator_tab import GeneratorTab +from views.tabs.generator_tab_factory import GeneratorTabFactory from views.tabs.accounts_tab import AccountsTab # SettingsTab import entfernt - wird nicht mehr verwendet @@ -86,10 +86,13 @@ class BasePlatformController(QObject): def create_generator_tab(self): """ - Erstellt den Generator-Tab. - Diese Methode sollte von Unterklassen überschrieben werden. + Erstellt den Generator-Tab mit der Factory. + Unterklassen können diese Methode überschreiben für spezielle Anforderungen. + + Returns: + QWidget: Plattform-spezifischer oder generischer Generator-Tab """ - return GeneratorTab(self.platform_name, self.language_manager) + return GeneratorTabFactory.create_tab(self.platform_name, self.language_manager) def create_accounts_tab(self): """ diff --git a/controllers/platform_controllers/facebook_controller.py b/controllers/platform_controllers/facebook_controller.py index fcfe357..5657bda 100644 --- a/controllers/platform_controllers/facebook_controller.py +++ b/controllers/platform_controllers/facebook_controller.py @@ -11,7 +11,7 @@ from typing import Dict, Any from controllers.platform_controllers.base_controller import BasePlatformController from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread -from views.tabs.generator_tab import GeneratorTab # Verwende generischen GeneratorTab +from views.tabs.generator_tab_factory import GeneratorTabFactory # Factory Pattern für Tab-Erstellung from views.widgets.forge_animation_widget import ForgeAnimationDialog # Forge Dialog für Animation from social_networks.facebook.facebook_automation import FacebookAutomation @@ -127,38 +127,39 @@ class FacebookController(BasePlatformController): def get_generator_tab(self): """ Erstellt und konfiguriert den Generator-Tab für Facebook. - + Verwendet die Factory für saubere Tab-Erstellung. + Returns: - GeneratorTab: Konfigurierter Tab für Facebook mit Geschlechtsauswahl + QWidget: Facebook-spezifischer Tab mit Geschlechtsauswahl """ - # Erstelle generischen Generator-Tab - generator_tab = GeneratorTab( + # Verwende Factory Pattern für Tab-Erstellung + # Die Factory entscheidet, welche Tab-Implementierung verwendet wird + generator_tab = GeneratorTabFactory.create_tab( "Facebook", self.language_manager ) - - # Facebook-spezifische Konfiguration - self._configure_facebook_fields(generator_tab) - - # Verbinde Signale - generator_tab.start_requested.connect(self.handle_generation_request) - generator_tab.stop_requested.connect(self.stop_generation) - + + # Verbinde Signale unabhängig von der Tab-Implementierung + # Alle Tabs müssen diese Signale unterstützen (Interface-Kontrakt) + if hasattr(generator_tab, 'start_requested'): + generator_tab.start_requested.connect(self.handle_generation_request) + if hasattr(generator_tab, 'stop_requested'): + generator_tab.stop_requested.connect(self.stop_generation) + return generator_tab def _configure_facebook_fields(self, generator_tab): """ - Konfiguriert Facebook-spezifische Felder im Generator-Tab. - + Legacy-Methode für Abwärtskompatibilität. + Die Konfiguration erfolgt jetzt in FacebookGeneratorTab. + Args: generator_tab: Der zu konfigurierende Tab """ - # Facebook-spezifische Konfiguration - # Der GeneratorTab kann für Facebook-spezifische Felder erweitert werden - # Geschlecht wird aus den Parametern extrahiert - # Vor- und Nachnamen werden im Worker-Thread aus full_name extrahiert - - logger.debug("Facebook-spezifische Felder konfiguriert (inkl. Geschlechtsauswahl)") + # Diese Methode ist nicht mehr nötig, da FacebookGeneratorTab + # bereits alle Facebook-spezifischen Felder enthält + # Behalten für Abwärtskompatibilität + pass def handle_generation_request(self, params: Dict[str, Any]): """ diff --git a/controllers/platform_controllers/gmail_controller.py b/controllers/platform_controllers/gmail_controller.py index 763cbfd..56fa3d2 100644 --- a/controllers/platform_controllers/gmail_controller.py +++ b/controllers/platform_controllers/gmail_controller.py @@ -89,8 +89,8 @@ class GmailController(BasePlatformController): def create_generator_tab(self): """Erstellt den Generator-Tab und verbindet die Signale""" - from views.tabs.generator_tab import GeneratorTab - generator_tab = GeneratorTab(self.platform_name, self.language_manager) + # Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt + generator_tab = super().create_generator_tab() # Signal verbinden generator_tab.start_requested.connect(self.start_account_creation) diff --git a/controllers/platform_controllers/instagram_controller.py b/controllers/platform_controllers/instagram_controller.py index 082bc33..23ceef5 100644 --- a/controllers/platform_controllers/instagram_controller.py +++ b/controllers/platform_controllers/instagram_controller.py @@ -11,7 +11,7 @@ from typing import Dict, Any from controllers.platform_controllers.base_controller import BasePlatformController from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread -from views.tabs.generator_tab import GeneratorTab +from views.tabs.generator_tab_factory import GeneratorTabFactory from views.tabs.accounts_tab import AccountsTab from views.tabs.settings_tab import SettingsTab from views.widgets.forge_animation_widget import ForgeAnimationDialog @@ -178,7 +178,8 @@ class InstagramController(BasePlatformController): def create_generator_tab(self): """Erstellt den Instagram-Generator-Tab.""" - generator_tab = GeneratorTab(self.platform_name, self.language_manager) + # Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt + generator_tab = super().create_generator_tab() # Instagram-spezifische Anpassungen # Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden @@ -412,4 +413,4 @@ class InstagramController(BasePlatformController): "birthday": "Geburtsdatum" } - return field_labels.get(field_type, field_type.capitalize()) \ No newline at end of file + return field_labels.get(field_type, field_type.capitalize()) diff --git a/controllers/platform_controllers/ok_ru_controller.py b/controllers/platform_controllers/ok_ru_controller.py index d16f31b..c4362cb 100644 --- a/controllers/platform_controllers/ok_ru_controller.py +++ b/controllers/platform_controllers/ok_ru_controller.py @@ -7,7 +7,6 @@ from PyQt5.QtCore import QThread, pyqtSignal from controllers.platform_controllers.base_controller import BasePlatformController from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread -from views.tabs.generator_tab import GeneratorTab from views.widgets.forge_animation_widget import ForgeAnimationDialog from social_networks.ok_ru.ok_ru_automation import OkRuAutomation @@ -44,7 +43,8 @@ class OkRuController(BasePlatformController): def create_generator_tab(self): """Erstellt den Generator-Tab für OK.ru.""" - generator_tab = GeneratorTab(self.platform_name, self.language_manager) + # Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt + generator_tab = super().create_generator_tab() # OK.ru verwendet nur Telefon-Registrierung # Keine spezielle Konfiguration nötig, da GeneratorTab standardmäßig alle Felder hat @@ -190,4 +190,4 @@ class OkRuController(BasePlatformController): self.forge_dialog = None # Normale Verarbeitung - self.handle_account_created(result) \ No newline at end of file + self.handle_account_created(result) diff --git a/controllers/platform_controllers/tiktok_controller.py b/controllers/platform_controllers/tiktok_controller.py index bceb15f..74ff97d 100644 --- a/controllers/platform_controllers/tiktok_controller.py +++ b/controllers/platform_controllers/tiktok_controller.py @@ -10,7 +10,7 @@ from typing import Dict, Any from controllers.platform_controllers.base_controller import BasePlatformController from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread -from views.tabs.generator_tab import GeneratorTab +from views.tabs.generator_tab_factory import GeneratorTabFactory from views.tabs.accounts_tab import AccountsTab from views.tabs.settings_tab import SettingsTab from views.widgets.forge_animation_widget import ForgeAnimationDialog @@ -183,7 +183,8 @@ class TikTokController(BasePlatformController): def create_generator_tab(self): """Erstellt den TikTok-Generator-Tab.""" - generator_tab = GeneratorTab(self.platform_name, self.language_manager) + # Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt + generator_tab = super().create_generator_tab() # TikTok-spezifische Anpassungen # Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden @@ -416,4 +417,4 @@ class TikTokController(BasePlatformController): self.forge_dialog = None # Normale Verarbeitung - self.handle_account_created(result) \ No newline at end of file + self.handle_account_created(result) diff --git a/controllers/platform_controllers/x_controller.py b/controllers/platform_controllers/x_controller.py index 0a84829..41e0c84 100644 --- a/controllers/platform_controllers/x_controller.py +++ b/controllers/platform_controllers/x_controller.py @@ -11,7 +11,7 @@ from typing import Dict, Any, Tuple from controllers.platform_controllers.base_controller import BasePlatformController from controllers.platform_controllers.base_worker_thread import BaseAccountCreationWorkerThread -from views.tabs.generator_tab import GeneratorTab +from views.tabs.generator_tab_factory import GeneratorTabFactory from views.tabs.accounts_tab import AccountsTab from views.tabs.settings_tab import SettingsTab from views.widgets.forge_animation_widget import ForgeAnimationDialog @@ -180,7 +180,8 @@ class XController(BasePlatformController): def create_generator_tab(self): """Erstellt den X-Generator-Tab.""" - generator_tab = GeneratorTab(self.platform_name, self.language_manager) + # Verwende die Basisimplementierung, die die GeneratorTabFactory nutzt + generator_tab = super().create_generator_tab() # X-spezifische Anpassungen # Diese Methode überschreiben, wenn spezifische Anpassungen benötigt werden @@ -414,4 +415,4 @@ class XController(BasePlatformController): "birthday": "Geburtsdatum" } - return field_labels.get(field_type, field_type.capitalize()) \ No newline at end of file + return field_labels.get(field_type, field_type.capitalize()) diff --git a/database/accounts.db b/database/accounts.db index ee29206..2f20744 100644 Binary files a/database/accounts.db and b/database/accounts.db differ diff --git a/tests/test_generator_tab_factory.py b/tests/test_generator_tab_factory.py new file mode 100644 index 0000000..d8f9c10 --- /dev/null +++ b/tests/test_generator_tab_factory.py @@ -0,0 +1,150 @@ +""" +Unit-Tests für GeneratorTabFactory. +Validiert die Factory-Implementierung und plattform-spezifische Tab-Erstellung. +""" + +import unittest +import sys +import os +from unittest.mock import MagicMock, patch + +# Füge Projekt-Root zum Path hinzu +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from PyQt5.QtWidgets import QApplication, QWidget +from views.tabs.generator_tab_factory import GeneratorTabFactory, create_generator_tab +from views.tabs.generator_tab import GeneratorTab + + +class TestGeneratorTabFactory(unittest.TestCase): + """ + Test-Suite für GeneratorTabFactory. + """ + + @classmethod + def setUpClass(cls): + """Erstelle QApplication für Qt-Widgets.""" + if not QApplication.instance(): + cls.app = QApplication([]) + else: + cls.app = QApplication.instance() + + def setUp(self): + """Setup vor jedem Test.""" + # Registry zurücksetzen + GeneratorTabFactory.clear_registry() + self.language_manager = MagicMock() + + def test_create_generic_tab_for_unknown_platform(self): + """Test: Factory erstellt generischen Tab für unbekannte Plattform.""" + tab = GeneratorTabFactory.create_tab("unknown_platform", self.language_manager) + + self.assertIsInstance(tab, QWidget) + self.assertIsInstance(tab, GeneratorTab) + + def test_create_facebook_tab(self): + """Test: Factory erstellt FacebookGeneratorTab für Facebook.""" + tab = GeneratorTabFactory.create_tab("facebook", self.language_manager) + + self.assertIsInstance(tab, QWidget) + # Prüfe ob es der Facebook-spezifische Tab ist + # FacebookGeneratorTab hat gender_male, gender_female, gender_custom Attribute + self.assertTrue(hasattr(tab, 'gender_male'), "Facebook-Tab sollte gender_male haben") + self.assertTrue(hasattr(tab, 'gender_female'), "Facebook-Tab sollte gender_female haben") + self.assertTrue(hasattr(tab, 'gender_custom'), "Facebook-Tab sollte gender_custom haben") + + def test_case_insensitive_platform_names(self): + """Test: Plattform-Namen sind case-insensitive.""" + tab1 = GeneratorTabFactory.create_tab("FACEBOOK", self.language_manager) + tab2 = GeneratorTabFactory.create_tab("Facebook", self.language_manager) + tab3 = GeneratorTabFactory.create_tab("facebook", self.language_manager) + + # Alle sollten Facebook-Tabs sein + for tab in [tab1, tab2, tab3]: + self.assertTrue(hasattr(tab, 'gender_male')) + + def test_registry_functionality(self): + """Test: Tab-Registry funktioniert korrekt.""" + # Erstelle Mock-Tab-Klasse + class MockTab(QWidget): + def __init__(self, platform, language_manager): + super().__init__() + self.platform = platform + self.language_manager = language_manager + + # Registriere Mock-Tab + GeneratorTabFactory.register_tab("test_platform", MockTab) + + # Erstelle Tab + tab = GeneratorTabFactory.create_tab("test_platform", self.language_manager) + + self.assertIsInstance(tab, MockTab) + self.assertEqual(tab.platform, "test_platform") + + def test_lazy_loading(self): + """Test: Tabs werden lazy geladen.""" + # Registry sollte initial leer sein + self.assertEqual(len(GeneratorTabFactory._tab_registry), 0) + + # Erstelle Facebook-Tab + tab = GeneratorTabFactory.create_tab("facebook", self.language_manager) + + # Jetzt sollte Facebook in Registry sein + self.assertIn("facebook", GeneratorTabFactory._tab_registry) + + def test_get_supported_platforms(self): + """Test: Liste der unterstützten Plattformen.""" + platforms = GeneratorTabFactory.get_supported_platforms() + + # Sollte bekannte Plattformen enthalten + self.assertIn("facebook", platforms) + self.assertIn("instagram", platforms) + self.assertIn("tiktok", platforms) + self.assertIn("x", platforms) + + def test_is_platform_supported(self): + """Test: Plattform-Support-Prüfung.""" + self.assertTrue(GeneratorTabFactory.is_platform_supported("facebook")) + self.assertTrue(GeneratorTabFactory.is_platform_supported("FACEBOOK")) + self.assertFalse(GeneratorTabFactory.is_platform_supported("unknown")) + + def test_convenience_function(self): + """Test: Convenience-Funktion create_generator_tab.""" + tab = create_generator_tab("facebook", self.language_manager) + + self.assertIsInstance(tab, QWidget) + self.assertTrue(hasattr(tab, 'gender_male')) + + def test_error_handling_fallback(self): + """Test: Factory fällt auf generischen Tab zurück bei Fehlern.""" + # Simuliere einen Fehler beim Tab-Erstellen + with patch('views.tabs.facebook_generator_tab.FacebookGeneratorTab.__init__', + side_effect=Exception("Test error")): + + tab = GeneratorTabFactory.create_tab("facebook", self.language_manager) + + # Sollte auf generischen Tab zurückfallen + self.assertIsInstance(tab, GeneratorTab) + + def test_signal_compatibility(self): + """Test: Alle Tabs haben die erforderlichen Signale.""" + platforms = ["facebook", "instagram", "tiktok", "x"] + + for platform in platforms: + tab = GeneratorTabFactory.create_tab(platform, self.language_manager) + + # Prüfe erforderliche Signale + self.assertTrue(hasattr(tab, 'start_requested'), + f"{platform}-Tab sollte start_requested Signal haben") + self.assertTrue(hasattr(tab, 'stop_requested'), + f"{platform}-Tab sollte stop_requested Signal haben") + self.assertTrue(hasattr(tab, 'account_created'), + f"{platform}-Tab sollte account_created Signal haben") + + def tearDown(self): + """Cleanup nach jedem Test.""" + GeneratorTabFactory.clear_registry() + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/views/tabs/facebook_generator_tab.py b/views/tabs/facebook_generator_tab.py index 63da0ed..7cc39aa 100644 --- a/views/tabs/facebook_generator_tab.py +++ b/views/tabs/facebook_generator_tab.py @@ -30,8 +30,10 @@ class FacebookGeneratorTab(QWidget): self.init_ui() if self.language_manager: - self.language_manager.language_changed.connect(self.update_texts) - self.update_texts() + # Nur echte LanguageManager haben language_changed Signal + if hasattr(self.language_manager, 'language_changed'): + self.language_manager.language_changed.connect(self.update_texts) + self.update_texts() def init_ui(self): """Initialisiert die UI mit Facebook-spezifischen Feldern.""" diff --git a/views/tabs/generator_tab.py b/views/tabs/generator_tab.py index c39713f..c6945a1 100644 --- a/views/tabs/generator_tab.py +++ b/views/tabs/generator_tab.py @@ -29,8 +29,10 @@ class GeneratorTab(QWidget): self.language_manager = language_manager self.init_ui() if self.language_manager: - self.language_manager.language_changed.connect(self.update_texts) - self.update_texts() + # Nur echte LanguageManager haben language_changed Signal + if hasattr(self.language_manager, 'language_changed'): + self.language_manager.language_changed.connect(self.update_texts) + self.update_texts() def init_ui(self): """Initialisiert die Benutzeroberfläche.""" diff --git a/views/tabs/generator_tab_factory.py b/views/tabs/generator_tab_factory.py new file mode 100644 index 0000000..7e68eb9 --- /dev/null +++ b/views/tabs/generator_tab_factory.py @@ -0,0 +1,185 @@ +""" +Factory Pattern für die Erstellung plattform-spezifischer Generator-Tabs. +Clean Architecture Implementation für erweiterbare Tab-Generierung. +""" + +import logging +from typing import Optional, Dict, Type +from PyQt5.QtWidgets import QWidget + +# Base imports +from views.tabs.generator_tab import GeneratorTab + +logger = logging.getLogger("generator_tab_factory") + + +class GeneratorTabFactory: + """ + Factory-Klasse zur Erstellung plattform-spezifischer Generator-Tabs. + + Diese Factory verwendet das Strategy Pattern intern, um verschiedene + Tab-Implementierungen basierend auf der Plattform zu erstellen. + Neue Plattformen können einfach durch Hinzufügen zur Registry erweitert werden. + """ + + # Registry für plattform-spezifische Tabs + # Key: Plattform-Name (lowercase), Value: Tab-Klasse + _tab_registry: Dict[str, Type[QWidget]] = {} + + @classmethod + def register_tab(cls, platform: str, tab_class: Type[QWidget]) -> None: + """ + Registriert eine Tab-Klasse für eine bestimmte Plattform. + + Args: + platform: Name der Plattform (wird zu lowercase konvertiert) + tab_class: Die Tab-Klasse für diese Plattform + """ + platform_lower = platform.lower() + cls._tab_registry[platform_lower] = tab_class + logger.info(f"Tab-Klasse {tab_class.__name__} für Plattform '{platform}' registriert") + + @classmethod + def create_tab(cls, platform: str, language_manager: Optional[object] = None) -> QWidget: + """ + Erstellt einen Generator-Tab für die angegebene Plattform. + + Diese Methode verwendet Lazy Loading für plattform-spezifische Tabs, + um zirkuläre Imports zu vermeiden und die Performance zu optimieren. + + Args: + platform: Name der Plattform + language_manager: Optionaler Language Manager für Internationalisierung + + Returns: + QWidget: Der erstellte Generator-Tab + + Raises: + ValueError: Wenn die Plattform nicht unterstützt wird + """ + platform_lower = platform.lower() + + # Lazy Loading und Registrierung für bekannte Plattformen + if platform_lower not in cls._tab_registry: + cls._lazy_load_platform_tab(platform_lower) + + # Tab-Klasse aus Registry holen + tab_class = cls._tab_registry.get(platform_lower) + + if tab_class: + logger.info(f"Erstelle {tab_class.__name__} für Plattform '{platform}'") + try: + return tab_class(platform, language_manager) + except Exception as e: + logger.error(f"Fehler beim Erstellen des Tabs für '{platform}': {e}") + logger.info(f"Fallback auf generischen GeneratorTab für '{platform}'") + return GeneratorTab(platform, language_manager) + else: + # Fallback auf generischen Tab + logger.info(f"Verwende generischen GeneratorTab für Plattform '{platform}'") + return GeneratorTab(platform, language_manager) + + @classmethod + def _lazy_load_platform_tab(cls, platform: str) -> None: + """ + Lädt plattform-spezifische Tab-Klassen lazy (bei Bedarf). + + Dies vermeidet zirkuläre Imports und verbessert die Startzeit, + indem nur benötigte Module geladen werden. + + Args: + platform: Name der Plattform (lowercase) + """ + try: + if platform == "facebook": + from views.tabs.facebook_generator_tab import FacebookGeneratorTab + cls.register_tab("facebook", FacebookGeneratorTab) + + elif platform == "instagram": + # Instagram verwendet möglicherweise einen spezifischen Tab + # Vorerst den generischen Tab verwenden + cls.register_tab("instagram", GeneratorTab) + + elif platform == "tiktok": + # TikTok könnte einen eigenen Tab haben + # Prüfe ob TikTokGeneratorTab existiert + try: + from views.tabs.tiktok_generator_tab import TikTokGeneratorTab + cls.register_tab("tiktok", TikTokGeneratorTab) + except ImportError: + # Fallback auf generischen Tab + cls.register_tab("tiktok", GeneratorTab) + + elif platform == "x": + # X (Twitter) Tab + try: + from views.tabs.x_generator_tab import XGeneratorTab + cls.register_tab("x", XGeneratorTab) + except ImportError: + cls.register_tab("x", GeneratorTab) + + else: + # Unbekannte Plattform - verwende generischen Tab + cls.register_tab(platform, GeneratorTab) + + except ImportError as e: + logger.warning(f"Konnte spezifischen Tab für '{platform}' nicht laden: {e}") + # Registriere generischen Tab als Fallback + cls.register_tab(platform, GeneratorTab) + + @classmethod + def get_supported_platforms(cls) -> list: + """ + Gibt eine Liste der unterstützten Plattformen zurück. + + Returns: + list: Liste der Plattform-Namen + """ + # Bekannte Plattformen, die lazy geladen werden können + known_platforms = ["facebook", "instagram", "tiktok", "x"] + + # Bereits registrierte Plattformen hinzufügen + registered = list(cls._tab_registry.keys()) + + # Kombiniere und entferne Duplikate + all_platforms = list(set(known_platforms + registered)) + all_platforms.sort() + + return all_platforms + + @classmethod + def is_platform_supported(cls, platform: str) -> bool: + """ + Prüft, ob eine Plattform unterstützt wird. + + Args: + platform: Name der Plattform + + Returns: + bool: True wenn unterstützt, sonst False + """ + return platform.lower() in cls.get_supported_platforms() + + @classmethod + def clear_registry(cls) -> None: + """ + Löscht die Tab-Registry. + Hauptsächlich für Tests und Cleanup. + """ + cls._tab_registry.clear() + logger.debug("Tab-Registry gelöscht") + + +# Convenience-Funktion für direkten Import +def create_generator_tab(platform: str, language_manager: Optional[object] = None) -> QWidget: + """ + Convenience-Funktion zum Erstellen eines Generator-Tabs. + + Args: + platform: Name der Plattform + language_manager: Optionaler Language Manager + + Returns: + QWidget: Der erstellte Generator-Tab + """ + return GeneratorTabFactory.create_tab(platform, language_manager) \ No newline at end of file diff --git a/views/widgets/forge_animation_widget.py b/views/widgets/forge_animation_widget.py index c96943f..c161d4a 100644 --- a/views/widgets/forge_animation_widget.py +++ b/views/widgets/forge_animation_widget.py @@ -23,13 +23,13 @@ class ForgeAnimationDialog(QDialog): # Timer für das regelmäßige Nach-vorne-Holen self.raise_timer = QTimer() self.raise_timer.timeout.connect(self._raise_to_front) - self.raise_timer.setInterval(500) # Alle 500ms + self.raise_timer.setInterval(1000) # Alle 1000ms (weniger aggressiv) def init_ui(self): """Initialisiert die UI mit verbessertem Design""" - # Dialog-Fenster ohne Stay-on-Top, um Browser nicht zu blockieren - # Entfernt Qt.WindowStaysOnTopHint, da dies den Browser-Fokus stören kann - self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint) + # Dialog-Fenster mit Stay-on-Top für Sichtbarkeit + # WindowStaysOnTopHint sorgt dafür, dass die Warnung immer sichtbar bleibt + self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Nicht modal - blockiert nicht das Hauptfenster self.setFixedSize(650, 600) # Ursprüngliche Größe beibehalten @@ -187,8 +187,8 @@ class ForgeAnimationDialog(QDialog): def start_animation(self): """Zeigt den Dialog an""" self.status_label.setText("Initialisiere...") - # Timer deaktiviert - verhindert Fokus-Probleme mit dem Browser - # self.raise_timer.start() # Deaktiviert: Stört Browser-Interaktion + # Timer aktiviert als Fallback für Window-Manager die TopHint ignorieren + self.raise_timer.start() # Fallback: Hält Dialog zuverlässig im Vordergrund def stop_animation(self): """Stoppt die Animation und den Timer""" @@ -227,9 +227,9 @@ class ForgeAnimationDialog(QDialog): super().keyPressEvent(event) def _raise_to_front(self): - """Holt den Dialog in den Vordergrund""" + """Holt den Dialog in den Vordergrund ohne Fokus zu stehlen""" self.raise_() - # Nicht activateWindow() aufrufen - das holt das Hauptfenster mit + # Nicht activateWindow() aufrufen - das würde den Fokus vom Browser stehlen def show(self): """Überschreibt show() um den Dialog richtig zu positionieren"""