Export drin noch nicht komplett

Dieser Commit ist enthalten in:
Claude Project Manager
2025-11-10 03:43:02 +01:00
Ursprung 14eefa18f6
Commit 88dc8eea5e
8 geänderte Dateien mit 1473 neuen und 4 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,389 @@
"""
Erfolgs-Dialog für Profil-Export
Zeigt exportierte Dateien und optional das generierte Passwort an.
"""
import os
import logging
import subprocess
import platform
from pathlib import Path
from typing import List, Optional
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QFrame, QGraphicsDropShadowEffect, QApplication, QScrollArea, QWidget
)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QColor
logger = logging.getLogger("export_success_dialog")
class ExportSuccessDialog(QDialog):
"""Dialog zur Anzeige von Export-Erfolg"""
def __init__(
self,
parent=None,
exported_files: List[str] = None,
export_directory: str = "",
password: Optional[str] = None
):
"""
Initialisiert den Erfolgs-Dialog.
Args:
parent: Parent-Widget
exported_files: Liste der exportierten Dateinamen
export_directory: Verzeichnis wo Dateien gespeichert wurden
password: Optional generiertes Passwort (nur bei Passwortschutz)
"""
super().__init__(parent)
self.exported_files = exported_files or []
self.export_directory = export_directory
self.password = password
self.init_ui()
def init_ui(self):
"""Initialisiert die UI"""
# Dialog-Eigenschaften
self.setWindowTitle("Export erfolgreich")
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setModal(True)
# Dynamische Höhe basierend auf Inhalt
base_height = 280
if self.password:
base_height += 80 # Extra Platz für Passwort-Anzeige
if len(self.exported_files) > 2:
base_height += 20 * (len(self.exported_files) - 2) # Extra für mehr Dateien
self.setFixedSize(500, min(base_height, 500))
# Hauptlayout
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(20, 20, 20, 20)
# Container mit modernem Design
self.container = QFrame()
self.container.setObjectName("exportSuccessContainer")
self.container.setStyleSheet("""
#exportSuccessContainer {
background-color: white;
border-radius: 12px;
border: 1px solid #E5E7EB;
}
""")
# Schatten-Effekt
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(30)
shadow.setXOffset(0)
shadow.setYOffset(10)
shadow.setColor(QColor(0, 0, 0, 80))
self.container.setGraphicsEffect(shadow)
# Container-Layout
container_layout = QVBoxLayout(self.container)
container_layout.setSpacing(18)
container_layout.setContentsMargins(30, 25, 30, 25)
# Header mit Icon und Titel
header_layout = QHBoxLayout()
header_layout.setSpacing(15)
# Erfolgs-Icon
icon_label = QLabel("")
icon_label.setStyleSheet("font-size: 32px;")
icon_label.setFixedSize(40, 40)
icon_label.setAlignment(Qt.AlignCenter)
# Titel
title_label = QLabel("Profil exportiert!")
title_font = QFont("Poppins", 16)
title_font.setWeight(QFont.DemiBold)
title_label.setFont(title_font)
title_label.setStyleSheet("color: #1F2937;")
header_layout.addWidget(icon_label)
header_layout.addWidget(title_label, 1)
container_layout.addLayout(header_layout)
# Dateien-Liste
files_label = QLabel("Dateien:")
files_label.setStyleSheet("color: #6B7280; font-size: 13px; font-weight: 600;")
container_layout.addWidget(files_label)
# Scrollbare Liste für Dateien
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setFrameShape(QFrame.NoFrame)
scroll_area.setStyleSheet("""
QScrollArea {
background-color: #F9FAFB;
border-radius: 6px;
border: 1px solid #E5E7EB;
}
""")
files_widget = QWidget()
files_layout = QVBoxLayout(files_widget)
files_layout.setSpacing(4)
files_layout.setContentsMargins(10, 10, 10, 10)
for filename in self.exported_files:
file_label = QLabel(f"{filename}")
file_label.setStyleSheet("""
color: #374151;
font-size: 12px;
font-family: 'Courier New', monospace;
padding: 2px;
""")
file_label.setWordWrap(True)
files_layout.addWidget(file_label)
files_layout.addStretch()
scroll_area.setWidget(files_widget)
scroll_area.setMaximumHeight(120)
container_layout.addWidget(scroll_area)
# Passwort-Anzeige (nur wenn Passwort vorhanden)
if self.password:
password_frame = QFrame()
password_frame.setStyleSheet("""
QFrame {
background-color: #FEF3C7;
border: 1px solid #FCD34D;
border-radius: 6px;
padding: 12px;
}
""")
password_layout = QVBoxLayout(password_frame)
password_layout.setSpacing(8)
# Warnung
warning_label = QLabel("⚠ Bitte Passwort speichern!")
warning_label.setStyleSheet("""
color: #92400E;
font-size: 13px;
font-weight: 600;
""")
# Passwort mit Copy-Button
password_row = QHBoxLayout()
password_row.setSpacing(10)
password_label = QLabel(f"🔒 Passwort: {self.password}")
password_label.setStyleSheet("""
color: #78350F;
font-size: 13px;
font-family: 'Courier New', monospace;
font-weight: 600;
""")
password_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
copy_button = QPushButton("Kopieren")
copy_button.setMaximumWidth(80)
copy_button.setMinimumHeight(28)
copy_button.setCursor(Qt.PointingHandCursor)
copy_button.setStyleSheet("""
QPushButton {
background-color: #F59E0B;
color: white;
border: none;
border-radius: 4px;
font-size: 12px;
font-weight: 600;
padding: 4px 8px;
}
QPushButton:hover {
background-color: #D97706;
}
QPushButton:pressed {
background-color: #B45309;
}
""")
copy_button.clicked.connect(self.copy_password)
password_row.addWidget(password_label, 1)
password_row.addWidget(copy_button)
password_layout.addWidget(warning_label)
password_layout.addLayout(password_row)
container_layout.addWidget(password_frame)
# Speicherort
location_label = QLabel(f"Gespeichert in:")
location_label.setStyleSheet("color: #6B7280; font-size: 12px;")
path_label = QLabel(self._format_path(self.export_directory))
path_label.setStyleSheet("""
color: #4B5563;
font-size: 11px;
font-family: 'Courier New', monospace;
""")
path_label.setWordWrap(True)
path_label.setTextInteractionFlags(Qt.TextSelectableByMouse)
container_layout.addWidget(location_label)
container_layout.addWidget(path_label)
# Spacer
container_layout.addStretch()
# Button-Layout
button_layout = QHBoxLayout()
button_layout.setSpacing(10)
button_layout.addStretch()
# Ordner öffnen Button
self.open_folder_button = QPushButton("Ordner öffnen")
self.open_folder_button.setMinimumHeight(38)
self.open_folder_button.setMinimumWidth(120)
self.open_folder_button.setCursor(Qt.PointingHandCursor)
self.open_folder_button.setStyleSheet("""
QPushButton {
background-color: #F3F4F6;
color: #374151;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
padding: 8px 16px;
}
QPushButton:hover {
background-color: #E5E7EB;
}
QPushButton:pressed {
background-color: #D1D5DB;
}
""")
self.open_folder_button.clicked.connect(self.open_folder)
# OK Button
self.ok_button = QPushButton("OK")
self.ok_button.setMinimumHeight(38)
self.ok_button.setMinimumWidth(100)
self.ok_button.setCursor(Qt.PointingHandCursor)
self.ok_button.setStyleSheet("""
QPushButton {
background-color: #10B981;
color: white;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
padding: 8px 16px;
}
QPushButton:hover {
background-color: #059669;
}
QPushButton:pressed {
background-color: #047857;
}
""")
self.ok_button.clicked.connect(self.accept)
button_layout.addWidget(self.open_folder_button)
button_layout.addWidget(self.ok_button)
container_layout.addLayout(button_layout)
# Container zum Hauptlayout hinzufügen
main_layout.addWidget(self.container)
def copy_password(self):
"""Kopiert das Passwort in die Zwischenablage"""
if self.password:
clipboard = QApplication.clipboard()
clipboard.setText(self.password)
logger.info("Passwort in Zwischenablage kopiert")
# Kurzes visuelles Feedback
sender = self.sender()
if sender:
original_text = sender.text()
sender.setText("✓ Kopiert!")
sender.setStyleSheet(sender.styleSheet().replace("#F59E0B", "#10B981"))
# Nach 2 Sekunden zurücksetzen
from PyQt5.QtCore import QTimer
QTimer.singleShot(2000, lambda: self._reset_copy_button(sender, original_text))
def _reset_copy_button(self, button, original_text):
"""Setzt den Copy-Button zurück"""
if button:
button.setText(original_text)
button.setStyleSheet(button.styleSheet().replace("#10B981", "#F59E0B"))
def open_folder(self):
"""Öffnet den Export-Ordner im Datei-Explorer"""
if not self.export_directory or not os.path.exists(self.export_directory):
logger.warning(f"Export-Verzeichnis existiert nicht: {self.export_directory}")
return
try:
system = platform.system()
if system == "Windows":
# Windows Explorer
os.startfile(self.export_directory)
elif system == "Darwin":
# macOS Finder
subprocess.run(["open", self.export_directory])
elif system == "Linux":
# Linux File Manager
subprocess.run(["xdg-open", self.export_directory])
else:
logger.warning(f"Unbekanntes Betriebssystem: {system}")
logger.info(f"Ordner geöffnet: {self.export_directory}")
except Exception as e:
logger.error(f"Fehler beim Öffnen des Ordners: {e}")
def _format_path(self, path: str) -> str:
"""
Formatiert den Pfad für die Anzeige.
Kürzt zu lange Pfade.
"""
if not path:
return ""
max_length = 60
if len(path) <= max_length:
return path
# Pfad kürzen: Anfang ... Ende
start_length = 25
end_length = max_length - start_length - 3
return f"{path[:start_length]}...{path[-end_length:]}"
# Helper-Funktion für einfache Verwendung
def show_export_success(
parent,
exported_files: List[str],
export_directory: str,
password: Optional[str] = None
):
"""
Zeigt den Export-Erfolgs-Dialog modal an.
Args:
parent: Parent-Widget
exported_files: Liste der exportierten Dateinamen
export_directory: Verzeichnis wo Dateien gespeichert wurden
password: Optional generiertes Passwort
"""
dialog = ExportSuccessDialog(parent, exported_files, export_directory, password)
dialog.exec_()

