Batch-Export geht jetzt
Dieser Commit ist enthalten in:
@ -176,9 +176,6 @@ class ProfileExportController:
|
|||||||
"""
|
"""
|
||||||
Exportiert mehrere Accounts gleichzeitig.
|
Exportiert mehrere Accounts gleichzeitig.
|
||||||
|
|
||||||
HINWEIS: Diese Funktion ist vorbereitet für zukünftige Erweiterung,
|
|
||||||
wird aber gemäß ROADMAP.md vorerst NICHT implementiert (YAGNI).
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parent_widget: Parent-Widget
|
parent_widget: Parent-Widget
|
||||||
account_ids: Liste von Account-IDs
|
account_ids: Liste von Account-IDs
|
||||||
@ -186,10 +183,154 @@ class ProfileExportController:
|
|||||||
Returns:
|
Returns:
|
||||||
True bei Erfolg, False bei Fehler
|
True bei Erfolg, False bei Fehler
|
||||||
"""
|
"""
|
||||||
show_warning(
|
try:
|
||||||
|
if not account_ids:
|
||||||
|
logger.warning("Keine Account-IDs zum Export übergeben")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Starte Batch-Export für {len(account_ids)} Accounts")
|
||||||
|
|
||||||
|
# 1. Alle Account-Daten laden
|
||||||
|
accounts_data = []
|
||||||
|
for account_id in account_ids:
|
||||||
|
account_data = self.db_manager.get_account(account_id)
|
||||||
|
if account_data:
|
||||||
|
accounts_data.append(account_data)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Account ID {account_id} nicht gefunden")
|
||||||
|
|
||||||
|
if not accounts_data:
|
||||||
|
show_error(
|
||||||
parent_widget,
|
parent_widget,
|
||||||
"Nicht verfügbar",
|
"Keine Accounts gefunden",
|
||||||
"Mehrfach-Export ist derzeit nicht verfügbar.\n"
|
"Keiner der ausgewählten Accounts konnte geladen werden."
|
||||||
"Bitte exportieren Sie Accounts einzeln."
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"{len(accounts_data)} Accounts erfolgreich geladen")
|
||||||
|
|
||||||
|
# 2. Export-Dialog anzeigen
|
||||||
|
accepted, formats, _ = show_export_dialog(parent_widget, f"{len(accounts_data)} Accounts")
|
||||||
|
|
||||||
|
if not accepted:
|
||||||
|
logger.info("Batch-Export abgebrochen durch Nutzer")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"Batch-Export-Optionen: Formate={formats}")
|
||||||
|
|
||||||
|
# 3. Hauptordner wählen
|
||||||
|
from datetime import datetime
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
|
||||||
|
suggested_folder_name = f"AccountForge_Export_{timestamp}"
|
||||||
|
|
||||||
|
save_directory = QFileDialog.getExistingDirectory(
|
||||||
|
parent_widget,
|
||||||
|
"Hauptordner für Batch-Export auswählen",
|
||||||
|
str(Path.home() / "Downloads" / suggested_folder_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not save_directory:
|
||||||
|
logger.info("Kein Speicherort ausgewählt - Export abgebrochen")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Hauptordner erstellen falls nicht vorhanden
|
||||||
|
os.makedirs(save_directory, exist_ok=True)
|
||||||
|
logger.info(f"Batch-Export-Ordner: {save_directory}")
|
||||||
|
|
||||||
|
# 4. Für jeden Account exportieren
|
||||||
|
exported_files = []
|
||||||
|
failed_accounts = []
|
||||||
|
|
||||||
|
for account_data in accounts_data:
|
||||||
|
username = account_data.get("username", "unknown")
|
||||||
|
platform = account_data.get("platform", "unknown").lower()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Plattform-Unterordner erstellen
|
||||||
|
platform_dir = os.path.join(save_directory, platform)
|
||||||
|
os.makedirs(platform_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Export durchführen
|
||||||
|
files_dict = self.export_service.export_account(
|
||||||
|
account_data,
|
||||||
|
formats,
|
||||||
|
password_protect=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dateien mit vereinfachten Namen speichern (ohne Timestamp)
|
||||||
|
for filename, content in files_dict.items():
|
||||||
|
# Vereinfachter Name: username.extension
|
||||||
|
ext = filename.split('.')[-1]
|
||||||
|
simple_filename = f"{username}.{ext}"
|
||||||
|
file_path = os.path.join(platform_dir, simple_filename)
|
||||||
|
|
||||||
|
with open(file_path, 'wb') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
exported_files.append(f"{platform}/{simple_filename}")
|
||||||
|
logger.info(f"Exportiert: {platform}/{simple_filename}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Export von {username}: {e}")
|
||||||
|
failed_accounts.append(username)
|
||||||
|
|
||||||
|
# 5. Summary-Datei erstellen
|
||||||
|
summary_path = os.path.join(save_directory, "export_summary.txt")
|
||||||
|
with open(summary_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(f"AccountForge Batch-Export\n")
|
||||||
|
f.write(f"="*50 + "\n\n")
|
||||||
|
f.write(f"Exportiert am: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n")
|
||||||
|
f.write(f"Anzahl Accounts: {len(accounts_data)}\n")
|
||||||
|
f.write(f"Erfolgreich: {len(accounts_data) - len(failed_accounts)}\n")
|
||||||
|
if failed_accounts:
|
||||||
|
f.write(f"Fehlgeschlagen: {len(failed_accounts)}\n")
|
||||||
|
f.write(f"\nFormate: {', '.join(formats).upper()}\n")
|
||||||
|
f.write(f"\n" + "="*50 + "\n\n")
|
||||||
|
|
||||||
|
# Gruppiere nach Plattform
|
||||||
|
platforms = {}
|
||||||
|
for account_data in accounts_data:
|
||||||
|
platform = account_data.get("platform", "unknown").lower()
|
||||||
|
if platform not in platforms:
|
||||||
|
platforms[platform] = []
|
||||||
|
platforms[platform].append(account_data.get("username", ""))
|
||||||
|
|
||||||
|
for platform, usernames in sorted(platforms.items()):
|
||||||
|
f.write(f"{platform.capitalize()}:\n")
|
||||||
|
for username in usernames:
|
||||||
|
if username in failed_accounts:
|
||||||
|
f.write(f" ✗ {username} (FEHLER)\n")
|
||||||
|
else:
|
||||||
|
f.write(f" ✓ {username}\n")
|
||||||
|
f.write(f"\n")
|
||||||
|
|
||||||
|
exported_files.append("export_summary.txt")
|
||||||
|
logger.info("Summary-Datei erstellt")
|
||||||
|
|
||||||
|
# 6. Erfolgs-Dialog anzeigen
|
||||||
|
show_export_success(
|
||||||
|
parent_widget,
|
||||||
|
exported_files,
|
||||||
|
save_directory
|
||||||
|
)
|
||||||
|
|
||||||
|
if failed_accounts:
|
||||||
|
show_warning(
|
||||||
|
parent_widget,
|
||||||
|
"Teilweise erfolgreich",
|
||||||
|
f"Export abgeschlossen, aber {len(failed_accounts)} Account(s) fehlgeschlagen:\n" +
|
||||||
|
"\n".join(f"- {name}" for name in failed_accounts[:5]) +
|
||||||
|
(f"\n... und {len(failed_accounts)-5} weitere" if len(failed_accounts) > 5 else "")
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Batch-Export erfolgreich: {len(exported_files)} Datei(en)")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Batch-Export: {e}", exc_info=True)
|
||||||
|
show_error(
|
||||||
|
parent_widget,
|
||||||
|
"Batch-Export fehlgeschlagen",
|
||||||
|
f"Beim Batch-Export ist ein Fehler aufgetreten:\n\n{str(e)}"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -5,7 +5,7 @@ Accounts Overview View - Account-Übersicht im Mockup-Style
|
|||||||
import logging
|
import logging
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton,
|
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton,
|
||||||
QScrollArea, QGridLayout, QFrame, QMessageBox
|
QScrollArea, QGridLayout, QFrame, QMessageBox, QCheckBox
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||||||
from PyQt5.QtGui import QFont
|
from PyQt5.QtGui import QFont
|
||||||
@ -106,6 +106,7 @@ class AccountsOverviewView(QWidget):
|
|||||||
Account-Übersicht im Mockup-Style mit:
|
Account-Übersicht im Mockup-Style mit:
|
||||||
- Sidebar-Filter
|
- Sidebar-Filter
|
||||||
- Grid-Layout mit Account-Karten
|
- Grid-Layout mit Account-Karten
|
||||||
|
- Batch-Export mit Checkbox-Auswahl
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Signals
|
# Signals
|
||||||
@ -113,6 +114,7 @@ class AccountsOverviewView(QWidget):
|
|||||||
account_export_requested = pyqtSignal(dict)
|
account_export_requested = pyqtSignal(dict)
|
||||||
account_delete_requested = pyqtSignal(dict)
|
account_delete_requested = pyqtSignal(dict)
|
||||||
export_requested = pyqtSignal() # Für Kompatibilität
|
export_requested = pyqtSignal() # Für Kompatibilität
|
||||||
|
bulk_export_requested = pyqtSignal(list) # List[int] - account_ids
|
||||||
|
|
||||||
def __init__(self, db_manager=None, language_manager=None):
|
def __init__(self, db_manager=None, language_manager=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -120,6 +122,9 @@ class AccountsOverviewView(QWidget):
|
|||||||
self.language_manager = language_manager
|
self.language_manager = language_manager
|
||||||
self.current_filter = "all"
|
self.current_filter = "all"
|
||||||
self.accounts = []
|
self.accounts = []
|
||||||
|
self.selected_account_ids = set() # Set of selected account IDs
|
||||||
|
self.account_cards = [] # List of AccountCard widgets
|
||||||
|
self._updating_selection = False # Flag to prevent selection loops
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
|
|
||||||
if self.language_manager:
|
if self.language_manager:
|
||||||
@ -159,8 +164,142 @@ class AccountsOverviewView(QWidget):
|
|||||||
|
|
||||||
header_layout.addStretch()
|
header_layout.addStretch()
|
||||||
|
|
||||||
|
# Selection Mode Button
|
||||||
|
self.selection_mode_btn = QPushButton("Exportieren")
|
||||||
|
self.selection_mode_btn.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.selection_mode_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #10B981;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
padding: 8px 16px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #059669;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #047857;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.selection_mode_btn.clicked.connect(self._enable_selection_mode)
|
||||||
|
header_layout.addWidget(self.selection_mode_btn)
|
||||||
|
|
||||||
content_layout.addLayout(header_layout)
|
content_layout.addLayout(header_layout)
|
||||||
|
|
||||||
|
# Selection Toolbar (initially hidden)
|
||||||
|
self.toolbar = QFrame()
|
||||||
|
self.toolbar.setObjectName("selection_toolbar")
|
||||||
|
self.toolbar.setVisible(False)
|
||||||
|
self.toolbar.setStyleSheet("""
|
||||||
|
#selection_toolbar {
|
||||||
|
background-color: #F3F4F6;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
toolbar_layout = QHBoxLayout(self.toolbar)
|
||||||
|
toolbar_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
toolbar_layout.setSpacing(16)
|
||||||
|
|
||||||
|
# "Alle auswählen" Checkbox
|
||||||
|
self.select_all_checkbox = QCheckBox("Alle auswählen")
|
||||||
|
self.select_all_checkbox.setTristate(True)
|
||||||
|
self.select_all_checkbox.setStyleSheet("""
|
||||||
|
QCheckBox {
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 2px solid #D1D5DB;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #0099CC;
|
||||||
|
border-color: #0099CC;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:indeterminate {
|
||||||
|
background-color: #0099CC;
|
||||||
|
border-color: #0099CC;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.select_all_checkbox.stateChanged.connect(self._on_select_all_changed)
|
||||||
|
toolbar_layout.addWidget(self.select_all_checkbox)
|
||||||
|
|
||||||
|
# Selection count label
|
||||||
|
self.selection_count_label = QLabel("0 ausgewählt")
|
||||||
|
self.selection_count_label.setStyleSheet("""
|
||||||
|
color: #6B7280;
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
""")
|
||||||
|
toolbar_layout.addWidget(self.selection_count_label)
|
||||||
|
|
||||||
|
toolbar_layout.addStretch()
|
||||||
|
|
||||||
|
# Export button
|
||||||
|
self.bulk_export_btn = QPushButton("Exportieren")
|
||||||
|
self.bulk_export_btn.setEnabled(False)
|
||||||
|
self.bulk_export_btn.setCursor(Qt.PointingHandCursor)
|
||||||
|
self.bulk_export_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: #0099CC;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
padding: 8px 16px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #0078A3;
|
||||||
|
}
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #005C7A;
|
||||||
|
}
|
||||||
|
QPushButton:disabled {
|
||||||
|
background-color: #D1D5DB;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.bulk_export_btn.clicked.connect(self._on_bulk_export_clicked)
|
||||||
|
toolbar_layout.addWidget(self.bulk_export_btn)
|
||||||
|
|
||||||
|
# Close selection mode button
|
||||||
|
close_btn = QPushButton("✕")
|
||||||
|
close_btn.setToolTip("Auswahl beenden")
|
||||||
|
close_btn.setCursor(Qt.PointingHandCursor)
|
||||||
|
close_btn.setStyleSheet("""
|
||||||
|
QPushButton {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #6B7280;
|
||||||
|
border: none;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 4px;
|
||||||
|
min-width: 24px;
|
||||||
|
max-width: 24px;
|
||||||
|
}
|
||||||
|
QPushButton:hover {
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
close_btn.clicked.connect(self._disable_selection_mode)
|
||||||
|
toolbar_layout.addWidget(close_btn)
|
||||||
|
|
||||||
|
content_layout.addWidget(self.toolbar)
|
||||||
|
|
||||||
# Scroll Area für Accounts
|
# Scroll Area für Accounts
|
||||||
self.scroll = QScrollArea()
|
self.scroll = QScrollArea()
|
||||||
self.scroll.setWidgetResizable(True)
|
self.scroll.setWidgetResizable(True)
|
||||||
@ -202,7 +341,8 @@ class AccountsOverviewView(QWidget):
|
|||||||
|
|
||||||
def _update_display(self):
|
def _update_display(self):
|
||||||
"""Aktualisiert die Anzeige basierend auf dem aktuellen Filter"""
|
"""Aktualisiert die Anzeige basierend auf dem aktuellen Filter"""
|
||||||
# Clear existing widgets
|
# Clear existing widgets and account_cards list
|
||||||
|
self.account_cards = []
|
||||||
while self.grid_layout.count():
|
while self.grid_layout.count():
|
||||||
child = self.grid_layout.takeAt(0)
|
child = self.grid_layout.takeAt(0)
|
||||||
if child.widget():
|
if child.widget():
|
||||||
@ -292,6 +432,10 @@ class AccountsOverviewView(QWidget):
|
|||||||
card.login_requested.connect(self.account_login_requested.emit)
|
card.login_requested.connect(self.account_login_requested.emit)
|
||||||
card.export_requested.connect(self.account_export_requested.emit)
|
card.export_requested.connect(self.account_export_requested.emit)
|
||||||
card.delete_requested.connect(self._on_delete_requested)
|
card.delete_requested.connect(self._on_delete_requested)
|
||||||
|
card.selection_changed.connect(self._on_card_selection_changed)
|
||||||
|
|
||||||
|
# Zu Liste hinzufügen für Batch-Operations
|
||||||
|
self.account_cards.append(card)
|
||||||
|
|
||||||
return card
|
return card
|
||||||
|
|
||||||
@ -377,3 +521,90 @@ class AccountsOverviewView(QWidget):
|
|||||||
"""
|
"""
|
||||||
# Session-Funktionalität wurde entfernt - diese Methode macht nichts mehr
|
# Session-Funktionalität wurde entfernt - diese Methode macht nichts mehr
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# ========== Selection Mode Methods ==========
|
||||||
|
|
||||||
|
def _enable_selection_mode(self):
|
||||||
|
"""Aktiviert den Selection-Modus"""
|
||||||
|
# Show toolbar, hide selection button
|
||||||
|
self.toolbar.setVisible(True)
|
||||||
|
self.selection_mode_btn.setVisible(False)
|
||||||
|
|
||||||
|
# Enable selection mode on all cards
|
||||||
|
for card in self.account_cards:
|
||||||
|
card.set_selection_mode(True)
|
||||||
|
|
||||||
|
# Reset selection state
|
||||||
|
self.selected_account_ids.clear()
|
||||||
|
self._update_selection_ui()
|
||||||
|
|
||||||
|
def _disable_selection_mode(self):
|
||||||
|
"""Deaktiviert den Selection-Modus"""
|
||||||
|
# Hide toolbar, show selection button
|
||||||
|
self.toolbar.setVisible(False)
|
||||||
|
self.selection_mode_btn.setVisible(True)
|
||||||
|
|
||||||
|
# Disable selection mode on all cards
|
||||||
|
for card in self.account_cards:
|
||||||
|
card.set_selection_mode(False)
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
self.selected_account_ids.clear()
|
||||||
|
self._update_selection_ui()
|
||||||
|
|
||||||
|
def _on_card_selection_changed(self, account_id: int, selected: bool):
|
||||||
|
"""Handler wenn eine Card ausgewählt/abgewählt wird"""
|
||||||
|
if selected:
|
||||||
|
self.selected_account_ids.add(account_id)
|
||||||
|
else:
|
||||||
|
self.selected_account_ids.discard(account_id)
|
||||||
|
|
||||||
|
self._update_selection_ui()
|
||||||
|
|
||||||
|
def _on_select_all_changed(self, state):
|
||||||
|
"""Handler für "Alle auswählen" Checkbox"""
|
||||||
|
if self._updating_selection:
|
||||||
|
return
|
||||||
|
|
||||||
|
select_all = (state == Qt.Checked)
|
||||||
|
|
||||||
|
# Set all cards to the same selection state
|
||||||
|
self._updating_selection = True
|
||||||
|
for card in self.account_cards:
|
||||||
|
card.set_selected(select_all)
|
||||||
|
self._updating_selection = False
|
||||||
|
|
||||||
|
def _update_selection_ui(self):
|
||||||
|
"""Aktualisiert die Selection-UI (Count-Label, Button-Status)"""
|
||||||
|
count = len(self.selected_account_ids)
|
||||||
|
|
||||||
|
# Update count label
|
||||||
|
if count == 0:
|
||||||
|
self.selection_count_label.setText("0 ausgewählt")
|
||||||
|
elif count == 1:
|
||||||
|
self.selection_count_label.setText("1 ausgewählt")
|
||||||
|
else:
|
||||||
|
self.selection_count_label.setText(f"{count} ausgewählt")
|
||||||
|
|
||||||
|
# Enable/Disable export button
|
||||||
|
self.bulk_export_btn.setEnabled(count > 0)
|
||||||
|
|
||||||
|
# Update "Alle auswählen" checkbox state
|
||||||
|
total_cards = len(self.account_cards)
|
||||||
|
if total_cards > 0:
|
||||||
|
self._updating_selection = True
|
||||||
|
if count == 0:
|
||||||
|
self.select_all_checkbox.setCheckState(Qt.Unchecked)
|
||||||
|
elif count == total_cards:
|
||||||
|
self.select_all_checkbox.setCheckState(Qt.Checked)
|
||||||
|
else:
|
||||||
|
self.select_all_checkbox.setCheckState(Qt.PartiallyChecked)
|
||||||
|
self._updating_selection = False
|
||||||
|
|
||||||
|
def _on_bulk_export_clicked(self):
|
||||||
|
"""Handler für Bulk-Export-Button"""
|
||||||
|
if len(self.selected_account_ids) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Emit signal with list of selected account IDs
|
||||||
|
self.bulk_export_requested.emit(list(self.selected_account_ids))
|
||||||
@ -60,6 +60,7 @@ class PlatformSelector(QWidget):
|
|||||||
self.accounts_overview.account_login_requested.connect(self._on_login_requested)
|
self.accounts_overview.account_login_requested.connect(self._on_login_requested)
|
||||||
self.accounts_overview.account_export_requested.connect(self._on_export_requested)
|
self.accounts_overview.account_export_requested.connect(self._on_export_requested)
|
||||||
self.accounts_overview.account_delete_requested.connect(self._on_delete_requested)
|
self.accounts_overview.account_delete_requested.connect(self._on_delete_requested)
|
||||||
|
self.accounts_overview.bulk_export_requested.connect(self._on_bulk_export_requested)
|
||||||
self.content_stack.addWidget(self.accounts_overview)
|
self.content_stack.addWidget(self.accounts_overview)
|
||||||
|
|
||||||
# Für Kompatibilität mit MainController - accounts_tab Referenz
|
# Für Kompatibilität mit MainController - accounts_tab Referenz
|
||||||
@ -123,6 +124,13 @@ class PlatformSelector(QWidget):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler beim Löschen des Accounts: {e}")
|
print(f"Fehler beim Löschen des Accounts: {e}")
|
||||||
|
|
||||||
|
def _on_bulk_export_requested(self, account_ids):
|
||||||
|
"""Behandelt Bulk-Export-Anfragen."""
|
||||||
|
from controllers.profile_export_controller import ProfileExportController
|
||||||
|
if self.db_manager:
|
||||||
|
controller = ProfileExportController(self.db_manager)
|
||||||
|
controller.export_multiple_accounts(self, account_ids)
|
||||||
|
|
||||||
def update_texts(self):
|
def update_texts(self):
|
||||||
"""Aktualisiert die Texte gemäß der aktuellen Sprache."""
|
"""Aktualisiert die Texte gemäß der aktuellen Sprache."""
|
||||||
# Die Komponenten aktualisieren ihre Texte selbst
|
# Die Komponenten aktualisieren ihre Texte selbst
|
||||||
|
|||||||
@ -4,7 +4,7 @@ Account Card Widget - Kompakte Account-Karte nach Styleguide
|
|||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QFrame, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
QFrame, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||||||
QGridLayout, QWidget, QApplication
|
QGridLayout, QWidget, QApplication, QCheckBox
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import Qt, pyqtSignal, QSize, QTimer
|
from PyQt5.QtCore import Qt, pyqtSignal, QSize, QTimer
|
||||||
from PyQt5.QtGui import QFont, QPixmap
|
from PyQt5.QtGui import QFont, QPixmap
|
||||||
@ -22,12 +22,15 @@ class AccountCard(QFrame):
|
|||||||
login_requested = pyqtSignal(dict) # Account-Daten
|
login_requested = pyqtSignal(dict) # Account-Daten
|
||||||
export_requested = pyqtSignal(dict) # Account-Daten
|
export_requested = pyqtSignal(dict) # Account-Daten
|
||||||
delete_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):
|
def __init__(self, account_data, language_manager=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.account_data = account_data
|
self.account_data = account_data
|
||||||
self.language_manager = language_manager
|
self.language_manager = language_manager
|
||||||
self.password_visible = False
|
self.password_visible = False
|
||||||
|
self.selection_mode = False
|
||||||
|
self.is_selected_state = False
|
||||||
|
|
||||||
# Timer für Icon-Animation
|
# Timer für Icon-Animation
|
||||||
self.email_copy_timer = QTimer()
|
self.email_copy_timer = QTimer()
|
||||||
@ -143,6 +146,24 @@ class AccountCard(QFrame):
|
|||||||
|
|
||||||
# Status wird jetzt über Karten-Hintergrund und Umrandung angezeigt
|
# 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
|
||||||
platform_icon = IconFactory.create_icon_label(
|
platform_icon = IconFactory.create_icon_label(
|
||||||
self.account_data.get("platform", "").lower(),
|
self.account_data.get("platform", "").lower(),
|
||||||
@ -379,3 +400,45 @@ class AccountCard(QFrame):
|
|||||||
"""Aktualisiert den Status der Account-Karte und das Styling"""
|
"""Aktualisiert den Status der Account-Karte und das Styling"""
|
||||||
self.account_data["status"] = new_status
|
self.account_data["status"] = new_status
|
||||||
self._apply_status_styling()
|
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)
|
||||||
In neuem Issue referenzieren
Einen Benutzer sperren