Batch-Export geht jetzt
Dieser Commit ist enthalten in:
@ -5,7 +5,7 @@ Accounts Overview View - Account-Übersicht im Mockup-Style
|
||||
import logging
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPushButton,
|
||||
QScrollArea, QGridLayout, QFrame, QMessageBox
|
||||
QScrollArea, QGridLayout, QFrame, QMessageBox, QCheckBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from PyQt5.QtGui import QFont
|
||||
@ -106,20 +106,25 @@ class AccountsOverviewView(QWidget):
|
||||
Account-Übersicht im Mockup-Style mit:
|
||||
- Sidebar-Filter
|
||||
- Grid-Layout mit Account-Karten
|
||||
- Batch-Export mit Checkbox-Auswahl
|
||||
"""
|
||||
|
||||
|
||||
# Signals
|
||||
account_login_requested = pyqtSignal(dict)
|
||||
account_export_requested = pyqtSignal(dict)
|
||||
account_delete_requested = pyqtSignal(dict)
|
||||
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):
|
||||
super().__init__()
|
||||
self.db_manager = db_manager
|
||||
self.language_manager = language_manager
|
||||
self.current_filter = "all"
|
||||
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()
|
||||
|
||||
if self.language_manager:
|
||||
@ -156,11 +161,145 @@ class AccountsOverviewView(QWidget):
|
||||
self.title.setFont(title_font)
|
||||
self.title.setObjectName("section_title") # For QSS targeting
|
||||
header_layout.addWidget(self.title)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# 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
|
||||
self.scroll = QScrollArea()
|
||||
self.scroll.setWidgetResizable(True)
|
||||
@ -202,7 +341,8 @@ class AccountsOverviewView(QWidget):
|
||||
|
||||
def _update_display(self):
|
||||
"""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():
|
||||
child = self.grid_layout.takeAt(0)
|
||||
if child.widget():
|
||||
@ -287,12 +427,16 @@ class AccountsOverviewView(QWidget):
|
||||
def _create_account_card(self, account_data):
|
||||
"""Erstellt eine Account-Karte"""
|
||||
card = AccountCard(account_data, self.language_manager)
|
||||
|
||||
|
||||
# Verbinde Signals
|
||||
card.login_requested.connect(self.account_login_requested.emit)
|
||||
card.export_requested.connect(self.account_export_requested.emit)
|
||||
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
|
||||
|
||||
def _on_delete_requested(self, account_data):
|
||||
@ -376,4 +520,91 @@ class AccountsOverviewView(QWidget):
|
||||
Session-Status-Update deaktiviert (Session-Funktionalität entfernt).
|
||||
"""
|
||||
# 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_export_requested.connect(self._on_export_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)
|
||||
|
||||
# Für Kompatibilität mit MainController - accounts_tab Referenz
|
||||
@ -123,6 +124,13 @@ class PlatformSelector(QWidget):
|
||||
except Exception as 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):
|
||||
"""Aktualisiert die Texte gemäß der aktuellen Sprache."""
|
||||
# Die Komponenten aktualisieren ihre Texte selbst
|
||||
|
||||
@ -4,7 +4,7 @@ Account Card Widget - Kompakte Account-Karte nach Styleguide
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QFrame, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||||
QGridLayout, QWidget, QApplication
|
||||
QGridLayout, QWidget, QApplication, QCheckBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QSize, QTimer
|
||||
from PyQt5.QtGui import QFont, QPixmap
|
||||
@ -17,17 +17,20 @@ 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()
|
||||
@ -136,16 +139,34 @@ class AccountCard(QFrame):
|
||||
|
||||
# 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(),
|
||||
self.account_data.get("platform", "").lower(),
|
||||
size=18
|
||||
)
|
||||
info_layout.addWidget(platform_icon)
|
||||
@ -378,4 +399,46 @@ class AccountCard(QFrame):
|
||||
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()
|
||||
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