Datei anzeigen

@ -0,0 +1,324 @@
"""
Export-Dialog für Account-Profile
Ermöglicht Auswahl von Export-Formaten und Passwortschutz.
"""
import logging
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
QCheckBox, QFrame, QGraphicsDropShadowEffect, QGroupBox
)
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QFont, QColor
logger = logging.getLogger("profile_export_dialog")
class ProfileExportDialog(QDialog):
"""Dialog zur Auswahl von Export-Optionen"""
# Signal wird emittiert wenn Export bestätigt wird
# Parameter: (formats: List[str], password_protect: bool)
export_confirmed = pyqtSignal(list, bool)
def __init__(self, parent=None, account_username: str = ""):
"""
Initialisiert den Export-Dialog.
Args:
parent: Parent-Widget
account_username: Username des zu exportierenden Accounts
"""
super().__init__(parent)
self.account_username = account_username
self.init_ui()
def init_ui(self):
"""Initialisiert die UI"""
# Dialog-Eigenschaften
self.setWindowTitle("Profil exportieren")
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground, True)
self.setModal(True)
self.setFixedSize(450, 600)
# Hauptlayout
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(20, 20, 20, 20)
# Container mit modernem Design
self.container = QFrame()
self.container.setObjectName("exportDialogContainer")
self.container.setStyleSheet("""
#exportDialogContainer {
background-color: white;
border-radius: 12px;
border: 1px solid #E5E7EB;
}
""")
# Schatten-Effekt
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(30)
shadow.setXOffset(0)
shadow.setYOffset(10)
shadow.setColor(QColor(0, 0, 0, 80))
self.container.setGraphicsEffect(shadow)
# Container-Layout
container_layout = QVBoxLayout(self.container)
container_layout.setSpacing(20)
container_layout.setContentsMargins(30, 25, 30, 25)
# Titel
title_label = QLabel("Profil exportieren")
title_font = QFont("Poppins", 16)
title_font.setWeight(QFont.DemiBold)
title_label.setFont(title_font)
title_label.setStyleSheet("color: #1F2937;")
container_layout.addWidget(title_label)
# Account-Info
if self.account_username:
account_label = QLabel(f"Account: {self.account_username}")
account_label.setStyleSheet("color: #6B7280; font-size: 13px; font-family: 'Poppins', sans-serif;")
container_layout.addWidget(account_label)
# Abstand vor Format-GroupBox
container_layout.addSpacing(15)
# Format-Auswahl GroupBox
format_group = QGroupBox("Format")
format_group.setMinimumHeight(150) # Mehr Platz für 3 Checkboxen
format_group.setStyleSheet("""
QGroupBox {
font-size: 14px;
font-weight: 600;
font-family: 'Poppins', sans-serif;
color: #374151;
border: 1px solid #E5E7EB;
border-radius: 8px;
margin-top: 20px;
padding-top: 30px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
left: 12px;
top: 2px;
padding: 2px 8px 2px 8px;
background-color: white;
}
""")
format_layout = QVBoxLayout(format_group)
format_layout.setSpacing(12)
format_layout.setContentsMargins(20, 25, 20, 20)
# Format-Checkboxen
self.csv_checkbox = QCheckBox("CSV")
self.csv_checkbox.setChecked(False) # Standard: nicht aktiviert
self.csv_checkbox.setStyleSheet("""
QCheckBox {
font-size: 13px;
font-family: 'Poppins', sans-serif;
color: #374151;
spacing: 8px;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
border-radius: 4px;
border: 2px solid #D1D5DB;
}
QCheckBox::indicator:checked {
background-color: #0099CC;
border-color: #0099CC;
}
""")
self.txt_checkbox = QCheckBox("TXT")
self.txt_checkbox.setChecked(False) # Standard: nicht aktiviert
self.txt_checkbox.setStyleSheet(self.csv_checkbox.styleSheet())
self.pdf_checkbox = QCheckBox("PDF")
self.pdf_checkbox.setChecked(False) # Standard: nicht aktiviert
self.pdf_checkbox.setStyleSheet(self.csv_checkbox.styleSheet())
# Checkboxen zum Format-Layout hinzufügen
format_layout.addWidget(self.csv_checkbox)
format_layout.addWidget(self.txt_checkbox)
format_layout.addWidget(self.pdf_checkbox)
container_layout.addWidget(format_group)
# Passwortschutz-Checkbox
self.password_checkbox = QCheckBox("Mit Passwort schützen")
self.password_checkbox.setChecked(False)
self.password_checkbox.setStyleSheet("""
QCheckBox {
font-size: 13px;
font-family: 'Poppins', sans-serif;
font-weight: 500;
color: #374151;
spacing: 8px;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
border-radius: 4px;
border: 2px solid #D1D5DB;
}
QCheckBox::indicator:checked {
background-color: #10B981;
border-color: #10B981;
}
""")
# Info-Text für Passwortschutz
password_info = QLabel("(Wird automatisch generiert)")
password_info.setStyleSheet("color: #9CA3AF; font-size: 11px; font-family: 'Poppins', sans-serif; margin-left: 26px;")
container_layout.addWidget(self.password_checkbox)
container_layout.addWidget(password_info)
# Spacer
container_layout.addStretch()
# Button-Layout
button_layout = QHBoxLayout()
button_layout.setSpacing(10)
button_layout.addStretch()
# Abbrechen-Button
self.cancel_button = QPushButton("Abbrechen")
self.cancel_button.setMinimumHeight(38)
self.cancel_button.setMinimumWidth(100)
self.cancel_button.setCursor(Qt.PointingHandCursor)
self.cancel_button.setStyleSheet("""
QPushButton {
background-color: #F3F4F6;
color: #374151;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
font-family: 'Poppins', sans-serif;
padding: 8px 16px;
}
QPushButton:hover {
background-color: #E5E7EB;
}
QPushButton:pressed {
background-color: #D1D5DB;
}
""")
self.cancel_button.clicked.connect(self.reject)
# Exportieren-Button
self.export_button = QPushButton("Exportieren")
self.export_button.setMinimumHeight(38)
self.export_button.setMinimumWidth(120)
self.export_button.setCursor(Qt.PointingHandCursor)
self.export_button.setStyleSheet("""
QPushButton {
background-color: #0099CC;
color: white;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
font-family: 'Poppins', sans-serif;
padding: 8px 16px;
}
QPushButton:hover {
background-color: #0078A3;
}
QPushButton:pressed {
background-color: #005C7A;
}
""")
self.export_button.clicked.connect(self.on_export_clicked)
button_layout.addWidget(self.cancel_button)
button_layout.addWidget(self.export_button)
container_layout.addLayout(button_layout)
# Container zum Hauptlayout hinzufügen
main_layout.addWidget(self.container)
def on_export_clicked(self):
"""Handler für Export-Button"""
# Sammle ausgewählte Formate
selected_formats = []
if self.csv_checkbox.isChecked():
selected_formats.append("csv")
if self.txt_checkbox.isChecked():
selected_formats.append("txt")
if self.pdf_checkbox.isChecked():
selected_formats.append("pdf")
# Validierung: Mindestens ein Format muss ausgewählt sein
if not selected_formats:
from views.widgets.modern_message_box import show_warning
show_warning(
self,
"Kein Format ausgewählt",
"Bitte wählen Sie mindestens ein Export-Format aus."
)
return
# Passwortschutz-Option
password_protect = self.password_checkbox.isChecked()
# Signal emittieren
self.export_confirmed.emit(selected_formats, password_protect)
# Dialog schließen
self.accept()
def get_selected_options(self):
"""
Gibt die ausgewählten Optionen zurück.
Returns:
Tuple: (formats: List[str], password_protect: bool)
"""
formats = []
if self.csv_checkbox.isChecked():
formats.append("csv")
if self.txt_checkbox.isChecked():
formats.append("txt")
if self.pdf_checkbox.isChecked():
formats.append("pdf")
password_protect = self.password_checkbox.isChecked()
return formats, password_protect
# Helper-Funktionen für einfache Verwendung
def show_export_dialog(parent, account_username: str = ""):
"""
Zeigt den Export-Dialog modal an.
Args:
parent: Parent-Widget
account_username: Username des Accounts
Returns:
Tuple: (accepted: bool, formats: List[str], password_protect: bool)
accepted ist True wenn Export bestätigt wurde
"""
dialog = ProfileExportDialog(parent, account_username)
result = dialog.exec_()
if result == QDialog.Accepted:
formats, password_protect = dialog.get_selected_options()
return True, formats, password_protect
else:
return False, [], False

