""" 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_())