Dieser Commit ist enthalten in:
Claude Project Manager
2025-07-03 21:11:05 +02:00
Commit 08ed938105
239 geänderte Dateien mit 21554 neuen und 0 gelöschten Zeilen

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

68
views/about_dialog.py Normale Datei
Datei anzeigen

@ -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
Datei anzeigen

@ -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
Datei anzeigen

@ -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äre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

207
views/tabs/accounts_tab.py Normale Datei
Datei anzeigen

@ -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
Datei anzeigen

@ -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
Datei anzeigen

@ -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äre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

Datei anzeigen

@ -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)

Datei anzeigen

@ -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;")