Datei anzeigen

@ -218,7 +218,30 @@ class AccountsTab(QWidget):
def on_export_clicked(self):
"""Wird aufgerufen, wenn der Exportieren-Button geklickt wird."""
self.export_requested.emit()
# Prüfen ob ein Account ausgewählt ist
selected_rows = self.accounts_table.selectionModel().selectedRows()
if not selected_rows:
title = "Kein Konto ausgewählt"
text = "Bitte wählen Sie ein Konto zum Exportieren aus."
if self.language_manager:
title = self.language_manager.get_text(
"accounts_tab.no_selection_title", title
)
text = self.language_manager.get_text(
"accounts_tab.no_export_selection_text", text
)
from views.widgets.modern_message_box import show_warning
show_warning(self, title, text)
return
# Account-ID holen
row = selected_rows[0].row()
account_id = int(self.accounts_table.item(row, 0).text())
# ProfileExportController verwenden
from controllers.profile_export_controller import ProfileExportController
export_controller = ProfileExportController(self.db_manager)
export_controller.export_account(self, account_id)
def update_session_status(self, account_id: str, status: dict):
"""

Datei anzeigen

@ -85,7 +85,22 @@ class AccountCard(QFrame):
show_error(self, "Export blockiert", error_msg)
return
self.export_requested.emit(self.account_data)
# 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"""