Files
AccountForger-neuerUpload/views/widgets/account_card.py
Claude Project Manager 2d276f167c Batch-Export geht jetzt
2025-11-16 23:24:04 +01:00

444 Zeilen
16 KiB
Python

"""
Account Card Widget - Kompakte Account-Karte nach Styleguide
"""
from PyQt5.QtWidgets import (
QFrame, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QGridLayout, QWidget, QApplication, QCheckBox
)
from PyQt5.QtCore import Qt, pyqtSignal, QSize, QTimer
from PyQt5.QtGui import QFont, QPixmap
import os
from views.widgets.icon_factory import IconFactory
class AccountCard(QFrame):
"""
Kompakte Account-Karte nach Styleguide für Light Mode
"""
# Signals
login_requested = pyqtSignal(dict) # Account-Daten
export_requested = pyqtSignal(dict) # Account-Daten
delete_requested = pyqtSignal(dict) # Account-Daten
selection_changed = pyqtSignal(int, bool) # account_id, selected
def __init__(self, account_data, language_manager=None):
super().__init__()
self.account_data = account_data
self.language_manager = language_manager
self.password_visible = False
self.selection_mode = False
self.is_selected_state = False
# Timer für Icon-Animation
self.email_copy_timer = QTimer()
self.email_copy_timer.timeout.connect(self._restore_email_copy_icon)
self.password_copy_timer = QTimer()
self.password_copy_timer.timeout.connect(self._restore_password_copy_icon)
# Feature 5: Timer für periodische Guard-Status-Prüfung
self.guard_check_timer = QTimer()
self.guard_check_timer.timeout.connect(self.update_login_button_state)
self.guard_check_timer.start(2000) # Alle 2 Sekunden prüfen
# Original Icons speichern
self.copy_icon = None
self.check_icon = None
self.init_ui()
if self.language_manager:
self.language_manager.language_changed.connect(self.update_texts)
self.update_texts()
# Feature 5: Initiale Button-Status-Prüfung
self.update_login_button_state()
def _on_login_clicked(self):
"""Handler für Login-Button"""
# Feature 5: Prüfe Guard-Status vor Login
from utils.process_guard import get_guard
guard = get_guard()
platform = self.account_data.get("platform", "Unknown")
can_start, error_msg = guard.can_start("Account-Login", platform)
if not can_start:
# Zeige benutzerfreundliche Fehlermeldung
from views.widgets.modern_message_box import show_error
show_error(self, "Login blockiert", error_msg)
return
self.login_requested.emit(self.account_data)
def _on_export_clicked(self):
"""Handler für Export-Button"""
# Feature 5: Prüfe Guard-Status vor Export
from utils.process_guard import get_guard
guard = get_guard()
platform = self.account_data.get("platform", "Unknown")
can_start, error_msg = guard.can_start("Profil-Export", platform)
if not can_start:
# Zeige benutzerfreundliche Fehlermeldung
from views.widgets.modern_message_box import show_error
show_error(self, "Export blockiert", error_msg)
return
# Neuer Feature 2: Profil-Export mit ProfileExportController
account_id = self.account_data.get("id")
if not account_id:
from views.widgets.modern_message_box import show_error
show_error(self, "Export fehlgeschlagen", "Account-ID nicht gefunden.")
return
# Hol db_manager aus dem Parent
parent_window = self.window()
if hasattr(parent_window, 'db_manager'):
from controllers.profile_export_controller import ProfileExportController
export_controller = ProfileExportController(parent_window.db_manager)
export_controller.export_account(self, account_id)
else:
# Fallback: Altes Signal emittieren (für Kompatibilität)
self.export_requested.emit(self.account_data)
def _on_delete_clicked(self):
"""Handler für Delete-Button"""
# Feature 5: Prüfe Guard-Status vor Löschen
from utils.process_guard import get_guard
guard = get_guard()
platform = self.account_data.get("platform", "Unknown")
can_start, error_msg = guard.can_start("Account-Löschen", platform)
if not can_start:
# Zeige benutzerfreundliche Fehlermeldung
from views.widgets.modern_message_box import show_error
show_error(self, "Löschen blockiert", error_msg)
return
self.delete_requested.emit(self.account_data)
def init_ui(self):
"""Initialisiert die UI nach Styleguide"""
self.setObjectName("accountCard")
# Status-basiertes Styling anwenden
self._apply_status_styling()
# Setze feste Breite für die Karte
self.setFixedWidth(360)
# Hauptlayout
layout = QVBoxLayout(self)
layout.setContentsMargins(16, 16, 16, 16)
layout.setSpacing(12)
# Header Zeile
header_layout = QHBoxLayout()
# Platform Icon + Username
info_layout = QHBoxLayout()
info_layout.setSpacing(8)
# Status wird jetzt über Karten-Hintergrund und Umrandung angezeigt
# Selection Checkbox (hidden by default)
self.selection_checkbox = QCheckBox()
self.selection_checkbox.setVisible(False)
self.selection_checkbox.stateChanged.connect(self._on_selection_changed)
self.selection_checkbox.setStyleSheet("""
QCheckBox::indicator {
width: 20px;
height: 20px;
border-radius: 4px;
border: 2px solid #D1D5DB;
}
QCheckBox::indicator:checked {
background-color: #0099CC;
border-color: #0099CC;
}
""")
info_layout.addWidget(self.selection_checkbox)
# Platform Icon
platform_icon = IconFactory.create_icon_label(
self.account_data.get("platform", "").lower(),
size=18
)
info_layout.addWidget(platform_icon)
# Username
username_label = QLabel(self.account_data.get("username", ""))
username_font = QFont("Poppins", 16)
username_font.setWeight(QFont.DemiBold)
username_label.setFont(username_font)
username_label.setObjectName("account_username") # For QSS targeting
info_layout.addWidget(username_label)
info_layout.addStretch()
header_layout.addLayout(info_layout)
# Login Button
self.login_btn = QPushButton("Login")
self.login_btn.setCursor(Qt.PointingHandCursor)
self.login_btn.setObjectName("account_login_btn") # For QSS targeting
self.login_btn.clicked.connect(lambda: self._on_login_clicked())
header_layout.addWidget(self.login_btn)
layout.addLayout(header_layout)
# Details Grid
details_grid = QGridLayout()
details_grid.setSpacing(8)
# Email
email_icon = IconFactory.create_icon_label("mail", size=14)
details_grid.addWidget(email_icon, 0, 0)
# Email container with copy button
email_container = QWidget()
email_layout = QHBoxLayout(email_container)
email_layout.setContentsMargins(0, 0, 0, 0)
email_layout.setSpacing(8)
email_label = QLabel(self.account_data.get("email", ""))
email_label.setObjectName("account_detail_text") # For QSS targeting
email_layout.addWidget(email_label)
# Email Copy Button
email_copy_btn = QPushButton()
email_copy_btn.setToolTip("E-Mail kopieren")
email_copy_btn.setCursor(Qt.PointingHandCursor)
email_copy_btn.setObjectName("account_icon_btn") # For QSS targeting
self.copy_icon = IconFactory.get_icon("copy", size=16)
self.check_icon = IconFactory.get_icon("check", size=16, color="#10B981")
self.email_copy_btn = email_copy_btn
self.email_copy_btn.setIcon(self.copy_icon)
self.email_copy_btn.setIconSize(QSize(16, 16))
email_copy_btn.clicked.connect(self._copy_email)
email_layout.addWidget(email_copy_btn)
email_layout.addStretch()
details_grid.addWidget(email_container, 0, 1)
# Password
pass_icon = IconFactory.create_icon_label("key", size=14)
details_grid.addWidget(pass_icon, 1, 0)
pass_container = QWidget()
pass_layout = QHBoxLayout(pass_container)
pass_layout.setContentsMargins(0, 0, 0, 0)
pass_layout.setSpacing(8)
self.password_label = QLabel("••••••••")
self.password_label.setObjectName("account_detail_text") # For QSS targeting
pass_layout.addWidget(self.password_label)
# Copy Button
copy_btn = QPushButton()
copy_btn.setToolTip("Passwort kopieren")
copy_btn.setCursor(Qt.PointingHandCursor)
copy_btn.setObjectName("account_icon_btn") # For QSS targeting
self.password_copy_btn = copy_btn
self.password_copy_btn.setIcon(self.copy_icon)
self.password_copy_btn.setIconSize(QSize(16, 16))
copy_btn.clicked.connect(self._copy_password)
pass_layout.addWidget(copy_btn)
# Show/Hide Button
self.visibility_btn = QPushButton()
self.visibility_btn.setToolTip("Passwort anzeigen")
self.visibility_btn.setCursor(Qt.PointingHandCursor)
self.visibility_btn.setObjectName("account_icon_btn") # For QSS targeting
self.eye_icon = IconFactory.get_icon("eye", size=16)
self.eye_slash_icon = IconFactory.get_icon("eye-slash", size=16)
self.visibility_btn.setIcon(self.eye_icon)
self.visibility_btn.setIconSize(QSize(16, 16))
self.visibility_btn.clicked.connect(self._toggle_password)
pass_layout.addWidget(self.visibility_btn)
pass_layout.addStretch()
details_grid.addWidget(pass_container, 1, 1)
# Created Date
date_icon = IconFactory.create_icon_label("calendar", size=14)
details_grid.addWidget(date_icon, 2, 0)
date_label = QLabel(self.account_data.get("created_at", ""))
date_label.setObjectName("account_date_text") # For QSS targeting
details_grid.addWidget(date_label, 2, 1)
layout.addLayout(details_grid)
# Action buttons
actions_layout = QHBoxLayout()
actions_layout.setSpacing(8)
# Export Button
self.export_btn = QPushButton("Profil\nexportieren")
self.export_btn.setCursor(Qt.PointingHandCursor)
self.export_btn.setObjectName("account_export_btn") # For QSS targeting
self.export_btn.clicked.connect(self._on_export_clicked)
actions_layout.addWidget(self.export_btn)
actions_layout.addStretch()
# Delete Button
self.delete_btn = QPushButton("Löschen")
self.delete_btn.setCursor(Qt.PointingHandCursor)
self.delete_btn.setObjectName("account_delete_btn") # For QSS targeting
self.delete_btn.clicked.connect(self._on_delete_clicked)
actions_layout.addWidget(self.delete_btn)
layout.addLayout(actions_layout)
def _toggle_password(self):
"""Zeigt/Versteckt das Passwort"""
self.password_visible = not self.password_visible
if self.password_visible:
self.password_label.setText(self.account_data.get("password", ""))
self.visibility_btn.setIcon(self.eye_slash_icon)
else:
self.password_label.setText("••••••••")
self.visibility_btn.setIcon(self.eye_icon)
def _copy_password(self):
"""Kopiert das Passwort in die Zwischenablage"""
clipboard = QApplication.clipboard()
clipboard.setText(self.account_data.get("password", ""))
# Icon zu Check wechseln
self.password_copy_btn.setIcon(self.check_icon)
# Timer starten um nach 2 Sekunden zurückzuwechseln
self.password_copy_timer.stop()
self.password_copy_timer.start(2000)
def _copy_email(self):
"""Kopiert die E-Mail in die Zwischenablage"""
clipboard = QApplication.clipboard()
clipboard.setText(self.account_data.get("email", ""))
# Icon zu Check wechseln
self.email_copy_btn.setIcon(self.check_icon)
# Timer starten um nach 2 Sekunden zurückzuwechseln
self.email_copy_timer.stop()
self.email_copy_timer.start(2000)
def update_login_button_state(self):
"""
Feature 5: Aktualisiert ALLE Button-Status basierend auf Process Guard.
Deaktiviert Login, Export und Delete wenn Guard locked ist oder Pause aktiv ist.
"""
from utils.process_guard import get_guard
guard = get_guard()
# Prüfe ob blockiert
is_locked = guard.is_locked()
is_paused = guard.is_paused()
is_blocked = is_locked or is_paused
# Alle Buttons entsprechend setzen
self.login_btn.setEnabled(not is_blocked)
self.export_btn.setEnabled(not is_blocked)
self.delete_btn.setEnabled(not is_blocked)
# Tooltips setzen
if is_blocked:
status_msg = guard.get_status_message()
self.login_btn.setToolTip(f"{status_msg}")
self.export_btn.setToolTip(f"{status_msg}")
self.delete_btn.setToolTip(f"{status_msg}")
else:
# Reset Tooltips
self.login_btn.setToolTip("Login durchführen")
self.export_btn.setToolTip("Profil exportieren")
self.delete_btn.setToolTip("Account löschen")
def update_texts(self):
"""Aktualisiert die Texte gemäß der aktuellen Sprache"""
if not self.language_manager:
return
self.login_btn.setText(
self.language_manager.get_text("account_card.login", "Login")
)
self.export_btn.setText(
self.language_manager.get_text("account_card.export", "Profil\nexportieren")
)
self.delete_btn.setText(
self.language_manager.get_text("account_card.delete", "Löschen")
)
def _restore_email_copy_icon(self):
"""Stellt das ursprüngliche Copy Icon für Email wieder her"""
self.email_copy_btn.setIcon(self.copy_icon)
self.email_copy_timer.stop()
def _restore_password_copy_icon(self):
"""Stellt das ursprüngliche Copy Icon für Passwort wieder her"""
self.password_copy_btn.setIcon(self.copy_icon)
self.password_copy_timer.stop()
def _apply_status_styling(self):
"""Wendet Status-basiertes Styling an"""
# Status aus Account-Daten lesen - Standard ist "active" (grün)
status = self.account_data.get("status", "active")
# Set a property that QSS can use for styling
self.setProperty("status", status)
# Force style refresh
self.setStyle(self.style())
def update_status(self, new_status: str):
"""Aktualisiert den Status der Account-Karte und das Styling"""
self.account_data["status"] = new_status
self._apply_status_styling()
# ========== Selection Mode Methods ==========
def set_selection_mode(self, enabled: bool):
"""
Aktiviert/Deaktiviert den Selection-Modus.
Args:
enabled: True = Checkbox anzeigen, False = Checkbox verstecken
"""
self.selection_mode = enabled
self.selection_checkbox.setVisible(enabled)
if not enabled:
# Reset selection when mode is disabled
self.set_selected(False)
def _on_selection_changed(self, state):
"""Handler für Checkbox state change"""
self.is_selected_state = (state == Qt.Checked)
account_id = self.account_data.get("id")
if account_id:
self.selection_changed.emit(account_id, self.is_selected_state)
def is_selected(self) -> bool:
"""
Gibt zurück ob die Card ausgewählt ist.
Returns:
True wenn ausgewählt, sonst False
"""
return self.is_selected_state
def set_selected(self, selected: bool):
"""
Setzt den Auswahl-Status.
Args:
selected: True = auswählen, False = Auswahl aufheben
"""
self.is_selected_state = selected
self.selection_checkbox.setChecked(selected)