RollBack Punkt 2025-09-13
Dieser Commit ist enthalten in:
@ -1,625 +0,0 @@
|
||||
"""
|
||||
Account Creation Modal V2 - Verbessertes Design mit eindringlicher Warnung
|
||||
Basierend auf dem Corporate Design Styleguide
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import datetime, timedelta
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QFrame, QWidget, QScrollArea,
|
||||
QProgressBar, QGraphicsOpacityEffect
|
||||
)
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QTimer, pyqtSignal, QPropertyAnimation,
|
||||
QEasingCurve, QRect, QSize
|
||||
)
|
||||
from PyQt5.QtGui import QFont, QPainter, QBrush, QColor, QPen, QPixmap
|
||||
|
||||
logger = logging.getLogger("account_creation_modal_v2")
|
||||
|
||||
|
||||
class AccountCreationModalV2(QDialog):
|
||||
"""
|
||||
Verbessertes Modal für Account-Erstellung mit:
|
||||
- Prominenter Warnung
|
||||
- Besserer Step-Visualisierung
|
||||
- Größerem Fenster
|
||||
- Klarerer Kommunikation
|
||||
"""
|
||||
|
||||
# Signale
|
||||
cancel_clicked = pyqtSignal()
|
||||
process_completed = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, platform: str = "Social Media"):
|
||||
super().__init__(parent)
|
||||
self.platform = platform
|
||||
self.current_step = 0
|
||||
self.total_steps = 0
|
||||
self.steps = []
|
||||
self.start_time = None
|
||||
self.is_process_running = False
|
||||
|
||||
# Timer für Updates
|
||||
self.update_timer = QTimer()
|
||||
self.update_timer.timeout.connect(self.update_time_display)
|
||||
self.update_timer.setInterval(1000) # Jede Sekunde
|
||||
|
||||
# Animation Timer für Warning
|
||||
self.warning_animation_timer = QTimer()
|
||||
self.warning_animation_timer.timeout.connect(self.animate_warning)
|
||||
self.warning_animation_timer.setInterval(100) # Smooth animation
|
||||
self.warning_opacity = 1.0
|
||||
self.warning_direction = -0.02
|
||||
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die UI mit verbessertem Design"""
|
||||
# Modal-Eigenschaften
|
||||
self.setModal(True)
|
||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
|
||||
# Größeres Fenster
|
||||
self.setFixedSize(650, 700)
|
||||
|
||||
# Zentriere auf Parent oder Bildschirm
|
||||
if self.parent():
|
||||
parent_rect = self.parent().geometry()
|
||||
x = parent_rect.x() + (parent_rect.width() - self.width()) // 2
|
||||
y = parent_rect.y() + (parent_rect.height() - self.height()) // 2
|
||||
self.move(x, y)
|
||||
|
||||
# Hauptlayout
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
# Container mit weißem Hintergrund
|
||||
self.container = QFrame()
|
||||
self.container.setObjectName("mainContainer")
|
||||
self.container.setStyleSheet("""
|
||||
QFrame#mainContainer {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E2E8F0;
|
||||
border-radius: 16px;
|
||||
}
|
||||
""")
|
||||
|
||||
container_layout = QVBoxLayout(self.container)
|
||||
container_layout.setContentsMargins(0, 0, 0, 0)
|
||||
container_layout.setSpacing(0)
|
||||
|
||||
# 1. WARNING BANNER (Sehr auffällig!)
|
||||
self.warning_banner = self.create_warning_banner()
|
||||
container_layout.addWidget(self.warning_banner)
|
||||
|
||||
# Content Area mit Padding
|
||||
content_widget = QWidget()
|
||||
content_layout = QVBoxLayout(content_widget)
|
||||
content_layout.setContentsMargins(40, 30, 40, 40)
|
||||
content_layout.setSpacing(24)
|
||||
|
||||
# 2. Titel-Bereich
|
||||
self.title_label = QLabel(f"Account wird erstellt für {self.platform}")
|
||||
self.title_label.setAlignment(Qt.AlignCenter)
|
||||
self.title_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #1A365D;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
""")
|
||||
content_layout.addWidget(self.title_label)
|
||||
|
||||
# 3. Progress-Bereich
|
||||
progress_widget = self.create_progress_section()
|
||||
content_layout.addWidget(progress_widget)
|
||||
|
||||
# 4. Steps-Liste
|
||||
self.steps_widget = self.create_steps_list()
|
||||
content_layout.addWidget(self.steps_widget)
|
||||
|
||||
# 5. Live-Status
|
||||
self.status_widget = self.create_status_section()
|
||||
content_layout.addWidget(self.status_widget)
|
||||
|
||||
# 6. Info-Box
|
||||
info_box = self.create_info_box()
|
||||
content_layout.addWidget(info_box)
|
||||
|
||||
# Spacer
|
||||
content_layout.addStretch()
|
||||
|
||||
# Container zum Hauptlayout hinzufügen
|
||||
container_layout.addWidget(content_widget)
|
||||
main_layout.addWidget(self.container)
|
||||
|
||||
def create_warning_banner(self) -> QFrame:
|
||||
"""Erstellt den auffälligen Warning Banner"""
|
||||
banner = QFrame()
|
||||
banner.setObjectName("warningBanner")
|
||||
banner.setFixedHeight(100)
|
||||
banner.setStyleSheet("""
|
||||
QFrame#warningBanner {
|
||||
background-color: #DC2626;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
border-bottom: none;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(banner)
|
||||
layout.setContentsMargins(40, 20, 40, 20)
|
||||
layout.setSpacing(8)
|
||||
|
||||
# Warning Icon + Haupttext
|
||||
main_warning = QLabel("⚠️ NICHT DEN BROWSER BERÜHREN!")
|
||||
main_warning.setAlignment(Qt.AlignCenter)
|
||||
main_warning.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #FFFFFF;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
""")
|
||||
|
||||
# Untertitel
|
||||
subtitle = QLabel("Die Automatisierung läuft. Jede Interaktion kann den Prozess unterbrechen.")
|
||||
subtitle.setAlignment(Qt.AlignCenter)
|
||||
subtitle.setWordWrap(True)
|
||||
subtitle.setStyleSheet("""
|
||||
QLabel {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
""")
|
||||
|
||||
layout.addWidget(main_warning)
|
||||
layout.addWidget(subtitle)
|
||||
|
||||
return banner
|
||||
|
||||
def create_progress_section(self) -> QWidget:
|
||||
"""Erstellt den Progress-Bereich"""
|
||||
widget = QWidget()
|
||||
layout = QHBoxLayout(widget)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(24)
|
||||
|
||||
# Progress Bar
|
||||
progress_container = QWidget()
|
||||
progress_layout = QVBoxLayout(progress_container)
|
||||
progress_layout.setContentsMargins(0, 0, 0, 0)
|
||||
progress_layout.setSpacing(8)
|
||||
|
||||
self.progress_bar = QProgressBar()
|
||||
self.progress_bar.setFixedHeight(12)
|
||||
self.progress_bar.setStyleSheet("""
|
||||
QProgressBar {
|
||||
background-color: #E2E8F0;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
QProgressBar::chunk {
|
||||
background-color: #3182CE;
|
||||
border-radius: 6px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.progress_label = QLabel("Schritt 0 von 0")
|
||||
self.progress_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #4A5568;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
""")
|
||||
|
||||
progress_layout.addWidget(self.progress_bar)
|
||||
progress_layout.addWidget(self.progress_label)
|
||||
|
||||
# Zeit-Anzeige
|
||||
time_container = QWidget()
|
||||
time_container.setFixedWidth(180)
|
||||
time_layout = QVBoxLayout(time_container)
|
||||
time_layout.setContentsMargins(0, 0, 0, 0)
|
||||
time_layout.setSpacing(4)
|
||||
|
||||
self.time_label = QLabel("~2 Min verbleibend")
|
||||
self.time_label.setAlignment(Qt.AlignRight)
|
||||
self.time_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #1A365D;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-family: 'Poppins', -apple-system, sans-serif;
|
||||
}
|
||||
""")
|
||||
|
||||
self.elapsed_label = QLabel("Verstrichene Zeit: 0:00")
|
||||
self.elapsed_label.setAlignment(Qt.AlignRight)
|
||||
self.elapsed_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #718096;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
""")
|
||||
|
||||
time_layout.addWidget(self.time_label)
|
||||
time_layout.addWidget(self.elapsed_label)
|
||||
|
||||
layout.addWidget(progress_container, 1)
|
||||
layout.addWidget(time_container)
|
||||
|
||||
return widget
|
||||
|
||||
def create_steps_list(self) -> QWidget:
|
||||
"""Erstellt die visuelle Steps-Liste"""
|
||||
container = QFrame()
|
||||
container.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #F8FAFC;
|
||||
border: 1px solid #E2E8F0;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.steps_layout = QVBoxLayout(container)
|
||||
self.steps_layout.setSpacing(12)
|
||||
|
||||
# Wird dynamisch befüllt
|
||||
return container
|
||||
|
||||
def create_status_section(self) -> QWidget:
|
||||
"""Erstellt den Live-Status Bereich"""
|
||||
container = QFrame()
|
||||
container.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #1A365D;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
min-height: 80px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout(container)
|
||||
layout.setSpacing(8)
|
||||
|
||||
status_title = QLabel("Aktueller Status:")
|
||||
status_title.setStyleSheet("""
|
||||
QLabel {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.current_status_label = QLabel("Initialisiere Browser...")
|
||||
self.current_status_label.setWordWrap(True)
|
||||
self.current_status_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
""")
|
||||
|
||||
layout.addWidget(status_title)
|
||||
layout.addWidget(self.current_status_label)
|
||||
|
||||
return container
|
||||
|
||||
def create_info_box(self) -> QWidget:
|
||||
"""Erstellt die Info-Box"""
|
||||
container = QFrame()
|
||||
container.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #DBEAFE;
|
||||
border: 1px solid #2563EB;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QHBoxLayout(container)
|
||||
layout.setSpacing(16)
|
||||
|
||||
# Info Icon
|
||||
icon_label = QLabel("ℹ️")
|
||||
icon_label.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 24px;
|
||||
color: #2563EB;
|
||||
}
|
||||
""")
|
||||
|
||||
# Info Text
|
||||
info_layout = QVBoxLayout()
|
||||
info_layout.setSpacing(4)
|
||||
|
||||
info_title = QLabel("Was passiert gerade?")
|
||||
info_title.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #1E40AF;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
""")
|
||||
|
||||
self.info_text = QLabel("Der Browser simuliert menschliches Verhalten beim Ausfüllen des Formulars.")
|
||||
self.info_text.setWordWrap(True)
|
||||
self.info_text.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #1E40AF;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
}
|
||||
""")
|
||||
|
||||
info_layout.addWidget(info_title)
|
||||
info_layout.addWidget(self.info_text)
|
||||
|
||||
layout.addWidget(icon_label)
|
||||
layout.addWidget(info_layout, 1)
|
||||
|
||||
return container
|
||||
|
||||
def set_steps(self, steps: List[str]):
|
||||
"""Setzt die Steps und erstellt die visuelle Liste"""
|
||||
self.steps = steps
|
||||
self.total_steps = len(steps)
|
||||
self.current_step = 0
|
||||
|
||||
# Progress Bar Setup
|
||||
self.progress_bar.setMaximum(self.total_steps)
|
||||
self.progress_bar.setValue(0)
|
||||
|
||||
# Clear existing steps
|
||||
for i in reversed(range(self.steps_layout.count())):
|
||||
self.steps_layout.itemAt(i).widget().setParent(None)
|
||||
|
||||
# Create step items
|
||||
self.step_widgets = []
|
||||
for i, step in enumerate(steps):
|
||||
step_widget = self.create_step_item(i, step)
|
||||
self.steps_layout.addWidget(step_widget)
|
||||
self.step_widgets.append(step_widget)
|
||||
|
||||
def create_step_item(self, index: int, text: str) -> QWidget:
|
||||
"""Erstellt ein einzelnes Step-Item"""
|
||||
widget = QWidget()
|
||||
layout = QHBoxLayout(widget)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(12)
|
||||
|
||||
# Status Icon
|
||||
icon_label = QLabel()
|
||||
icon_label.setFixedSize(24, 24)
|
||||
icon_label.setAlignment(Qt.AlignCenter)
|
||||
icon_label.setObjectName(f"stepIcon_{index}")
|
||||
|
||||
# Text
|
||||
text_label = QLabel(text)
|
||||
text_label.setObjectName(f"stepText_{index}")
|
||||
|
||||
# Initial state (pending)
|
||||
self.update_step_appearance(widget, 'pending')
|
||||
|
||||
layout.addWidget(icon_label)
|
||||
layout.addWidget(text_label, 1)
|
||||
|
||||
return widget
|
||||
|
||||
def update_step_appearance(self, widget: QWidget, status: str):
|
||||
"""Aktualisiert das Aussehen eines Steps basierend auf Status"""
|
||||
icon_label = widget.findChild(QLabel, QRegExp("stepIcon_*"))
|
||||
text_label = widget.findChild(QLabel, QRegExp("stepText_*"))
|
||||
|
||||
if status == 'completed':
|
||||
icon_label.setText("✅")
|
||||
text_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #059669;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
""")
|
||||
elif status == 'active':
|
||||
icon_label.setText("🔄")
|
||||
text_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #2563EB;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
""")
|
||||
# Rotation animation würde hier hinzugefügt
|
||||
else: # pending
|
||||
icon_label.setText("⏳")
|
||||
text_label.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #718096;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
""")
|
||||
|
||||
def start_process(self):
|
||||
"""Startet den Account-Erstellungsprozess"""
|
||||
self.is_process_running = True
|
||||
self.start_time = datetime.now()
|
||||
self.update_timer.start()
|
||||
self.warning_animation_timer.start()
|
||||
self.show()
|
||||
|
||||
def next_step(self, status_text: str = None, info_text: str = None):
|
||||
"""Geht zum nächsten Schritt"""
|
||||
if self.current_step < self.total_steps:
|
||||
# Aktuellen Step als aktiv markieren
|
||||
if self.current_step > 0:
|
||||
self.update_step_appearance(self.step_widgets[self.current_step - 1], 'completed')
|
||||
|
||||
if self.current_step < len(self.step_widgets):
|
||||
self.update_step_appearance(self.step_widgets[self.current_step], 'active')
|
||||
|
||||
self.current_step += 1
|
||||
self.progress_bar.setValue(self.current_step)
|
||||
self.progress_label.setText(f"Schritt {self.current_step} von {self.total_steps}")
|
||||
|
||||
# Update status
|
||||
if status_text:
|
||||
self.current_status_label.setText(status_text)
|
||||
|
||||
# Update info
|
||||
if info_text:
|
||||
self.info_text.setText(info_text)
|
||||
|
||||
# Update time estimate
|
||||
self.update_time_estimate()
|
||||
|
||||
def update_time_estimate(self):
|
||||
"""Aktualisiert die Zeitschätzung"""
|
||||
if self.total_steps > 0 and self.current_step > 0:
|
||||
elapsed = (datetime.now() - self.start_time).total_seconds()
|
||||
avg_time_per_step = elapsed / self.current_step
|
||||
remaining_steps = self.total_steps - self.current_step
|
||||
estimated_remaining = remaining_steps * avg_time_per_step
|
||||
|
||||
if estimated_remaining < 60:
|
||||
self.time_label.setText(f"~{int(estimated_remaining)}s verbleibend")
|
||||
else:
|
||||
minutes = int(estimated_remaining / 60)
|
||||
self.time_label.setText(f"~{minutes} Min verbleibend")
|
||||
|
||||
def update_time_display(self):
|
||||
"""Aktualisiert die verstrichene Zeit"""
|
||||
if self.start_time:
|
||||
elapsed = datetime.now() - self.start_time
|
||||
minutes = int(elapsed.total_seconds() / 60)
|
||||
seconds = int(elapsed.total_seconds() % 60)
|
||||
self.elapsed_label.setText(f"Verstrichene Zeit: {minutes}:{seconds:02d}")
|
||||
|
||||
def animate_warning(self):
|
||||
"""Animiert den Warning Banner (Pulseffekt)"""
|
||||
self.warning_opacity += self.warning_direction
|
||||
|
||||
if self.warning_opacity <= 0.7:
|
||||
self.warning_opacity = 0.7
|
||||
self.warning_direction = 0.02
|
||||
elif self.warning_opacity >= 1.0:
|
||||
self.warning_opacity = 1.0
|
||||
self.warning_direction = -0.02
|
||||
|
||||
# Opacity-Effekt anwenden
|
||||
effect = QGraphicsOpacityEffect()
|
||||
effect.setOpacity(self.warning_opacity)
|
||||
self.warning_banner.setGraphicsEffect(effect)
|
||||
|
||||
def set_status(self, status: str, info: str = None):
|
||||
"""Setzt den aktuellen Status"""
|
||||
self.current_status_label.setText(status)
|
||||
if info:
|
||||
self.info_text.setText(info)
|
||||
|
||||
def complete_process(self):
|
||||
"""Beendet den Prozess erfolgreich"""
|
||||
self.is_process_running = False
|
||||
self.update_timer.stop()
|
||||
self.warning_animation_timer.stop()
|
||||
|
||||
# Letzten Step als completed markieren
|
||||
if self.current_step > 0 and self.current_step <= len(self.step_widgets):
|
||||
self.update_step_appearance(self.step_widgets[self.current_step - 1], 'completed')
|
||||
|
||||
# Success message
|
||||
self.current_status_label.setText("✅ Account erfolgreich erstellt!")
|
||||
self.info_text.setText("Der Prozess wurde erfolgreich abgeschlossen. Das Fenster schließt sich in wenigen Sekunden.")
|
||||
|
||||
# Auto-close nach 3 Sekunden
|
||||
QTimer.singleShot(3000, self.close)
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""Custom paint event für Transparenz"""
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
# Semi-transparenter Hintergrund
|
||||
painter.fillRect(self.rect(), QColor(0, 0, 0, 120))
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Verhindert das Schließen mit ESC"""
|
||||
if event.key() == Qt.Key_Escape:
|
||||
event.ignore()
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Verhindert das Schließen während der Prozess läuft"""
|
||||
if self.is_process_running:
|
||||
event.ignore()
|
||||
else:
|
||||
self.update_timer.stop()
|
||||
self.warning_animation_timer.stop()
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
# Beispiel-Verwendung
|
||||
if __name__ == "__main__":
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow
|
||||
import sys
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# Test window
|
||||
main_window = QMainWindow()
|
||||
main_window.setGeometry(100, 100, 800, 600)
|
||||
main_window.show()
|
||||
|
||||
# Create and show modal
|
||||
modal = AccountCreationModalV2(main_window, "Instagram")
|
||||
modal.set_steps([
|
||||
"Browser vorbereiten",
|
||||
"Instagram-Seite laden",
|
||||
"Registrierungsformular öffnen",
|
||||
"Benutzerdaten eingeben",
|
||||
"Account erstellen",
|
||||
"E-Mail-Verifizierung",
|
||||
"Profil einrichten"
|
||||
])
|
||||
|
||||
# Simulate process
|
||||
modal.start_process()
|
||||
|
||||
# Simulate step progression
|
||||
def next_step():
|
||||
modal.next_step(
|
||||
f"Führe Schritt {modal.current_step + 1} aus...",
|
||||
"Der Browser füllt automatisch die erforderlichen Felder aus."
|
||||
)
|
||||
|
||||
if modal.current_step < modal.total_steps:
|
||||
QTimer.singleShot(2000, next_step)
|
||||
else:
|
||||
modal.complete_process()
|
||||
|
||||
QTimer.singleShot(1000, next_step)
|
||||
|
||||
sys.exit(app.exec_())
|
||||
@ -1,370 +0,0 @@
|
||||
"""
|
||||
Forge Animation Widget V2 - Verbessertes Design mit prominenter Warnung
|
||||
Angepasst an den bestehenden Code mit minimalen Änderungen
|
||||
"""
|
||||
|
||||
from PyQt5.QtWidgets import QDialog, QWidget, QVBoxLayout, QLabel, QTextEdit, QPushButton, QHBoxLayout, QFrame
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
|
||||
from PyQt5.QtGui import QFont, QMovie, QPixmap
|
||||
|
||||
class ForgeAnimationDialog(QDialog):
|
||||
"""Modal-Dialog für die Account-Erstellung mit verbessertem Design"""
|
||||
|
||||
# Signal wenn Abbrechen geklickt wird
|
||||
cancel_clicked = pyqtSignal()
|
||||
# Signal wenn Dialog geschlossen wird
|
||||
closed = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.init_ui()
|
||||
|
||||
# Timer für das regelmäßige Nach-vorne-Holen
|
||||
self.raise_timer = QTimer()
|
||||
self.raise_timer.timeout.connect(self._raise_to_front)
|
||||
self.raise_timer.setInterval(500) # Alle 500ms
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialisiert die UI mit verbessertem Design"""
|
||||
# Nur Dialog im Vordergrund, nicht das ganze Hauptfenster
|
||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||
self.setModal(False) # Nicht modal - blockiert nicht das Hauptfenster
|
||||
self.setFixedSize(650, 600) # Größer für bessere Sichtbarkeit
|
||||
|
||||
# Styling für Light Theme
|
||||
self.setStyleSheet("""
|
||||
ForgeAnimationDialog {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E2E8F0;
|
||||
border-radius: 8px;
|
||||
}
|
||||
""")
|
||||
|
||||
# Hauptlayout
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
|
||||
# NEUE WARNUNG OBEN - Sehr auffällig!
|
||||
warning_banner = QFrame()
|
||||
warning_banner.setFixedHeight(80)
|
||||
warning_banner.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #DC2626;
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
padding: 15px 30px;
|
||||
}
|
||||
""")
|
||||
|
||||
warning_layout = QVBoxLayout(warning_banner)
|
||||
warning_layout.setContentsMargins(0, 10, 0, 10)
|
||||
|
||||
# Großer Warning Text
|
||||
warning_text = QLabel("⚠️ BROWSER NICHT BERÜHREN!")
|
||||
warning_text.setAlignment(Qt.AlignCenter)
|
||||
warning_text.setStyleSheet("""
|
||||
QLabel {
|
||||
color: #FFFFFF;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
""")
|
||||
|
||||
warning_subtext = QLabel("Jede Interaktion kann den Prozess unterbrechen")
|
||||
warning_subtext.setAlignment(Qt.AlignCenter)
|
||||
warning_subtext.setStyleSheet("""
|
||||
QLabel {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
}
|
||||
""")
|
||||
|
||||
warning_layout.addWidget(warning_text)
|
||||
warning_layout.addWidget(warning_subtext)
|
||||
|
||||
layout.addWidget(warning_banner)
|
||||
|
||||
# Content Container
|
||||
content_container = QWidget()
|
||||
content_layout = QVBoxLayout(content_container)
|
||||
content_layout.setContentsMargins(30, 25, 30, 30)
|
||||
content_layout.setSpacing(20)
|
||||
|
||||
# Titel (etwas größer)
|
||||
title_label = QLabel("Account wird erstellt")
|
||||
title_label.setObjectName("titleLabel")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
title_label.setStyleSheet("""
|
||||
QLabel#titleLabel {
|
||||
color: #1A365D;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
padding-bottom: 10px;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
content_layout.addWidget(title_label)
|
||||
|
||||
# Moderne Info-Karte (angepasst)
|
||||
info_frame = QFrame()
|
||||
info_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background: #DBEAFE;
|
||||
border: 1px solid #2563EB;
|
||||
border-radius: 12px;
|
||||
min-height: 100px;
|
||||
}
|
||||
""")
|
||||
|
||||
info_layout = QHBoxLayout(info_frame)
|
||||
info_layout.setContentsMargins(20, 15, 20, 15)
|
||||
info_layout.setSpacing(15)
|
||||
|
||||
# Icon Container
|
||||
icon_container = QFrame()
|
||||
icon_container.setFixedSize(50, 50)
|
||||
icon_container.setStyleSheet("""
|
||||
QFrame {
|
||||
background: #2563EB;
|
||||
border-radius: 25px;
|
||||
}
|
||||
""")
|
||||
|
||||
icon_layout = QVBoxLayout(icon_container)
|
||||
icon_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
icon_label = QLabel("🤖")
|
||||
icon_label.setAlignment(Qt.AlignCenter)
|
||||
icon_label.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 24px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: white;
|
||||
}
|
||||
""")
|
||||
icon_layout.addWidget(icon_label)
|
||||
|
||||
# Text Container
|
||||
text_container = QFrame()
|
||||
text_layout = QVBoxLayout(text_container)
|
||||
text_layout.setContentsMargins(0, 0, 0, 0)
|
||||
text_layout.setSpacing(5)
|
||||
|
||||
# Titel
|
||||
info_title = QLabel("Automatisierung läuft")
|
||||
info_title.setObjectName("infoTitle")
|
||||
info_title.setStyleSheet("""
|
||||
QLabel#infoTitle {
|
||||
color: #1E40AF;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
font-family: 'Segoe UI', -apple-system, sans-serif;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
""")
|
||||
|
||||
# Beschreibung (deutlicher)
|
||||
info_desc = QLabel("Der Browser arbeitet automatisch. Bitte warten Sie, bis der Vorgang abgeschlossen ist.")
|
||||
info_desc.setObjectName("infoDesc")
|
||||
info_desc.setWordWrap(True)
|
||||
info_desc.setStyleSheet("""
|
||||
QLabel#infoDesc {
|
||||
color: #1E40AF;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-family: 'Segoe UI', -apple-system, sans-serif;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
line-height: 20px;
|
||||
}
|
||||
""")
|
||||
|
||||
text_layout.addWidget(info_title)
|
||||
text_layout.addWidget(info_desc)
|
||||
text_layout.addStretch()
|
||||
|
||||
info_layout.addWidget(icon_container)
|
||||
info_layout.addWidget(text_container, 1)
|
||||
|
||||
content_layout.addWidget(info_frame)
|
||||
|
||||
# Status Container
|
||||
status_container = QFrame()
|
||||
status_container.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #1A365D;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
}
|
||||
""")
|
||||
|
||||
status_layout = QVBoxLayout(status_container)
|
||||
|
||||
# Status-Label (größer)
|
||||
self.status_label = QLabel("Initialisiere...")
|
||||
self.status_label.setObjectName("statusLabel")
|
||||
self.status_label.setAlignment(Qt.AlignLeft)
|
||||
self.status_label.setStyleSheet("""
|
||||
QLabel#statusLabel {
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
padding: 5px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
}
|
||||
""")
|
||||
status_layout.addWidget(self.status_label)
|
||||
|
||||
content_layout.addWidget(status_container)
|
||||
|
||||
# Log-Ausgabe (größer)
|
||||
self.log_output = QTextEdit()
|
||||
self.log_output.setReadOnly(True)
|
||||
self.log_output.setMaximumHeight(180) # Etwas größer
|
||||
self.log_output.setStyleSheet("""
|
||||
QTextEdit {
|
||||
background-color: #F8FAFC;
|
||||
color: #2D3748;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
|
||||
font-size: 12px;
|
||||
border: 1px solid #CBD5E0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
""")
|
||||
content_layout.addWidget(self.log_output)
|
||||
|
||||
# Button-Container
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
# Abbrechen-Button (weniger prominent)
|
||||
self.cancel_button = QPushButton("Abbrechen")
|
||||
self.cancel_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #F0F4F8;
|
||||
color: #4A5568;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 8px 24px;
|
||||
border-radius: 24px;
|
||||
border: 1px solid #E2E8F0;
|
||||
min-height: 40px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #FEE2E2;
|
||||
color: #DC2626;
|
||||
border-color: #DC2626;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #DC2626;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
""")
|
||||
self.cancel_button.clicked.connect(self.cancel_clicked.emit)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
button_layout.addStretch()
|
||||
content_layout.addLayout(button_layout)
|
||||
|
||||
layout.addWidget(content_container)
|
||||
self.setLayout(layout)
|
||||
|
||||
# Volle Sichtbarkeit
|
||||
self.setWindowOpacity(1.0)
|
||||
|
||||
def start_animation(self):
|
||||
"""Zeigt den Dialog an"""
|
||||
self.status_label.setText("Initialisiere...")
|
||||
self.raise_timer.start() # Starte Timer für Always-on-Top
|
||||
|
||||
def stop_animation(self):
|
||||
"""Stoppt die Animation und den Timer"""
|
||||
self.raise_timer.stop()
|
||||
|
||||
def set_status(self, status: str):
|
||||
"""Aktualisiert den Status-Text"""
|
||||
self.status_label.setText(status)
|
||||
|
||||
def add_log(self, message: str):
|
||||
"""Fügt eine Log-Nachricht hinzu"""
|
||||
self.log_output.append(message)
|
||||
# Auto-scroll zum Ende
|
||||
scrollbar = self.log_output.verticalScrollBar()
|
||||
scrollbar.setValue(scrollbar.maximum())
|
||||
|
||||
def clear_log(self):
|
||||
"""Löscht alle Log-Nachrichten"""
|
||||
self.log_output.clear()
|
||||
|
||||
def set_progress(self, value: int):
|
||||
"""Setzt den Fortschritt (0-100) - wird ignoriert da wir Spinner nutzen"""
|
||||
pass
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Wird aufgerufen wenn der Dialog geschlossen wird"""
|
||||
self.stop_animation()
|
||||
self.closed.emit()
|
||||
event.accept()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Verhindert das Schließen mit ESC"""
|
||||
if event.key() == Qt.Key_Escape:
|
||||
event.ignore()
|
||||
else:
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def _raise_to_front(self):
|
||||
"""Holt den Dialog in den Vordergrund"""
|
||||
self.raise_()
|
||||
|
||||
def show(self):
|
||||
"""Überschreibt show() um den Dialog richtig zu positionieren"""
|
||||
super().show()
|
||||
self._raise_to_front() # Initial in den Vordergrund holen
|
||||
|
||||
|
||||
# Zusätzliche Widget-Klasse für den AccountForger
|
||||
class ForgeAnimationWidget(QLabel):
|
||||
"""
|
||||
Einfaches Animation Widget für den Progress Modal
|
||||
Kann einen Spinner oder andere Animation anzeigen
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setText("⚙️") # Placeholder Icon
|
||||
self.setAlignment(Qt.AlignCenter)
|
||||
self.setStyleSheet("""
|
||||
QLabel {
|
||||
font-size: 48px;
|
||||
color: #3182CE;
|
||||
}
|
||||
""")
|
||||
|
||||
# Animation Timer
|
||||
self.animation_timer = QTimer()
|
||||
self.animation_timer.timeout.connect(self.rotate_icon)
|
||||
self.rotation_state = 0
|
||||
|
||||
def start_animation(self):
|
||||
"""Startet die Animation"""
|
||||
self.animation_timer.start(100)
|
||||
|
||||
def stop_animation(self):
|
||||
"""Stoppt die Animation"""
|
||||
self.animation_timer.stop()
|
||||
|
||||
def rotate_icon(self):
|
||||
"""Einfache Rotation Animation"""
|
||||
icons = ["⚙️", "🔧", "🔨", "⚒️"]
|
||||
self.setText(icons[self.rotation_state % len(icons)])
|
||||
self.rotation_state += 1
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren