UX-Verbesserung: Gesamtdauer in Transformations-Zusammenfassung
Erweitert die Zusammenfassung nach Abschluss aller Transformationen um die Gesamtdauer: - TransformationThread misst jetzt die Gesamtdauer aller Jobs - Signal all_jobs_finished erweitert um total_duration Parameter - Statusbar und MessageBox zeigen Gesamtdauer an (Format: "12.34s") - Dauer wird sowohl bei Erfolg als auch bei Fehlern angezeigt 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
+43
-25
@@ -388,7 +388,7 @@ class TransformationThread(QThread):
|
||||
job_started = Signal(str, str) # xml_file_name, xsl_id_str
|
||||
job_finished = Signal(dict) # result_dict
|
||||
job_error = Signal(str, str, str) # xml_file_name, xsl_id_str, error_message
|
||||
all_jobs_finished = Signal(int, int) # successful_count, total_count
|
||||
all_jobs_finished = Signal(int, int, float) # successful_count, total_count, total_duration
|
||||
|
||||
def __init__(self, jobs: list[TransformationJob], force: bool = False):
|
||||
"""
|
||||
@@ -407,6 +407,9 @@ class TransformationThread(QThread):
|
||||
"""
|
||||
Führt alle Transformations-Jobs sequenziell aus.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
start_time = datetime.now()
|
||||
logger.info(f"Starte Transformation von {len(self.jobs)} Jobs")
|
||||
|
||||
for job in self.jobs:
|
||||
@@ -430,9 +433,14 @@ class TransformationThread(QThread):
|
||||
xsl_id_str = "_".join(str(x) for x in job.xsl_id) if job.xsl_id else ""
|
||||
self.job_error.emit(str(job.xml_file), xsl_id_str, error_msg)
|
||||
|
||||
# Sende Abschluss-Signal für alle Jobs
|
||||
self.all_jobs_finished.emit(self.successful_count, len(self.jobs))
|
||||
logger.info(f"Transformation abgeschlossen: {self.successful_count}/{len(self.jobs)} erfolgreich")
|
||||
# Berechne Gesamtdauer
|
||||
total_duration = (datetime.now() - start_time).total_seconds()
|
||||
|
||||
# Sende Abschluss-Signal für alle Jobs mit Gesamtdauer
|
||||
self.all_jobs_finished.emit(self.successful_count, len(self.jobs), total_duration)
|
||||
logger.info(
|
||||
f"Transformation abgeschlossen: {self.successful_count}/{len(self.jobs)} erfolgreich ({total_duration:.2f}s)"
|
||||
)
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
@@ -954,7 +962,9 @@ class MainWindow(QMainWindow):
|
||||
|
||||
if not selected_items or not self.project:
|
||||
# Keine Selektion oder kein Projekt - Viewer leeren
|
||||
logger.debug(f"Keine Selektion oder kein Projekt: selected_items={len(selected_items) if selected_items else 0}, project={self.project is not None}")
|
||||
logger.debug(
|
||||
f"Keine Selektion oder kein Projekt: selected_items={len(selected_items) if selected_items else 0}, project={self.project is not None}"
|
||||
)
|
||||
if self.pdf_documents:
|
||||
self._clear_pdf_viewer()
|
||||
return
|
||||
@@ -1080,6 +1090,7 @@ class MainWindow(QMainWindow):
|
||||
Returns:
|
||||
QTreeWidgetItem oder None wenn nicht gefunden
|
||||
"""
|
||||
|
||||
def search_recursive(item):
|
||||
"""Rekursive Suche durch TreeWidget."""
|
||||
# Prüfe aktuelles Item
|
||||
@@ -2721,9 +2732,7 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
|
||||
# Zeige Dialog für die erste Datei
|
||||
dialog = XmlToXslAssignDialog(
|
||||
parent=self, xml_file_path=xml_files[0], project_nodes=self.pdf_project.nodes
|
||||
)
|
||||
dialog = XmlToXslAssignDialog(parent=self, xml_file_path=xml_files[0], project_nodes=self.pdf_project.nodes)
|
||||
|
||||
if dialog.exec() != XmlToXslAssignDialog.DialogCode.Accepted:
|
||||
logger.debug("Dialog abgebrochen - keine Dateien verarbeitet")
|
||||
@@ -3703,7 +3712,7 @@ class MainWindow(QMainWindow):
|
||||
Returns:
|
||||
bool: True wenn mindestens eine XML-Datei gefunden wurde
|
||||
"""
|
||||
if not hasattr(node, 'children') or not node.children:
|
||||
if not hasattr(node, "children") or not node.children:
|
||||
return False
|
||||
|
||||
for child in node.children:
|
||||
@@ -3731,7 +3740,7 @@ class MainWindow(QMainWindow):
|
||||
"""
|
||||
pairs = []
|
||||
|
||||
if not hasattr(tree_node, 'children') or not tree_node.children:
|
||||
if not hasattr(tree_node, "children") or not tree_node.children:
|
||||
return pairs
|
||||
|
||||
# Durchlaufe alle Kinder des TreeNode
|
||||
@@ -3845,23 +3854,23 @@ class MainWindow(QMainWindow):
|
||||
return None
|
||||
|
||||
# Zusätzliche Sicherheitsprüfung für path_to_binary_file Attribute
|
||||
if java_vm is None or not hasattr(java_vm, 'path_to_binary_file') or java_vm.path_to_binary_file is None:
|
||||
if java_vm is None or not hasattr(java_vm, "path_to_binary_file") or java_vm.path_to_binary_file is None:
|
||||
QMessageBox.warning(self, "Konfigurationsfehler", "Java VM Pfad ist nicht konfiguriert")
|
||||
return None
|
||||
|
||||
if saxon_jar is None or not hasattr(saxon_jar, 'path_to_jar_file') or saxon_jar.path_to_jar_file is None:
|
||||
if saxon_jar is None or not hasattr(saxon_jar, "path_to_jar_file") or saxon_jar.path_to_jar_file is None:
|
||||
QMessageBox.warning(self, "Konfigurationsfehler", "Saxon JAR Pfad ist nicht konfiguriert")
|
||||
return None
|
||||
|
||||
if apache_fop is None or not hasattr(apache_fop, 'path_to_dir') or apache_fop.path_to_dir is None:
|
||||
if apache_fop is None or not hasattr(apache_fop, "path_to_dir") or apache_fop.path_to_dir is None:
|
||||
QMessageBox.warning(self, "Konfigurationsfehler", "Apache FOP Pfad ist nicht konfiguriert")
|
||||
return None
|
||||
|
||||
if diff_pdf is None or not hasattr(diff_pdf, 'path_to_binary_file') or diff_pdf.path_to_binary_file is None:
|
||||
if diff_pdf is None or not hasattr(diff_pdf, "path_to_binary_file") or diff_pdf.path_to_binary_file is None:
|
||||
QMessageBox.warning(self, "Konfigurationsfehler", "diff-pdf Pfad ist nicht konfiguriert")
|
||||
return None
|
||||
|
||||
if xsl_dir is None or not hasattr(xsl_dir, 'path_to_root_dir') or xsl_dir.path_to_root_dir is None:
|
||||
if xsl_dir is None or not hasattr(xsl_dir, "path_to_root_dir") or xsl_dir.path_to_root_dir is None:
|
||||
QMessageBox.warning(self, "Konfigurationsfehler", "XSL-Verzeichnis Pfad ist nicht konfiguriert")
|
||||
return None
|
||||
|
||||
@@ -3885,9 +3894,7 @@ class MainWindow(QMainWindow):
|
||||
# 2. Überschreibe mit XslFile-eigenen Parametern (höchste Priorität)
|
||||
xslt_params.update(xsl_file_obj.xslt_params)
|
||||
|
||||
logger.info(
|
||||
f"Finale XSLT-Parameter für {xml_file_obj.xml} mit {xsl_file_obj.bez}: {xslt_params}"
|
||||
)
|
||||
logger.info(f"Finale XSLT-Parameter für {xml_file_obj.xml} mit {xsl_file_obj.bez}: {xslt_params}")
|
||||
|
||||
# Erstelle TransformationJob
|
||||
job = TransformationJob(
|
||||
@@ -4078,15 +4085,18 @@ class MainWindow(QMainWindow):
|
||||
self.ui.treeWidget.removeItemWidget(tree_item, 2)
|
||||
logger.debug(f"Progress Bar für {map_key} entfernt (Fehler)")
|
||||
|
||||
def _on_all_transformations_finished(self, successful_count: int, total_count: int):
|
||||
def _on_all_transformations_finished(self, successful_count: int, total_count: int, total_duration: float):
|
||||
"""
|
||||
Signal-Handler: Alle Jobs wurden abgeschlossen.
|
||||
|
||||
Args:
|
||||
successful_count: Anzahl erfolgreicher Jobs
|
||||
total_count: Gesamtanzahl der Jobs
|
||||
total_duration: Gesamtdauer aller Transformationen in Sekunden
|
||||
"""
|
||||
logger.info(f"Alle Transformationen abgeschlossen: {successful_count}/{total_count} erfolgreich")
|
||||
logger.info(
|
||||
f"Alle Transformationen abgeschlossen: {successful_count}/{total_count} erfolgreich ({total_duration:.2f}s)"
|
||||
)
|
||||
|
||||
# Verstecke Transformation-Progressbar
|
||||
self._hide_transformation_progress_bar()
|
||||
@@ -4095,20 +4105,25 @@ class MainWindow(QMainWindow):
|
||||
self._update_all_diff_pdf_counts()
|
||||
self._update_diff_icons_for_existing_pdfs()
|
||||
|
||||
# Formatiere Dauer für Anzeige
|
||||
duration_str = f"{total_duration:.2f}s"
|
||||
|
||||
if successful_count == total_count:
|
||||
self.statusBar().showMessage(f"✓ Alle {total_count} Transformationen erfolgreich", 5000)
|
||||
self.statusBar().showMessage(f"✓ Alle {total_count} Transformationen erfolgreich ({duration_str})", 5000)
|
||||
QMessageBox.information(
|
||||
self, "Abgeschlossen", f"Alle {total_count} Transformationen wurden erfolgreich abgeschlossen"
|
||||
self,
|
||||
"Abgeschlossen",
|
||||
f"Alle {total_count} Transformationen wurden erfolgreich abgeschlossen.\n\nGesamtdauer: {duration_str}",
|
||||
)
|
||||
else:
|
||||
failed_count = total_count - successful_count
|
||||
self.statusBar().showMessage(
|
||||
f"⚠ {successful_count}/{total_count} erfolgreich, {failed_count} fehlgeschlagen", 5000
|
||||
f"⚠ {successful_count}/{total_count} erfolgreich, {failed_count} fehlgeschlagen ({duration_str})", 5000
|
||||
)
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Abgeschlossen mit Fehlern",
|
||||
f"{successful_count} von {total_count} Transformationen erfolgreich\n{failed_count} fehlgeschlagen",
|
||||
f"{successful_count} von {total_count} Transformationen erfolgreich\n{failed_count} fehlgeschlagen\n\nGesamtdauer: {duration_str}",
|
||||
)
|
||||
|
||||
def _collect_all_diff_pdfs_under_node(
|
||||
@@ -4341,7 +4356,9 @@ class MainWindow(QMainWindow):
|
||||
)
|
||||
else:
|
||||
failed_count = len(diff_pdfs) - successful_count
|
||||
logger.warning(f"{successful_count}/{len(diff_pdfs)} Änderungen übernommen, {failed_count} fehlgeschlagen")
|
||||
logger.warning(
|
||||
f"{successful_count}/{len(diff_pdfs)} Änderungen übernommen, {failed_count} fehlgeschlagen"
|
||||
)
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Teilweise erfolgreich",
|
||||
@@ -4435,6 +4452,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# Verarbeite alle pending Qt Events um sicherzustellen, dass Widgets/Ressourcen freigegeben werden
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
QApplication.processEvents()
|
||||
|
||||
logger.info("PDF-Dokumente geschlossen und UI geleert vor Dateioperationen")
|
||||
|
||||
Reference in New Issue
Block a user