Initial commit
Dieser Commit ist enthalten in:
BIN
views/__pycache__/about_dialog.cpython-310.pyc
Normale Datei
BIN
views/__pycache__/about_dialog.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/__pycache__/main_window.cpython-310.pyc
Normale Datei
BIN
views/__pycache__/main_window.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/__pycache__/main_window.cpython-313.pyc
Normale Datei
BIN
views/__pycache__/main_window.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/__pycache__/platform_selector.cpython-310.pyc
Normale Datei
BIN
views/__pycache__/platform_selector.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/__pycache__/platform_selector.cpython-313.pyc
Normale Datei
BIN
views/__pycache__/platform_selector.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
68
views/about_dialog.py
Normale Datei
68
views/about_dialog.py
Normale Datei
@ -0,0 +1,68 @@
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
from updates.version import get_version
|
||||
|
||||
|
||||
class AboutDialog(QDialog):
|
||||
"""Dialog that shows information about the application."""
|
||||
|
||||
def __init__(self, language_manager=None, parent=None):
|
||||
super().__init__(parent)
|
||||
# Remove the standard "?" help button that appears on some platforms
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self.language_manager = language_manager
|
||||
self._setup_ui()
|
||||
if self.language_manager:
|
||||
self.language_manager.language_changed.connect(self.update_texts)
|
||||
self.update_texts()
|
||||
|
||||
def _setup_ui(self):
|
||||
self.setWindowTitle("About")
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
self.info_label = QLabel()
|
||||
self.info_label.setAlignment(Qt.AlignCenter)
|
||||
self.info_label.setWordWrap(True)
|
||||
layout.addWidget(self.info_label)
|
||||
|
||||
self.close_button = QPushButton("OK")
|
||||
self.close_button.clicked.connect(self.accept)
|
||||
layout.addWidget(self.close_button, alignment=Qt.AlignCenter)
|
||||
|
||||
def update_texts(self):
|
||||
version_text = (
|
||||
self.language_manager.get_text("main.version", f"Version {get_version()}")
|
||||
if self.language_manager
|
||||
else f"Version {get_version()}"
|
||||
)
|
||||
lm = self.language_manager
|
||||
title = "Social Media Account Generator" if not lm else lm.get_text("main.title", "Social Media Account Generator")
|
||||
support = (
|
||||
lm.get_text(
|
||||
"about_dialog.support",
|
||||
"Für Support kontaktieren Sie uns unter: support@example.com",
|
||||
)
|
||||
if lm
|
||||
else "Für Support kontaktieren Sie uns unter: support@example.com"
|
||||
)
|
||||
license_text = (
|
||||
lm.get_text(
|
||||
"about_dialog.license",
|
||||
"Diese Software ist lizenzpflichtig und darf nur mit gültiger Lizenz verwendet werden.",
|
||||
)
|
||||
if lm
|
||||
else "Diese Software ist lizenzpflichtig und darf nur mit gültiger Lizenz verwendet werden."
|
||||
)
|
||||
lines = [
|
||||
f"<h1>{title}</h1>",
|
||||
f"<p>{version_text}</p>",
|
||||
"<p>© 2025 Chimaira</p>",
|
||||
f"<p>{support}</p>",
|
||||
f"<p>{license_text}</p>",
|
||||
]
|
||||
self.info_label.setText("".join(lines))
|
||||
if lm:
|
||||
self.setWindowTitle(lm.get_text("menu.about", "Über"))
|
||||
self.close_button.setText(lm.get_text("buttons.ok", "OK"))
|
||||
|
||||
235
views/main_window.py
Normale Datei
235
views/main_window.py
Normale Datei
@ -0,0 +1,235 @@
|
||||
# Path: views/main_window.py
|
||||
|
||||
"""
|
||||
Hauptfenster der Social Media Account Generator 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
|
||||
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()
|
||||
theme_toggled = 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("Social Media Account Generator")
|
||||
# Größere Mindest- und Startgröße, damit Plattformnamen
|
||||
# (z.B. "Twitter" und "VK") nicht abgeschnitten werden und
|
||||
# Tabelleninhalte genügend Platz haben
|
||||
self.setMinimumSize(1200, 700)
|
||||
self.resize(1200, 700)
|
||||
|
||||
# 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(0, 0, 0, 0)
|
||||
|
||||
# Zurück-Button
|
||||
self.back_button = QPushButton("↩ Zurück")
|
||||
self.back_button.setFixedWidth(100)
|
||||
self.header_layout.addWidget(self.back_button)
|
||||
|
||||
# Plattform-Titel
|
||||
self.platform_title = QLabel()
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(14)
|
||||
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.setFixedWidth(100)
|
||||
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
|
||||
gen_text = self.language_manager.get_text("tabs.generator", "Account Generator") if self.language_manager else "Account Generator"
|
||||
self.platform_title.setText(f"{platform.title()} {gen_text}")
|
||||
|
||||
# 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()} {gen_text}")
|
||||
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 für "Über" und Sprachen."""
|
||||
|
||||
# "Über"-Aktion
|
||||
self.about_action = QAction(self.language_manager.get_text("menu.about", "Über"), self)
|
||||
self.menuBar().addAction(self.about_action)
|
||||
self.about_action.triggered.connect(self._show_about_dialog)
|
||||
|
||||
|
||||
def _show_about_dialog(self):
|
||||
"""Öffnet den Über-Dialog."""
|
||||
dialog = AboutDialog(self.language_manager, self)
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
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", "Social Media Account Generator"))
|
||||
|
||||
# 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"))
|
||||
|
||||
# Menüs aktualisieren
|
||||
self.about_action.setText(self.language_manager.get_text("menu.about", "Über"))
|
||||
|
||||
# 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().split()[0].lower() if self.platform_title.text() else None
|
||||
if current_platform:
|
||||
gen_text = self.language_manager.get_text("tabs.generator", "Account Generator")
|
||||
self.platform_title.setText(f"{current_platform.title()} {gen_text}")
|
||||
|
||||
# Tabs aktualisieren
|
||||
tab_names = {
|
||||
0: self.language_manager.get_text("tabs.generator", "Account Generator"),
|
||||
1: self.language_manager.get_text("tabs.settings", "Einstellungen")
|
||||
}
|
||||
|
||||
for i in range(self.tabs.count()):
|
||||
self.tabs.setTabText(i, tab_names.get(i, self.tabs.tabText(i)))
|
||||
|
||||
# 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()
|
||||
gen_text = self.language_manager.get_text("tabs.generator", "Account Generator") if self.language_manager else "Account Generator"
|
||||
self.tabs.addTab(generator_tab, gen_text)
|
||||
|
||||
# Einstellungen-Tab
|
||||
if hasattr(platform_controller, "get_settings_tab"):
|
||||
settings_tab = platform_controller.get_settings_tab()
|
||||
settings_text = self.language_manager.get_text("tabs.settings", "Einstellungen") if self.language_manager else "Einstellungen"
|
||||
self.tabs.addTab(settings_tab, settings_text)
|
||||
|
||||
|
||||
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("Social Media Account Generator")
|
||||
|
||||
# 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)
|
||||
148
views/platform_selector.py
Normale Datei
148
views/platform_selector.py
Normale Datei
@ -0,0 +1,148 @@
|
||||
# Path: views/platform_selector.py
|
||||
|
||||
"""
|
||||
Plattformauswahl-Widget für die Social Media Account Generator Anwendung.
|
||||
"""
|
||||
|
||||
import os
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGridLayout, QLabel,
|
||||
QHBoxLayout
|
||||
)
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, QSize
|
||||
from PyQt5.QtGui import QFont
|
||||
|
||||
from views.widgets.platform_button import PlatformButton
|
||||
from views.widgets.language_dropdown import LanguageDropdown
|
||||
from views.tabs.accounts_tab import AccountsTab
|
||||
|
||||
class PlatformSelector(QWidget):
|
||||
"""Widget zur Auswahl der Plattform."""
|
||||
|
||||
# Signal wird ausgelöst, wenn eine Plattform ausgewählt wird
|
||||
platform_selected = pyqtSignal(str)
|
||||
|
||||
def __init__(self, language_manager=None, db_manager=None):
|
||||
super().__init__()
|
||||
self.language_manager = language_manager
|
||||
self.db_manager = db_manager
|
||||
self.init_ui()
|
||||
if self.language_manager:
|
||||
self.language_manager.language_changed.connect(self.update_texts)
|
||||
self.update_texts()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die Benutzeroberfläche."""
|
||||
main_layout = QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
# -----------------------------
|
||||
# Linke Seite: Plattformwahl
|
||||
# -----------------------------
|
||||
left_widget = QWidget()
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
|
||||
|
||||
self.title_label = QLabel("Social Media Account Generator")
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
title_font = QFont()
|
||||
title_font.setPointSize(18)
|
||||
title_font.setBold(True)
|
||||
self.title_label.setFont(title_font)
|
||||
left_layout.addWidget(self.title_label)
|
||||
|
||||
self.subtitle_label = QLabel("Wählen Sie eine Plattform")
|
||||
self.subtitle_label.setAlignment(Qt.AlignCenter)
|
||||
subtitle_font = QFont()
|
||||
subtitle_font.setPointSize(12)
|
||||
self.subtitle_label.setFont(subtitle_font)
|
||||
left_layout.addWidget(self.subtitle_label)
|
||||
|
||||
platforms_container = QWidget()
|
||||
grid_layout = QGridLayout(platforms_container)
|
||||
grid_layout.setSpacing(40)
|
||||
|
||||
# Definiere verfügbare Plattformen
|
||||
platforms = [
|
||||
{"name": "Instagram", "enabled": True},
|
||||
{"name": "Facebook", "enabled": True},
|
||||
{"name": "TikTok", "enabled": True},
|
||||
{"name": "Twitter", "enabled": True},
|
||||
{"name": "VK", "enabled": True}
|
||||
]
|
||||
|
||||
# Relativer Pfad zu den Icons - vom aktuellen Verzeichnis aus
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
icons_dir = os.path.join(parent_dir, "resources", "icons")
|
||||
|
||||
# Platziere Buttons in einem 2x3 Grid
|
||||
for i, platform in enumerate(platforms):
|
||||
row = i // 3
|
||||
col = i % 3
|
||||
|
||||
# Icon-Pfad erstellen
|
||||
icon_path = os.path.join(icons_dir, f"{platform['name'].lower()}.svg")
|
||||
|
||||
# Verwende das Icon nur, wenn die Datei existiert
|
||||
if not os.path.exists(icon_path):
|
||||
icon_path = None
|
||||
|
||||
button = PlatformButton(
|
||||
platform["name"],
|
||||
icon_path,
|
||||
platform["enabled"]
|
||||
)
|
||||
button.clicked.connect(lambda checked=False, p=platform["name"]: self.platform_selected.emit(p.lower()))
|
||||
grid_layout.addWidget(button, row, col, Qt.AlignCenter)
|
||||
|
||||
left_layout.addWidget(platforms_container)
|
||||
left_layout.addStretch()
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Rechte Seite: Übersicht
|
||||
# -----------------------------
|
||||
right_widget = QWidget()
|
||||
right_layout = QVBoxLayout(right_widget)
|
||||
|
||||
header_right_layout = QHBoxLayout()
|
||||
header_right_layout.addStretch()
|
||||
if self.language_manager:
|
||||
self.language_dropdown = LanguageDropdown(self.language_manager)
|
||||
header_right_layout.addWidget(self.language_dropdown)
|
||||
right_layout.addLayout(header_right_layout)
|
||||
|
||||
self.overview_label = QLabel("Übersicht")
|
||||
self.overview_label.setAlignment(Qt.AlignCenter)
|
||||
ov_font = QFont()
|
||||
ov_font.setPointSize(14)
|
||||
ov_font.setBold(True)
|
||||
self.overview_label.setFont(ov_font)
|
||||
right_layout.addWidget(self.overview_label)
|
||||
|
||||
self.accounts_tab = AccountsTab(None, self.db_manager, self.language_manager)
|
||||
right_layout.addWidget(self.accounts_tab)
|
||||
|
||||
main_layout.addWidget(left_widget)
|
||||
main_layout.addWidget(right_widget)
|
||||
|
||||
def load_accounts(self):
|
||||
"""Lädt die Konten in der Übersicht neu."""
|
||||
if hasattr(self, "accounts_tab"):
|
||||
self.accounts_tab.load_accounts()
|
||||
|
||||
def update_texts(self):
|
||||
"""Aktualisiert die Texte gemäß der aktuellen Sprache."""
|
||||
if not self.language_manager:
|
||||
return
|
||||
|
||||
self.title_label.setText(
|
||||
self.language_manager.get_text("main.title", "Social Media Account Generator")
|
||||
)
|
||||
self.subtitle_label.setText(
|
||||
self.language_manager.get_text("main.subtitle", "Wählen Sie eine Plattform")
|
||||
)
|
||||
self.overview_label.setText(
|
||||
self.language_manager.get_text("main.overview", "Übersicht")
|
||||
)
|
||||
BIN
views/tabs/__pycache__/accounts_tab.cpython-310.pyc
Normale Datei
BIN
views/tabs/__pycache__/accounts_tab.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/tabs/__pycache__/accounts_tab.cpython-313.pyc
Normale Datei
BIN
views/tabs/__pycache__/accounts_tab.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/tabs/__pycache__/generator_tab.cpython-310.pyc
Normale Datei
BIN
views/tabs/__pycache__/generator_tab.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/tabs/__pycache__/generator_tab.cpython-313.pyc
Normale Datei
BIN
views/tabs/__pycache__/generator_tab.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/tabs/__pycache__/settings_tab.cpython-310.pyc
Normale Datei
BIN
views/tabs/__pycache__/settings_tab.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/tabs/__pycache__/settings_tab.cpython-313.pyc
Normale Datei
BIN
views/tabs/__pycache__/settings_tab.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
207
views/tabs/accounts_tab.py
Normale Datei
207
views/tabs/accounts_tab.py
Normale Datei
@ -0,0 +1,207 @@
|
||||
"""
|
||||
Tab zur Verwaltung der erstellten Social-Media-Accounts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QTableWidget,
|
||||
QTableWidgetItem, QPushButton, QHeaderView, QMessageBox
|
||||
)
|
||||
from PyQt5.QtCore import pyqtSignal, Qt
|
||||
|
||||
logger = logging.getLogger("accounts_tab")
|
||||
|
||||
class AccountsTab(QWidget):
|
||||
"""Widget für den Konten-Tab."""
|
||||
|
||||
# Signale
|
||||
refresh_requested = pyqtSignal()
|
||||
export_requested = pyqtSignal()
|
||||
delete_requested = pyqtSignal(int) # account_id
|
||||
|
||||
def __init__(self, platform_name=None, db_manager=None, language_manager=None):
|
||||
super().__init__()
|
||||
self.platform_name = platform_name
|
||||
self.db_manager = db_manager
|
||||
self.language_manager = language_manager
|
||||
self.init_ui()
|
||||
if self.language_manager:
|
||||
self.language_manager.language_changed.connect(self.update_texts)
|
||||
self.update_texts()
|
||||
|
||||
# Konten laden, falls db_manager vorhanden
|
||||
if self.db_manager:
|
||||
self.load_accounts()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die Benutzeroberfläche."""
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Konten-Tabelle
|
||||
self.accounts_table = QTableWidget()
|
||||
self.accounts_table.setColumnCount(8)
|
||||
self.accounts_table.setHorizontalHeaderLabels([
|
||||
"ID",
|
||||
"Benutzername",
|
||||
"Passwort",
|
||||
"E-Mail",
|
||||
"Handynummer",
|
||||
"Name",
|
||||
"Plattform",
|
||||
"Erstellt am",
|
||||
])
|
||||
self.accounts_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
# ID-Spalte verstecken
|
||||
self.accounts_table.setColumnHidden(0, True)
|
||||
|
||||
layout.addWidget(self.accounts_table)
|
||||
|
||||
# Button-Leiste
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
self.refresh_button = QPushButton("Aktualisieren")
|
||||
self.refresh_button.clicked.connect(self.on_refresh_clicked)
|
||||
|
||||
self.export_button = QPushButton("Exportieren")
|
||||
self.export_button.clicked.connect(self.on_export_clicked)
|
||||
|
||||
self.delete_button = QPushButton("Löschen")
|
||||
self.delete_button.clicked.connect(self.on_delete_clicked)
|
||||
|
||||
button_layout.addWidget(self.refresh_button)
|
||||
button_layout.addWidget(self.export_button)
|
||||
button_layout.addWidget(self.delete_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
def load_accounts(self):
|
||||
"""Lädt Konten aus der Datenbank und zeigt sie in der Tabelle an."""
|
||||
try:
|
||||
if (self.platform_name and str(self.platform_name).lower() not in ["all", ""]
|
||||
and hasattr(self.db_manager, "get_accounts_by_platform")):
|
||||
accounts = self.db_manager.get_accounts_by_platform(self.platform_name.lower())
|
||||
else:
|
||||
accounts = self.db_manager.get_all_accounts()
|
||||
if self.platform_name and str(self.platform_name).lower() not in ["all", ""]:
|
||||
accounts = [
|
||||
acc for acc in accounts
|
||||
if acc.get("platform", "").lower() == str(self.platform_name).lower()
|
||||
]
|
||||
|
||||
self.display_accounts(accounts)
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Konten: {e}")
|
||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Laden der Konten:\n{str(e)}")
|
||||
|
||||
def display_accounts(self, accounts):
|
||||
"""Zeigt die Konten in der Tabelle an."""
|
||||
self.accounts_table.setRowCount(len(accounts))
|
||||
|
||||
for row, account in enumerate(accounts):
|
||||
self.accounts_table.setItem(row, 0, QTableWidgetItem(str(account.get("id", ""))))
|
||||
self.accounts_table.setItem(row, 1, QTableWidgetItem(account.get("username", "")))
|
||||
self.accounts_table.setItem(row, 2, QTableWidgetItem(account.get("password", "")))
|
||||
self.accounts_table.setItem(row, 3, QTableWidgetItem(account.get("email", "")))
|
||||
self.accounts_table.setItem(row, 4, QTableWidgetItem(account.get("phone", "")))
|
||||
self.accounts_table.setItem(row, 5, QTableWidgetItem(account.get("full_name", "")))
|
||||
self.accounts_table.setItem(row, 6, QTableWidgetItem(account.get("platform", "")))
|
||||
self.accounts_table.setItem(row, 7, QTableWidgetItem(account.get("created_at", "")))
|
||||
|
||||
def on_refresh_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Aktualisieren-Button geklickt wird."""
|
||||
self.refresh_requested.emit()
|
||||
|
||||
# Direkt aktualisieren, falls db_manager vorhanden
|
||||
if self.db_manager:
|
||||
self.load_accounts()
|
||||
|
||||
def on_export_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Exportieren-Button geklickt wird."""
|
||||
self.export_requested.emit()
|
||||
|
||||
def on_delete_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Löschen-Button geklickt wird."""
|
||||
selected_rows = self.accounts_table.selectionModel().selectedRows()
|
||||
if not selected_rows:
|
||||
title = "Kein Konto ausgewählt"
|
||||
text = "Bitte wählen Sie ein Konto zum Löschen aus."
|
||||
if self.language_manager:
|
||||
title = self.language_manager.get_text(
|
||||
"accounts_tab.no_selection_title", title
|
||||
)
|
||||
text = self.language_manager.get_text(
|
||||
"accounts_tab.no_selection_text", text
|
||||
)
|
||||
QMessageBox.warning(self, title, text)
|
||||
return
|
||||
|
||||
account_id = int(self.accounts_table.item(selected_rows[0].row(), 0).text())
|
||||
username = self.accounts_table.item(selected_rows[0].row(), 1).text()
|
||||
|
||||
q_title = "Konto löschen"
|
||||
q_text = f"Möchten Sie das Konto '{username}' wirklich löschen?"
|
||||
if self.language_manager:
|
||||
q_title = self.language_manager.get_text("accounts_tab.delete_title", q_title)
|
||||
q_text = self.language_manager.get_text(
|
||||
"accounts_tab.delete_text", q_text
|
||||
).format(username=username)
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
q_title,
|
||||
q_text,
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No,
|
||||
)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
self.delete_requested.emit(account_id)
|
||||
|
||||
# Direkt aktualisieren, falls db_manager vorhanden
|
||||
if self.db_manager:
|
||||
success = self.db_manager.delete_account(account_id)
|
||||
if success:
|
||||
self.load_accounts()
|
||||
suc_title = "Erfolg"
|
||||
suc_text = f"Konto '{username}' wurde gelöscht."
|
||||
if self.language_manager:
|
||||
suc_title = self.language_manager.get_text(
|
||||
"accounts_tab.delete_success_title", suc_title
|
||||
)
|
||||
suc_text = self.language_manager.get_text(
|
||||
"accounts_tab.delete_success_text", suc_text
|
||||
).format(username=username)
|
||||
QMessageBox.information(self, suc_title, suc_text)
|
||||
else:
|
||||
err_title = "Fehler"
|
||||
err_text = f"Konto '{username}' konnte nicht gelöscht werden."
|
||||
if self.language_manager:
|
||||
err_title = self.language_manager.get_text(
|
||||
"accounts_tab.delete_error_title", err_title
|
||||
)
|
||||
err_text = self.language_manager.get_text(
|
||||
"accounts_tab.delete_error_text", err_text
|
||||
).format(username=username)
|
||||
QMessageBox.critical(self, err_title, err_text)
|
||||
|
||||
def update_texts(self):
|
||||
"""Aktualisiert UI-Texte gemäß aktueller Sprache."""
|
||||
if not self.language_manager:
|
||||
return
|
||||
lm = self.language_manager
|
||||
self.refresh_button.setText(lm.get_text("buttons.refresh", "Aktualisieren"))
|
||||
self.export_button.setText(lm.get_text("buttons.export", "Exportieren"))
|
||||
self.delete_button.setText(lm.get_text("buttons.delete", "Löschen"))
|
||||
headers = lm.get_text(
|
||||
"accounts_tab.headers",
|
||||
[
|
||||
"ID",
|
||||
"Benutzername",
|
||||
"Passwort",
|
||||
"E-Mail",
|
||||
"Handynummer",
|
||||
"Name",
|
||||
"Plattform",
|
||||
"Erstellt am",
|
||||
],
|
||||
)
|
||||
self.accounts_table.setHorizontalHeaderLabels(headers)
|
||||
500
views/tabs/generator_tab.py
Normale Datei
500
views/tabs/generator_tab.py
Normale Datei
@ -0,0 +1,500 @@
|
||||
# Pfad: views/tabs/generator_tab.py
|
||||
|
||||
"""
|
||||
Tab zur Erstellung von Social-Media-Accounts.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QFormLayout,
|
||||
QGroupBox, QLabel, QLineEdit, QSpinBox, QRadioButton,
|
||||
QCheckBox, QComboBox, QPushButton, QTextEdit, QProgressBar,
|
||||
QMessageBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
|
||||
from utils.logger import add_gui_handler
|
||||
|
||||
logger = logging.getLogger("generator_tab")
|
||||
|
||||
class GeneratorTab(QWidget):
|
||||
"""Widget für den Account-Generator-Tab."""
|
||||
|
||||
# Signale
|
||||
start_requested = pyqtSignal(dict)
|
||||
stop_requested = pyqtSignal()
|
||||
account_created = pyqtSignal(str, dict) # (platform, account_data)
|
||||
|
||||
def __init__(self, platform_name, language_manager=None):
|
||||
super().__init__()
|
||||
self.platform_name = platform_name
|
||||
self.language_manager = language_manager
|
||||
self.init_ui()
|
||||
if self.language_manager:
|
||||
self.language_manager.language_changed.connect(self.update_texts)
|
||||
self.update_texts()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die Benutzeroberfläche."""
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Formularbereich
|
||||
self.form_group = QGroupBox()
|
||||
form_layout = QFormLayout()
|
||||
self.form_group.setLayout(form_layout)
|
||||
|
||||
# Vorname und Nachname statt vollständigem Namen
|
||||
self.first_name_label = QLabel()
|
||||
self.first_name_input = QLineEdit()
|
||||
form_layout.addRow(self.first_name_label, self.first_name_input)
|
||||
|
||||
self.last_name_label = QLabel()
|
||||
self.last_name_input = QLineEdit()
|
||||
form_layout.addRow(self.last_name_label, self.last_name_input)
|
||||
|
||||
# Alter (jetzt mit QLineEdit statt QSpinBox)
|
||||
self.age_label = QLabel()
|
||||
self.age_input = QLineEdit()
|
||||
form_layout.addRow(self.age_label, self.age_input)
|
||||
|
||||
# Registrierungsmethode
|
||||
self.reg_method_group = QWidget()
|
||||
reg_method_layout = QHBoxLayout(self.reg_method_group)
|
||||
reg_method_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.email_radio = QRadioButton()
|
||||
self.phone_radio = QRadioButton()
|
||||
self.email_radio.setChecked(True)
|
||||
|
||||
reg_method_layout.addWidget(self.email_radio)
|
||||
reg_method_layout.addWidget(self.phone_radio)
|
||||
|
||||
self.reg_method_label = QLabel()
|
||||
form_layout.addRow(self.reg_method_label, self.reg_method_group)
|
||||
|
||||
# Telefonnummer (nur sichtbar, wenn Telefon ausgewählt)
|
||||
self.phone_label = QLabel()
|
||||
self.phone_input = QLineEdit()
|
||||
self.phone_input.setEnabled(False)
|
||||
form_layout.addRow(self.phone_label, self.phone_input)
|
||||
|
||||
# E-Mail-Domain
|
||||
self.email_domain_label = QLabel()
|
||||
self.email_domain_input = QLineEdit("z5m7q9dk3ah2v1plx6ju.com")
|
||||
form_layout.addRow(self.email_domain_label, self.email_domain_input)
|
||||
|
||||
# Proxy verwenden
|
||||
self.use_proxy_check = QCheckBox()
|
||||
self.use_proxy_check.setChecked(True)
|
||||
|
||||
# Proxy-Typ
|
||||
self.proxy_type_combo = QComboBox()
|
||||
|
||||
proxy_widget = QWidget()
|
||||
proxy_layout = QHBoxLayout(proxy_widget)
|
||||
proxy_layout.setContentsMargins(0, 0, 0, 0)
|
||||
proxy_layout.addWidget(self.use_proxy_check)
|
||||
self.proxy_type_label = QLabel()
|
||||
proxy_layout.addWidget(self.proxy_type_label)
|
||||
proxy_layout.addWidget(self.proxy_type_combo)
|
||||
proxy_layout.addStretch()
|
||||
|
||||
self.proxy_label = QLabel()
|
||||
form_layout.addRow(self.proxy_label, proxy_widget)
|
||||
|
||||
# Headless-Modus
|
||||
self.headless_check = QCheckBox()
|
||||
form_layout.addRow("", self.headless_check)
|
||||
|
||||
# Debug-Modus
|
||||
self.debug_check = QCheckBox()
|
||||
form_layout.addRow("", self.debug_check)
|
||||
|
||||
# Plattformspezifische Parameter hinzufügen
|
||||
self.add_platform_specific_fields(form_layout)
|
||||
|
||||
# Formular zum Layout hinzufügen
|
||||
layout.addWidget(self.form_group)
|
||||
|
||||
# Fortschrittsanzeige
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setRange(0, 100)
|
||||
self.progress_bar.setValue(0)
|
||||
layout.addWidget(self.progress_bar)
|
||||
|
||||
# Log-Bereich
|
||||
self.log_group = QGroupBox()
|
||||
log_layout = QVBoxLayout(self.log_group)
|
||||
|
||||
self.log_text = QTextEdit()
|
||||
self.log_text.setReadOnly(True)
|
||||
log_layout.addWidget(self.log_text)
|
||||
|
||||
# Log-Handler für das TextEdit
|
||||
add_gui_handler(logger, self.log_text)
|
||||
|
||||
layout.addWidget(self.log_group)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
self.start_button = QPushButton()
|
||||
self.start_button.clicked.connect(self.on_start_clicked)
|
||||
|
||||
self.stop_button = QPushButton()
|
||||
self.stop_button.clicked.connect(self.on_stop_clicked)
|
||||
self.stop_button.setEnabled(False)
|
||||
|
||||
button_layout.addWidget(self.start_button)
|
||||
button_layout.addWidget(self.stop_button)
|
||||
|
||||
layout.addLayout(button_layout)
|
||||
|
||||
# Event-Verbindungen
|
||||
self.email_radio.toggled.connect(self.toggle_phone_input)
|
||||
self.phone_radio.toggled.connect(self.toggle_phone_input)
|
||||
self.use_proxy_check.toggled.connect(self.toggle_proxy_combo)
|
||||
|
||||
def add_platform_specific_fields(self, form_layout):
|
||||
"""
|
||||
Fügt plattformspezifische Felder hinzu.
|
||||
Diese Methode kann in abgeleiteten Klassen überschrieben werden.
|
||||
|
||||
Args:
|
||||
form_layout: Das Formular-Layout, zu dem die Felder hinzugefügt werden sollen
|
||||
"""
|
||||
# In plattformspezifischen Unterklassen überschreiben
|
||||
platform = self.platform_name.lower()
|
||||
|
||||
if platform == "tiktok":
|
||||
# Beispiel: Kategorie/Nische für TikTok
|
||||
self.category_label = QLabel()
|
||||
self.category_combo = QComboBox()
|
||||
|
||||
categories = [
|
||||
"Allgemein",
|
||||
"Gaming",
|
||||
"Mode",
|
||||
"Fitness",
|
||||
"Reisen",
|
||||
"Kochen",
|
||||
"Technologie",
|
||||
"Bildung",
|
||||
]
|
||||
|
||||
if self.language_manager:
|
||||
categories = [
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_general", "Allgemein"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_gaming", "Gaming"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_fashion", "Mode"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_fitness", "Fitness"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_travel", "Reisen"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_cooking", "Kochen"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_technology", "Technologie"
|
||||
),
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_education", "Bildung"
|
||||
),
|
||||
]
|
||||
|
||||
self.category_label.setText(
|
||||
self.language_manager.get_text(
|
||||
"generator_tab.tiktok_category_label", "Kategorie/Nische:"
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.category_label.setText("Kategorie/Nische:")
|
||||
|
||||
self.category_combo.addItems(categories)
|
||||
form_layout.addRow(self.category_label, self.category_combo)
|
||||
|
||||
elif platform == "twitter":
|
||||
# Beispiel: Interessen für Twitter
|
||||
self.interests_combo = QComboBox()
|
||||
interests = ["Allgemein", "Politik", "Technologie", "Wirtschaft", "Sport", "Unterhaltung", "Wissenschaft"]
|
||||
self.interests_combo.addItems(interests)
|
||||
form_layout.addRow("Interessen:", self.interests_combo)
|
||||
|
||||
elif platform == "facebook":
|
||||
# Beispiel: Datenschutzeinstellungen für Facebook
|
||||
self.privacy_combo = QComboBox()
|
||||
privacy_options = ["Öffentlich", "Nur Freunde", "Freunde außer...", "Nur ich"]
|
||||
self.privacy_combo.addItems(privacy_options)
|
||||
form_layout.addRow("Datenschutz:", self.privacy_combo)
|
||||
|
||||
def toggle_phone_input(self):
|
||||
"""Aktiviert/Deaktiviert das Telefoneingabefeld basierend auf der Radiobutton-Auswahl."""
|
||||
self.phone_input.setEnabled(self.phone_radio.isChecked())
|
||||
|
||||
def toggle_proxy_combo(self):
|
||||
"""Aktiviert/Deaktiviert die Proxy-Typ-Combobox basierend auf der Checkbox."""
|
||||
self.proxy_type_combo.setEnabled(self.use_proxy_check.isChecked())
|
||||
|
||||
def on_start_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Start-Button geklickt wird."""
|
||||
# Parameter sammeln
|
||||
params = self.get_params()
|
||||
|
||||
# Eingaben validieren
|
||||
valid, error_msg = self.validate_inputs(params)
|
||||
if not valid:
|
||||
self.show_error(error_msg)
|
||||
return
|
||||
|
||||
# Signal auslösen
|
||||
self.start_requested.emit(params)
|
||||
|
||||
def on_stop_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Stop-Button geklickt wird."""
|
||||
self.stop_requested.emit()
|
||||
|
||||
def get_params(self):
|
||||
"""Sammelt alle Parameter für die Account-Erstellung."""
|
||||
# Vorname und Nachname kombinieren für den vollständigen Namen
|
||||
first_name = self.first_name_input.text().strip()
|
||||
last_name = self.last_name_input.text().strip()
|
||||
full_name = f"{first_name} {last_name}"
|
||||
|
||||
params = {
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
"full_name": full_name,
|
||||
"age_text": self.age_input.text().strip(), # Speichere den Rohtext
|
||||
"registration_method": "email" if self.email_radio.isChecked() else "phone",
|
||||
"headless": self.headless_check.isChecked(),
|
||||
"debug": self.debug_check.isChecked(),
|
||||
"email_domain": self.email_domain_input.text().strip()
|
||||
}
|
||||
|
||||
# Telefonnummer (wenn ausgewählt)
|
||||
if self.phone_radio.isChecked():
|
||||
params["phone_number"] = self.phone_input.text().strip()
|
||||
|
||||
# Proxy (wenn aktiviert)
|
||||
if self.use_proxy_check.isChecked():
|
||||
proxy_type = self.proxy_type_combo.currentText().lower()
|
||||
params["use_proxy"] = True
|
||||
params["proxy_type"] = proxy_type
|
||||
else:
|
||||
params["use_proxy"] = False
|
||||
|
||||
# Plattformspezifische Parameter
|
||||
additional_params = self.get_platform_specific_params()
|
||||
if additional_params:
|
||||
params["additional_params"] = additional_params
|
||||
|
||||
return params
|
||||
|
||||
def validate_inputs(self, params):
|
||||
"""
|
||||
Validiert die Eingaben für die Account-Erstellung.
|
||||
|
||||
Args:
|
||||
params: Die gesammelten Parameter
|
||||
|
||||
Returns:
|
||||
tuple: (gültig, Fehlermeldung)
|
||||
"""
|
||||
# Namen prüfen
|
||||
if not params.get("first_name"):
|
||||
msg = "Bitte geben Sie einen Vornamen ein."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.first_name_error",
|
||||
"Bitte geben Sie einen Vornamen ein.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
if not params.get("last_name"):
|
||||
msg = "Bitte geben Sie einen Nachnamen ein."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.last_name_error",
|
||||
"Bitte geben Sie einen Nachnamen ein.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
# Alter prüfen
|
||||
age_text = params.get("age_text", "")
|
||||
if not age_text:
|
||||
msg = "Bitte geben Sie ein Alter ein."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.age_empty_error",
|
||||
"Bitte geben Sie ein Alter ein.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
# Alter muss eine Zahl sein
|
||||
try:
|
||||
age = int(age_text)
|
||||
params["age"] = age # Füge das konvertierte Alter zu den Parametern hinzu
|
||||
except ValueError:
|
||||
msg = "Das Alter muss eine ganze Zahl sein."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.age_int_error",
|
||||
"Das Alter muss eine ganze Zahl sein.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
# Alter-Bereich prüfen - hier ist die allgemeine Prüfung für 13-99
|
||||
# Die plattformspezifische Validierung kann später in den Controllern erfolgen
|
||||
if age < 13 or age > 99:
|
||||
msg = "Das Alter muss zwischen 13 und 99 liegen."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.age_range_error",
|
||||
"Das Alter muss zwischen 13 und 99 liegen.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
# Telefonnummer prüfen, falls erforderlich
|
||||
if params.get("registration_method") == "phone" and not params.get("phone_number"):
|
||||
msg = "Bitte geben Sie eine Telefonnummer ein."
|
||||
if self.language_manager:
|
||||
msg = self.language_manager.get_text(
|
||||
"generator_tab.phone_error",
|
||||
"Bitte geben Sie eine Telefonnummer ein.",
|
||||
)
|
||||
return False, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
def get_platform_specific_params(self):
|
||||
"""
|
||||
Gibt plattformspezifische Parameter zurück.
|
||||
Diese Methode kann in abgeleiteten Klassen überschrieben werden.
|
||||
|
||||
Returns:
|
||||
dict: Plattformspezifische Parameter
|
||||
"""
|
||||
# In plattformspezifischen Unterklassen überschreiben
|
||||
platform = self.platform_name.lower()
|
||||
additional_params = {}
|
||||
|
||||
if platform == "tiktok" and hasattr(self, "category_combo"):
|
||||
additional_params["category"] = self.category_combo.currentText()
|
||||
|
||||
elif platform == "twitter" and hasattr(self, "interests_combo"):
|
||||
additional_params["interests"] = self.interests_combo.currentText()
|
||||
|
||||
elif platform == "facebook" and hasattr(self, "privacy_combo"):
|
||||
additional_params["privacy"] = self.privacy_combo.currentText()
|
||||
|
||||
return additional_params
|
||||
|
||||
def set_running(self, running: bool):
|
||||
"""Setzt den Status auf 'Wird ausgeführt' oder 'Bereit'."""
|
||||
self.start_button.setEnabled(not running)
|
||||
self.stop_button.setEnabled(running)
|
||||
|
||||
def clear_log(self):
|
||||
"""Löscht den Log-Bereich."""
|
||||
self.log_text.clear()
|
||||
|
||||
def add_log(self, message: str):
|
||||
"""Fügt eine Nachricht zum Log-Bereich hinzu."""
|
||||
self.log_text.append(message)
|
||||
# Scrolle nach unten
|
||||
# Korrigierter Code für die Qt-Version
|
||||
try:
|
||||
self.log_text.moveCursor(Qt.MoveOperation.MoveEnd)
|
||||
except AttributeError:
|
||||
# Fallback für ältere Qt-Versionen
|
||||
try:
|
||||
self.log_text.moveCursor(Qt.MoveEnd)
|
||||
except AttributeError:
|
||||
# Weitere Fallbacks
|
||||
try:
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
self.log_text.moveCursor(QTextCursor.End)
|
||||
except:
|
||||
pass # Im Notfall einfach ignorieren
|
||||
|
||||
def set_progress(self, value: int):
|
||||
"""Setzt den Fortschritt der Fortschrittsanzeige."""
|
||||
self.progress_bar.setValue(value)
|
||||
|
||||
def set_status(self, message: str):
|
||||
"""Setzt die Statusnachricht."""
|
||||
# Diese Methode könnte später um eine Statusleiste erweitert werden
|
||||
self.add_log(message)
|
||||
|
||||
def show_error(self, message: str):
|
||||
"""Zeigt eine Fehlermeldung an."""
|
||||
title = "Fehler"
|
||||
if self.language_manager:
|
||||
title = self.language_manager.get_text(
|
||||
"generator_tab.error_title", "Fehler"
|
||||
)
|
||||
QMessageBox.critical(self, title, message)
|
||||
|
||||
def update_texts(self):
|
||||
"""Aktualisiert UI-Texte gemäß der aktuellen Sprache."""
|
||||
if not self.language_manager:
|
||||
return
|
||||
|
||||
lm = self.language_manager
|
||||
|
||||
self.form_group.setTitle(lm.get_text("generator_tab.form_title", "Account-Informationen"))
|
||||
self.first_name_label.setText(lm.get_text("generator_tab.first_name_label", "Vorname:"))
|
||||
self.first_name_input.setPlaceholderText(lm.get_text("generator_tab.first_name_placeholder", "z.B. Max"))
|
||||
self.last_name_label.setText(lm.get_text("generator_tab.last_name_label", "Nachname:"))
|
||||
self.last_name_input.setPlaceholderText(lm.get_text("generator_tab.last_name_placeholder", "z.B. Mustermann"))
|
||||
self.age_label.setText(lm.get_text("generator_tab.age_label", "Alter:"))
|
||||
self.age_input.setPlaceholderText(lm.get_text("generator_tab.age_placeholder", "Alter zwischen 13 und 99"))
|
||||
self.reg_method_label.setText(lm.get_text("generator_tab.registration_method_label", "Registrierungsmethode:"))
|
||||
self.email_radio.setText(lm.get_text("generator_tab.email_radio", "E-Mail"))
|
||||
self.phone_radio.setText(lm.get_text("generator_tab.phone_radio", "Telefon"))
|
||||
self.phone_label.setText(lm.get_text("generator_tab.phone_label", "Telefonnummer:"))
|
||||
self.phone_input.setPlaceholderText(lm.get_text("generator_tab.phone_placeholder", "z.B. +49123456789"))
|
||||
self.email_domain_label.setText(lm.get_text("generator_tab.email_domain_label", "E-Mail-Domain:"))
|
||||
self.use_proxy_check.setText(lm.get_text("generator_tab.proxy_use", "Proxy verwenden"))
|
||||
self.proxy_type_label.setText(lm.get_text("generator_tab.proxy_type_label", "Typ:"))
|
||||
self.proxy_type_combo.clear()
|
||||
self.proxy_type_combo.addItems([
|
||||
lm.get_text("generator_tab.proxy_type_ipv4", "IPv4"),
|
||||
lm.get_text("generator_tab.proxy_type_ipv6", "IPv6"),
|
||||
lm.get_text("generator_tab.proxy_type_mobile", "Mobile"),
|
||||
])
|
||||
self.proxy_label.setText(lm.get_text("generator_tab.proxy_label", "Proxy:"))
|
||||
self.headless_check.setText(lm.get_text("generator_tab.headless", "Browser im Hintergrund ausführen"))
|
||||
self.debug_check.setText(lm.get_text("generator_tab.debug", "Debug-Modus (detaillierte Protokollierung)"))
|
||||
|
||||
platform = self.platform_name.lower()
|
||||
if platform == "tiktok" and hasattr(self, "category_combo"):
|
||||
self.category_label.setText(
|
||||
lm.get_text("generator_tab.tiktok_category_label", "Kategorie/Nische:")
|
||||
)
|
||||
categories = [
|
||||
lm.get_text("generator_tab.tiktok_category_general", "Allgemein"),
|
||||
lm.get_text("generator_tab.tiktok_category_gaming", "Gaming"),
|
||||
lm.get_text("generator_tab.tiktok_category_fashion", "Mode"),
|
||||
lm.get_text("generator_tab.tiktok_category_fitness", "Fitness"),
|
||||
lm.get_text("generator_tab.tiktok_category_travel", "Reisen"),
|
||||
lm.get_text("generator_tab.tiktok_category_cooking", "Kochen"),
|
||||
lm.get_text("generator_tab.tiktok_category_technology", "Technologie"),
|
||||
lm.get_text("generator_tab.tiktok_category_education", "Bildung"),
|
||||
]
|
||||
current = self.category_combo.currentText()
|
||||
self.category_combo.clear()
|
||||
self.category_combo.addItems(categories)
|
||||
if current in categories:
|
||||
self.category_combo.setCurrentIndex(categories.index(current))
|
||||
self.log_group.setTitle(lm.get_text("generator_tab.log_title", "Log"))
|
||||
self.start_button.setText(lm.get_text("buttons.create", "Account erstellen"))
|
||||
self.stop_button.setText(lm.get_text("buttons.cancel", "Abbrechen"))
|
||||
315
views/tabs/settings_tab.py
Normale Datei
315
views/tabs/settings_tab.py
Normale Datei
@ -0,0 +1,315 @@
|
||||
"""
|
||||
Tab für die Einstellungen der Anwendung.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QFormLayout,
|
||||
QGroupBox, QLabel, QLineEdit, QSpinBox, QTextEdit,
|
||||
QPushButton, QCheckBox, QComboBox
|
||||
)
|
||||
from PyQt5.QtCore import pyqtSignal, Qt
|
||||
|
||||
logger = logging.getLogger("settings_tab")
|
||||
|
||||
class SettingsTab(QWidget):
|
||||
"""Widget für den Einstellungen-Tab."""
|
||||
|
||||
# Signale
|
||||
proxy_settings_saved = pyqtSignal(dict)
|
||||
proxy_tested = pyqtSignal(str) # proxy_type
|
||||
email_settings_saved = pyqtSignal(dict)
|
||||
email_tested = pyqtSignal(dict) # email_settings
|
||||
license_activated = pyqtSignal(str) # license_key
|
||||
|
||||
def __init__(self, platform_name, proxy_rotator=None, email_handler=None, license_manager=None, language_manager=None):
|
||||
super().__init__()
|
||||
self.platform_name = platform_name
|
||||
self.proxy_rotator = proxy_rotator
|
||||
self.email_handler = email_handler
|
||||
self.license_manager = license_manager
|
||||
self.language_manager = language_manager
|
||||
self.init_ui()
|
||||
if self.language_manager:
|
||||
self.language_manager.language_changed.connect(self.update_texts)
|
||||
self.update_texts()
|
||||
|
||||
# Einstellungen laden, falls Handler vorhanden
|
||||
self.load_settings()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die Benutzeroberfläche."""
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Proxy-Einstellungen
|
||||
proxy_group = QGroupBox("Proxy-Einstellungen")
|
||||
proxy_layout = QVBoxLayout(proxy_group)
|
||||
|
||||
# IPv4 Proxies
|
||||
ipv4_form = QFormLayout()
|
||||
self.ipv4_proxy_input = QTextEdit()
|
||||
self.ipv4_proxy_input.setPlaceholderText("Ein Proxy pro Zeile im Format: host:port:username:password")
|
||||
ipv4_form.addRow("IPv4 Proxies:", self.ipv4_proxy_input)
|
||||
proxy_layout.addLayout(ipv4_form)
|
||||
|
||||
# IPv6 Proxies
|
||||
ipv6_form = QFormLayout()
|
||||
self.ipv6_proxy_input = QTextEdit()
|
||||
self.ipv6_proxy_input.setPlaceholderText("Ein Proxy pro Zeile im Format: host:port:username:password")
|
||||
ipv6_form.addRow("IPv6 Proxies:", self.ipv6_proxy_input)
|
||||
proxy_layout.addLayout(ipv6_form)
|
||||
|
||||
# Mobile Proxies
|
||||
mobile_form = QFormLayout()
|
||||
self.mobile_proxy_input = QTextEdit()
|
||||
self.mobile_proxy_input.setPlaceholderText("Ein Proxy pro Zeile im Format: host:port:username:password")
|
||||
mobile_form.addRow("Mobile Proxies:", self.mobile_proxy_input)
|
||||
proxy_layout.addLayout(mobile_form)
|
||||
|
||||
# Mobile Proxy API Keys
|
||||
api_key_layout = QFormLayout()
|
||||
self.marsproxy_api_input = QLineEdit()
|
||||
api_key_layout.addRow("MarsProxies API Key:", self.marsproxy_api_input)
|
||||
|
||||
self.iproyal_api_input = QLineEdit()
|
||||
api_key_layout.addRow("IPRoyal API Key:", self.iproyal_api_input)
|
||||
|
||||
proxy_layout.addLayout(api_key_layout)
|
||||
|
||||
# Test-Button
|
||||
proxy_button_layout = QHBoxLayout()
|
||||
|
||||
self.test_proxy_button = QPushButton("Proxy testen")
|
||||
self.test_proxy_button.clicked.connect(self.on_test_proxy_clicked)
|
||||
|
||||
self.save_proxy_button = QPushButton("Proxy-Einstellungen speichern")
|
||||
self.save_proxy_button.clicked.connect(self.on_save_proxy_clicked)
|
||||
|
||||
proxy_button_layout.addWidget(self.test_proxy_button)
|
||||
proxy_button_layout.addWidget(self.save_proxy_button)
|
||||
|
||||
proxy_layout.addLayout(proxy_button_layout)
|
||||
|
||||
layout.addWidget(proxy_group)
|
||||
|
||||
# E-Mail-Einstellungen
|
||||
email_group = QGroupBox("E-Mail-Einstellungen")
|
||||
email_layout = QFormLayout(email_group)
|
||||
|
||||
self.imap_server_input = QLineEdit("imap.ionos.de")
|
||||
email_layout.addRow("IMAP-Server:", self.imap_server_input)
|
||||
|
||||
self.imap_port_input = QSpinBox()
|
||||
self.imap_port_input.setRange(1, 65535)
|
||||
self.imap_port_input.setValue(993)
|
||||
email_layout.addRow("IMAP-Port:", self.imap_port_input)
|
||||
|
||||
self.imap_user_input = QLineEdit()
|
||||
email_layout.addRow("IMAP-Benutzername:", self.imap_user_input)
|
||||
|
||||
self.imap_pass_input = QLineEdit()
|
||||
self.imap_pass_input.setEchoMode(QLineEdit.Password)
|
||||
email_layout.addRow("IMAP-Passwort:", self.imap_pass_input)
|
||||
|
||||
email_button_layout = QHBoxLayout()
|
||||
|
||||
self.test_email_button = QPushButton("E-Mail testen")
|
||||
self.test_email_button.clicked.connect(self.on_test_email_clicked)
|
||||
|
||||
self.save_email_button = QPushButton("E-Mail-Einstellungen speichern")
|
||||
self.save_email_button.clicked.connect(self.on_save_email_clicked)
|
||||
|
||||
email_button_layout.addWidget(self.test_email_button)
|
||||
email_button_layout.addWidget(self.save_email_button)
|
||||
|
||||
email_layout.addRow("", email_button_layout)
|
||||
|
||||
layout.addWidget(email_group)
|
||||
|
||||
# Plattformspezifische Einstellungen
|
||||
if self.platform_name.lower() != "instagram":
|
||||
self.add_platform_specific_settings(layout)
|
||||
|
||||
# Lizenz-Gruppe
|
||||
license_group = QGroupBox("Lizenz")
|
||||
license_layout = QFormLayout(license_group)
|
||||
|
||||
self.license_key_input = QLineEdit()
|
||||
license_layout.addRow("Lizenzschlüssel:", self.license_key_input)
|
||||
|
||||
self.activate_license_button = QPushButton("Lizenz aktivieren")
|
||||
self.activate_license_button.clicked.connect(self.on_activate_license_clicked)
|
||||
license_layout.addRow("", self.activate_license_button)
|
||||
|
||||
layout.addWidget(license_group)
|
||||
|
||||
# Stretch am Ende hinzufügen
|
||||
layout.addStretch(1)
|
||||
|
||||
def add_platform_specific_settings(self, layout):
|
||||
"""
|
||||
Fügt plattformspezifische Einstellungen hinzu.
|
||||
Diese Methode kann in abgeleiteten Klassen überschrieben werden.
|
||||
|
||||
Args:
|
||||
layout: Das Layout, zu dem die Einstellungen hinzugefügt werden sollen
|
||||
"""
|
||||
platform = self.platform_name.lower()
|
||||
|
||||
platform_settings_group = QGroupBox(f"{self.platform_name}-spezifische Einstellungen")
|
||||
platform_settings_layout = QFormLayout(platform_settings_group)
|
||||
|
||||
# Je nach Plattform unterschiedliche Einstellungen
|
||||
if platform == "twitter":
|
||||
self.twitter_api_key = QLineEdit()
|
||||
platform_settings_layout.addRow("Twitter API Key:", self.twitter_api_key)
|
||||
|
||||
self.twitter_api_secret = QLineEdit()
|
||||
platform_settings_layout.addRow("Twitter API Secret:", self.twitter_api_secret)
|
||||
|
||||
elif platform == "tiktok":
|
||||
self.video_upload = QCheckBox("Video automatisch hochladen")
|
||||
platform_settings_layout.addRow("", self.video_upload)
|
||||
|
||||
self.follower_action = QCheckBox("Automatisch anderen Nutzern folgen")
|
||||
platform_settings_layout.addRow("", self.follower_action)
|
||||
|
||||
elif platform == "facebook":
|
||||
self.page_creation = QCheckBox("Seite automatisch erstellen")
|
||||
platform_settings_layout.addRow("", self.page_creation)
|
||||
|
||||
self.privacy_level = QComboBox()
|
||||
self.privacy_level.addItems(["Öffentlich", "Freunde", "Nur ich"])
|
||||
platform_settings_layout.addRow("Datenschutzeinstellung:", self.privacy_level)
|
||||
|
||||
# Speichern-Button für plattformspezifische Einstellungen
|
||||
self.platform_save_button = QPushButton(f"{self.platform_name}-Einstellungen speichern")
|
||||
self.platform_save_button.clicked.connect(self.on_save_platform_settings_clicked)
|
||||
platform_settings_layout.addRow("", self.platform_save_button)
|
||||
|
||||
layout.addWidget(platform_settings_group)
|
||||
|
||||
def load_settings(self):
|
||||
"""Lädt die Einstellungen aus den Handlern."""
|
||||
# Proxy-Einstellungen laden
|
||||
if self.proxy_rotator:
|
||||
try:
|
||||
proxy_config = self.proxy_rotator.get_config() or {}
|
||||
|
||||
# IPv4 Proxies
|
||||
ipv4_proxies = proxy_config.get("ipv4", [])
|
||||
self.ipv4_proxy_input.setPlainText("\n".join(ipv4_proxies))
|
||||
|
||||
# IPv6 Proxies
|
||||
ipv6_proxies = proxy_config.get("ipv6", [])
|
||||
self.ipv6_proxy_input.setPlainText("\n".join(ipv6_proxies))
|
||||
|
||||
# Mobile Proxies
|
||||
mobile_proxies = proxy_config.get("mobile", [])
|
||||
self.mobile_proxy_input.setPlainText("\n".join(mobile_proxies))
|
||||
|
||||
# API Keys
|
||||
mobile_api = proxy_config.get("mobile_api", {})
|
||||
self.marsproxy_api_input.setText(mobile_api.get("marsproxies", ""))
|
||||
self.iproyal_api_input.setText(mobile_api.get("iproyal", ""))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Proxy-Einstellungen: {e}")
|
||||
|
||||
# E-Mail-Einstellungen laden
|
||||
if self.email_handler:
|
||||
try:
|
||||
email_config = self.email_handler.get_config() or {}
|
||||
|
||||
self.imap_server_input.setText(email_config.get("imap_server", "imap.ionos.de"))
|
||||
self.imap_port_input.setValue(email_config.get("imap_port", 993))
|
||||
self.imap_user_input.setText(email_config.get("imap_user", ""))
|
||||
self.imap_pass_input.setText(email_config.get("imap_pass", ""))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der E-Mail-Einstellungen: {e}")
|
||||
|
||||
# Lizenzeinstellungen laden
|
||||
if self.license_manager:
|
||||
try:
|
||||
license_info = self.license_manager.get_license_info()
|
||||
self.license_key_input.setText(license_info.get("key", ""))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der Lizenzeinstellungen: {e}")
|
||||
|
||||
def on_save_proxy_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Proxy-Speichern-Button geklickt wird."""
|
||||
# Proxy-Einstellungen sammeln
|
||||
settings = {
|
||||
"ipv4_proxies": self.ipv4_proxy_input.toPlainText(),
|
||||
"ipv6_proxies": self.ipv6_proxy_input.toPlainText(),
|
||||
"mobile_proxies": self.mobile_proxy_input.toPlainText(),
|
||||
"mobile_api": {
|
||||
"marsproxies": self.marsproxy_api_input.text().strip(),
|
||||
"iproyal": self.iproyal_api_input.text().strip()
|
||||
}
|
||||
}
|
||||
|
||||
# Signal auslösen
|
||||
self.proxy_settings_saved.emit(settings)
|
||||
|
||||
def on_test_proxy_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Proxy-Test-Button geklickt wird."""
|
||||
# Proxy-Typ aus der Combobox in der Generator-Tab holen
|
||||
# Da wir keine direkte Referenz haben, nehmen wir den ersten Eintrag
|
||||
proxy_type = "ipv4"
|
||||
|
||||
# Signal auslösen
|
||||
self.proxy_tested.emit(proxy_type)
|
||||
|
||||
def on_save_email_clicked(self):
|
||||
"""Wird aufgerufen, wenn der E-Mail-Speichern-Button geklickt wird."""
|
||||
# E-Mail-Einstellungen sammeln
|
||||
settings = {
|
||||
"imap_server": self.imap_server_input.text().strip(),
|
||||
"imap_port": self.imap_port_input.value(),
|
||||
"imap_user": self.imap_user_input.text().strip(),
|
||||
"imap_pass": self.imap_pass_input.text()
|
||||
}
|
||||
|
||||
# Signal auslösen
|
||||
self.email_settings_saved.emit(settings)
|
||||
|
||||
def on_test_email_clicked(self):
|
||||
"""Wird aufgerufen, wenn der E-Mail-Test-Button geklickt wird."""
|
||||
# E-Mail-Einstellungen sammeln
|
||||
settings = {
|
||||
"imap_server": self.imap_server_input.text().strip(),
|
||||
"imap_port": self.imap_port_input.value(),
|
||||
"imap_user": self.imap_user_input.text().strip(),
|
||||
"imap_pass": self.imap_pass_input.text()
|
||||
}
|
||||
|
||||
# Signal auslösen
|
||||
self.email_tested.emit(settings)
|
||||
|
||||
def on_save_platform_settings_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Plattform-Einstellungen-Speichern-Button geklickt wird."""
|
||||
# Hier könnte ein plattformspezifisches Signal ausgelöst werden
|
||||
logger.info(f"{self.platform_name}-Einstellungen gespeichert")
|
||||
|
||||
def on_activate_license_clicked(self):
|
||||
"""Wird aufgerufen, wenn der Lizenz-Aktivieren-Button geklickt wird."""
|
||||
license_key = self.license_key_input.text().strip()
|
||||
|
||||
if not license_key:
|
||||
return
|
||||
|
||||
# Signal auslösen
|
||||
self.license_activated.emit(license_key)
|
||||
|
||||
def update_texts(self):
|
||||
"""Aktualisiert UI-Texte gemäß aktueller Sprache."""
|
||||
if not self.language_manager:
|
||||
return
|
||||
self.test_proxy_button.setText(self.language_manager.get_text("buttons.test_proxy", "Proxy testen"))
|
||||
self.save_proxy_button.setText(self.language_manager.get_text("buttons.save_proxy", "Proxy-Einstellungen speichern"))
|
||||
self.test_email_button.setText(self.language_manager.get_text("buttons.test_email", "E-Mail testen"))
|
||||
self.save_email_button.setText(self.language_manager.get_text("buttons.save_email", "E-Mail-Einstellungen speichern"))
|
||||
self.activate_license_button.setText(self.language_manager.get_text("buttons.activate_license", "Lizenz aktivieren"))
|
||||
BIN
views/widgets/__pycache__/language_dropdown.cpython-310.pyc
Normale Datei
BIN
views/widgets/__pycache__/language_dropdown.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/widgets/__pycache__/language_dropdown.cpython-313.pyc
Normale Datei
BIN
views/widgets/__pycache__/language_dropdown.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/widgets/__pycache__/platform_button.cpython-310.pyc
Normale Datei
BIN
views/widgets/__pycache__/platform_button.cpython-310.pyc
Normale Datei
Binäre Datei nicht angezeigt.
BIN
views/widgets/__pycache__/platform_button.cpython-313.pyc
Normale Datei
BIN
views/widgets/__pycache__/platform_button.cpython-313.pyc
Normale Datei
Binäre Datei nicht angezeigt.
203
views/widgets/language_dropdown.py
Normale Datei
203
views/widgets/language_dropdown.py
Normale Datei
@ -0,0 +1,203 @@
|
||||
# Path: views/widgets/language_dropdown.py
|
||||
|
||||
"""
|
||||
Benutzerdefiniertes Dropdown-Widget für die Sprachauswahl mit Flaggen-Icons.
|
||||
"""
|
||||
|
||||
import os
|
||||
from PyQt5.QtWidgets import (QWidget, QComboBox, QLabel, QHBoxLayout,
|
||||
QVBoxLayout, QFrame, QListWidget, QListWidgetItem,
|
||||
QAbstractItemView, QApplication)
|
||||
from PyQt5.QtCore import Qt, QSize, QEvent, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon, QPainter, QPen, QColor, QCursor
|
||||
|
||||
class LanguageDropdown(QWidget):
|
||||
"""Benutzerdefiniertes Dropdown für die Sprachauswahl mit Flaggen."""
|
||||
|
||||
def __init__(self, language_manager):
|
||||
super().__init__()
|
||||
self.language_manager = language_manager
|
||||
self.is_open = False
|
||||
self.languages = {}
|
||||
self.current_language = self.language_manager.get_current_language()
|
||||
|
||||
# QApplication-Instanz merken, um einen Event-Filter installieren zu können
|
||||
self.app = QApplication.instance()
|
||||
|
||||
# Verfügbare Sprachen aus dem Manager holen
|
||||
self.available_languages = self.language_manager.get_available_languages()
|
||||
|
||||
self.init_ui()
|
||||
|
||||
# Verbinde Signal des Language Managers
|
||||
self.language_manager.language_changed.connect(self.on_language_changed)
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die Benutzeroberfläche."""
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# Container für die aktuelle Sprachauswahl
|
||||
self.current_language_container = QFrame()
|
||||
self.current_language_container.setObjectName("languageSelector")
|
||||
self.current_language_container.setCursor(Qt.PointingHandCursor)
|
||||
self.current_language_container.setStyleSheet("""
|
||||
QFrame#languageSelector {
|
||||
background-color: transparent;
|
||||
border-radius: 15px;
|
||||
}
|
||||
QFrame#languageSelector:hover {
|
||||
background-color: rgba(200, 200, 200, 30);
|
||||
}
|
||||
""")
|
||||
|
||||
current_layout = QHBoxLayout(self.current_language_container)
|
||||
current_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# Icon der aktuellen Sprache
|
||||
self.current_flag = QLabel()
|
||||
self.current_flag.setFixedSize(24, 24)
|
||||
|
||||
# Pfad zum Icon
|
||||
icon_path = self.get_language_icon_path(self.current_language)
|
||||
if icon_path:
|
||||
self.current_flag.setPixmap(QIcon(icon_path).pixmap(QSize(24, 24)))
|
||||
|
||||
current_layout.addWidget(self.current_flag)
|
||||
|
||||
# Kleiner Pfeil nach unten
|
||||
arrow_label = QLabel("▼")
|
||||
arrow_label.setStyleSheet("font-size: 8px; color: #888888;")
|
||||
current_layout.addWidget(arrow_label)
|
||||
|
||||
layout.addWidget(self.current_language_container)
|
||||
|
||||
# Dropdown-Liste (anfangs versteckt)
|
||||
self.dropdown_list = QListWidget()
|
||||
self.dropdown_list.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
|
||||
self.dropdown_list.setFocusPolicy(Qt.NoFocus)
|
||||
self.dropdown_list.setMouseTracking(True)
|
||||
self.dropdown_list.setFrameShape(QFrame.NoFrame)
|
||||
self.dropdown_list.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.dropdown_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
|
||||
self.dropdown_list.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.dropdown_list.setStyleSheet("""
|
||||
QListWidget {
|
||||
background-color: white;
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
QListWidget::item {
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
QListWidget::item:hover {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
""")
|
||||
|
||||
# Sprachen zum Dropdown hinzufügen
|
||||
self.populate_dropdown()
|
||||
|
||||
# Event-Verbindungen
|
||||
self.current_language_container.mousePressEvent = self.toggle_dropdown
|
||||
self.dropdown_list.itemClicked.connect(self.on_language_selected)
|
||||
|
||||
# Zugänglichkeit mit Tastaturfokus
|
||||
self.setFocusPolicy(Qt.StrongFocus)
|
||||
self.current_language_container.setFocusPolicy(Qt.StrongFocus)
|
||||
|
||||
def get_language_icon_path(self, language_code):
|
||||
"""Gibt den Pfad zum Icon für den angegebenen Sprachcode zurück."""
|
||||
# Projektbasis-Verzeichnis ermitteln
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
icon_path = os.path.join(base_dir, "resources", "icons", f"{language_code}.svg")
|
||||
|
||||
if os.path.exists(icon_path):
|
||||
return icon_path
|
||||
return None
|
||||
|
||||
def populate_dropdown(self):
|
||||
"""Füllt das Dropdown mit den verfügbaren Sprachen."""
|
||||
self.dropdown_list.clear()
|
||||
self.languages = {}
|
||||
|
||||
for code, name in self.available_languages.items():
|
||||
item = QListWidgetItem(name)
|
||||
|
||||
# Icon erstellen
|
||||
icon_path = self.get_language_icon_path(code)
|
||||
if icon_path:
|
||||
item.setIcon(QIcon(icon_path))
|
||||
|
||||
# Sprach-Code speichern
|
||||
item.setData(Qt.UserRole, code)
|
||||
|
||||
# Zum Dropdown hinzufügen
|
||||
self.dropdown_list.addItem(item)
|
||||
self.languages[code] = item
|
||||
|
||||
def toggle_dropdown(self, event):
|
||||
"""Öffnet oder schließt das Dropdown-Menü."""
|
||||
if not self.is_open:
|
||||
# Position des Dropdowns unter dem Button berechnen
|
||||
pos = self.current_language_container.mapToGlobal(self.current_language_container.rect().bottomLeft())
|
||||
self.dropdown_list.setGeometry(pos.x(), pos.y(), 120, 120) # Größe anpassen
|
||||
self.dropdown_list.show()
|
||||
self.is_open = True
|
||||
if self.app:
|
||||
self.app.installEventFilter(self)
|
||||
else:
|
||||
self.dropdown_list.hide()
|
||||
self.is_open = False
|
||||
if self.app:
|
||||
self.app.removeEventFilter(self)
|
||||
|
||||
def on_language_selected(self, item):
|
||||
"""Wird aufgerufen, wenn eine Sprache im Dropdown ausgewählt wird."""
|
||||
language_code = item.data(Qt.UserRole)
|
||||
|
||||
# Sprache wechseln
|
||||
if language_code != self.current_language:
|
||||
self.language_manager.change_language(language_code)
|
||||
|
||||
# Dropdown schließen
|
||||
self.dropdown_list.hide()
|
||||
self.is_open = False
|
||||
if self.app:
|
||||
self.app.removeEventFilter(self)
|
||||
|
||||
def on_language_changed(self, language_code):
|
||||
"""Wird aufgerufen, wenn sich die Sprache im LanguageManager ändert."""
|
||||
self.current_language = language_code
|
||||
|
||||
# Icon aktualisieren
|
||||
icon_path = self.get_language_icon_path(language_code)
|
||||
if icon_path:
|
||||
self.current_flag.setPixmap(QIcon(icon_path).pixmap(QSize(24, 24)))
|
||||
|
||||
# Texte aktualisieren (falls vorhanden)
|
||||
if hasattr(self.parent(), "update_texts"):
|
||||
self.parent().update_texts()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Behandelt Tastatureingaben für verbesserte Zugänglichkeit."""
|
||||
if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return:
|
||||
self.toggle_dropdown(None)
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
"""Schließt das Dropdown, wenn außerhalb geklickt wird."""
|
||||
if self.is_open and event.type() == QEvent.MouseButtonPress:
|
||||
# Klickposition relativ zum Dropdown ermitteln
|
||||
if not self.dropdown_list.geometry().contains(event.globalPos()) and \
|
||||
not self.current_language_container.geometry().contains(
|
||||
self.mapFromGlobal(event.globalPos())):
|
||||
self.dropdown_list.hide()
|
||||
self.is_open = False
|
||||
if self.app:
|
||||
self.app.removeEventFilter(self)
|
||||
return super().eventFilter(obj, event)
|
||||
76
views/widgets/platform_button.py
Normale Datei
76
views/widgets/platform_button.py
Normale Datei
@ -0,0 +1,76 @@
|
||||
# Path: views/widgets/platform_button.py
|
||||
|
||||
"""
|
||||
Benutzerdefinierter Button für die Plattformauswahl.
|
||||
"""
|
||||
|
||||
import os
|
||||
from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QLabel, QWidget
|
||||
from PyQt5.QtCore import QSize, Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QIcon, QFont
|
||||
|
||||
class PlatformButton(QWidget):
|
||||
"""Angepasster Button-Widget für Plattformauswahl mit Icon."""
|
||||
|
||||
# Signal wenn geklickt
|
||||
clicked = pyqtSignal()
|
||||
|
||||
def __init__(self, platform_name, icon_path=None, enabled=True):
|
||||
super().__init__()
|
||||
|
||||
self.platform = platform_name.lower()
|
||||
self.setMinimumSize(200, 200)
|
||||
self.setEnabled(enabled)
|
||||
|
||||
# Layout für den Container
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setAlignment(Qt.AlignCenter)
|
||||
layout.setContentsMargins(10, 10, 10, 10)
|
||||
|
||||
# Icon-Button
|
||||
self.icon_button = QPushButton()
|
||||
self.icon_button.setFlat(True)
|
||||
self.icon_button.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
# Icon setzen, falls vorhanden
|
||||
if icon_path and os.path.exists(icon_path):
|
||||
self.icon_button.setIcon(QIcon(icon_path))
|
||||
self.icon_button.setIconSize(QSize(120, 120)) # Größeres Icon
|
||||
|
||||
self.icon_button.setMinimumSize(150, 150)
|
||||
self.icon_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: rgba(200, 200, 200, 50);
|
||||
border-radius: 10px;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: rgba(150, 150, 150, 80);
|
||||
}
|
||||
QPushButton:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
""")
|
||||
|
||||
# Button-Signal verbinden
|
||||
self.icon_button.clicked.connect(self.clicked)
|
||||
|
||||
# Name-Label
|
||||
self.name_label = QLabel(platform_name)
|
||||
self.name_label.setAlignment(Qt.AlignCenter)
|
||||
name_font = QFont()
|
||||
name_font.setPointSize(12)
|
||||
name_font.setBold(True)
|
||||
self.name_label.setFont(name_font)
|
||||
self.name_label.setStyleSheet("color: black;") # Schwarzer Text
|
||||
|
||||
# Widgets zum Layout hinzufügen
|
||||
layout.addWidget(self.icon_button, 0, Qt.AlignCenter)
|
||||
layout.addWidget(self.name_label, 0, Qt.AlignCenter)
|
||||
|
||||
# Styling für den deaktivierten Zustand
|
||||
if not enabled:
|
||||
self.setStyleSheet("opacity: 0.5;")
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren