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_started = Signal(str, str) # xml_file_name, xsl_id_str
|
||||||
job_finished = Signal(dict) # result_dict
|
job_finished = Signal(dict) # result_dict
|
||||||
job_error = Signal(str, str, str) # xml_file_name, xsl_id_str, error_message
|
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):
|
def __init__(self, jobs: list[TransformationJob], force: bool = False):
|
||||||
"""
|
"""
|
||||||
@@ -407,6 +407,9 @@ class TransformationThread(QThread):
|
|||||||
"""
|
"""
|
||||||
Führt alle Transformations-Jobs sequenziell aus.
|
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")
|
logger.info(f"Starte Transformation von {len(self.jobs)} Jobs")
|
||||||
|
|
||||||
for job in self.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 ""
|
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)
|
self.job_error.emit(str(job.xml_file), xsl_id_str, error_msg)
|
||||||
|
|
||||||
# Sende Abschluss-Signal für alle Jobs
|
# Berechne Gesamtdauer
|
||||||
self.all_jobs_finished.emit(self.successful_count, len(self.jobs))
|
total_duration = (datetime.now() - start_time).total_seconds()
|
||||||
logger.info(f"Transformation abgeschlossen: {self.successful_count}/{len(self.jobs)} erfolgreich")
|
|
||||||
|
# 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):
|
class MainWindow(QMainWindow):
|
||||||
@@ -954,7 +962,9 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
if not selected_items or not self.project:
|
if not selected_items or not self.project:
|
||||||
# Keine Selektion oder kein Projekt - Viewer leeren
|
# 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:
|
if self.pdf_documents:
|
||||||
self._clear_pdf_viewer()
|
self._clear_pdf_viewer()
|
||||||
return
|
return
|
||||||
@@ -1080,6 +1090,7 @@ class MainWindow(QMainWindow):
|
|||||||
Returns:
|
Returns:
|
||||||
QTreeWidgetItem oder None wenn nicht gefunden
|
QTreeWidgetItem oder None wenn nicht gefunden
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def search_recursive(item):
|
def search_recursive(item):
|
||||||
"""Rekursive Suche durch TreeWidget."""
|
"""Rekursive Suche durch TreeWidget."""
|
||||||
# Prüfe aktuelles Item
|
# Prüfe aktuelles Item
|
||||||
@@ -2721,9 +2732,7 @@ class MainWindow(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Zeige Dialog für die erste Datei
|
# Zeige Dialog für die erste Datei
|
||||||
dialog = XmlToXslAssignDialog(
|
dialog = XmlToXslAssignDialog(parent=self, xml_file_path=xml_files[0], project_nodes=self.pdf_project.nodes)
|
||||||
parent=self, xml_file_path=xml_files[0], project_nodes=self.pdf_project.nodes
|
|
||||||
)
|
|
||||||
|
|
||||||
if dialog.exec() != XmlToXslAssignDialog.DialogCode.Accepted:
|
if dialog.exec() != XmlToXslAssignDialog.DialogCode.Accepted:
|
||||||
logger.debug("Dialog abgebrochen - keine Dateien verarbeitet")
|
logger.debug("Dialog abgebrochen - keine Dateien verarbeitet")
|
||||||
@@ -3703,7 +3712,7 @@ class MainWindow(QMainWindow):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True wenn mindestens eine XML-Datei gefunden wurde
|
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
|
return False
|
||||||
|
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
@@ -3731,7 +3740,7 @@ class MainWindow(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
pairs = []
|
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
|
return pairs
|
||||||
|
|
||||||
# Durchlaufe alle Kinder des TreeNode
|
# Durchlaufe alle Kinder des TreeNode
|
||||||
@@ -3845,23 +3854,23 @@ class MainWindow(QMainWindow):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Zusätzliche Sicherheitsprüfung für path_to_binary_file Attribute
|
# 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")
|
QMessageBox.warning(self, "Konfigurationsfehler", "Java VM Pfad ist nicht konfiguriert")
|
||||||
return None
|
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")
|
QMessageBox.warning(self, "Konfigurationsfehler", "Saxon JAR Pfad ist nicht konfiguriert")
|
||||||
return None
|
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")
|
QMessageBox.warning(self, "Konfigurationsfehler", "Apache FOP Pfad ist nicht konfiguriert")
|
||||||
return None
|
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")
|
QMessageBox.warning(self, "Konfigurationsfehler", "diff-pdf Pfad ist nicht konfiguriert")
|
||||||
return None
|
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")
|
QMessageBox.warning(self, "Konfigurationsfehler", "XSL-Verzeichnis Pfad ist nicht konfiguriert")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -3885,9 +3894,7 @@ class MainWindow(QMainWindow):
|
|||||||
# 2. Überschreibe mit XslFile-eigenen Parametern (höchste Priorität)
|
# 2. Überschreibe mit XslFile-eigenen Parametern (höchste Priorität)
|
||||||
xslt_params.update(xsl_file_obj.xslt_params)
|
xslt_params.update(xsl_file_obj.xslt_params)
|
||||||
|
|
||||||
logger.info(
|
logger.info(f"Finale XSLT-Parameter für {xml_file_obj.xml} mit {xsl_file_obj.bez}: {xslt_params}")
|
||||||
f"Finale XSLT-Parameter für {xml_file_obj.xml} mit {xsl_file_obj.bez}: {xslt_params}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Erstelle TransformationJob
|
# Erstelle TransformationJob
|
||||||
job = TransformationJob(
|
job = TransformationJob(
|
||||||
@@ -4078,15 +4085,18 @@ class MainWindow(QMainWindow):
|
|||||||
self.ui.treeWidget.removeItemWidget(tree_item, 2)
|
self.ui.treeWidget.removeItemWidget(tree_item, 2)
|
||||||
logger.debug(f"Progress Bar für {map_key} entfernt (Fehler)")
|
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.
|
Signal-Handler: Alle Jobs wurden abgeschlossen.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
successful_count: Anzahl erfolgreicher Jobs
|
successful_count: Anzahl erfolgreicher Jobs
|
||||||
total_count: Gesamtanzahl der 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
|
# Verstecke Transformation-Progressbar
|
||||||
self._hide_transformation_progress_bar()
|
self._hide_transformation_progress_bar()
|
||||||
@@ -4095,20 +4105,25 @@ class MainWindow(QMainWindow):
|
|||||||
self._update_all_diff_pdf_counts()
|
self._update_all_diff_pdf_counts()
|
||||||
self._update_diff_icons_for_existing_pdfs()
|
self._update_diff_icons_for_existing_pdfs()
|
||||||
|
|
||||||
|
# Formatiere Dauer für Anzeige
|
||||||
|
duration_str = f"{total_duration:.2f}s"
|
||||||
|
|
||||||
if successful_count == total_count:
|
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(
|
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:
|
else:
|
||||||
failed_count = total_count - successful_count
|
failed_count = total_count - successful_count
|
||||||
self.statusBar().showMessage(
|
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(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
"Abgeschlossen mit Fehlern",
|
"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(
|
def _collect_all_diff_pdfs_under_node(
|
||||||
@@ -4341,7 +4356,9 @@ class MainWindow(QMainWindow):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
failed_count = len(diff_pdfs) - successful_count
|
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(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
"Teilweise erfolgreich",
|
"Teilweise erfolgreich",
|
||||||
@@ -4435,6 +4452,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# Verarbeite alle pending Qt Events um sicherzustellen, dass Widgets/Ressourcen freigegeben werden
|
# Verarbeite alle pending Qt Events um sicherzustellen, dass Widgets/Ressourcen freigegeben werden
|
||||||
from PySide6.QtWidgets import QApplication
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
QApplication.processEvents()
|
QApplication.processEvents()
|
||||||
|
|
||||||
logger.info("PDF-Dokumente geschlossen und UI geleert vor Dateioperationen")
|
logger.info("PDF-Dokumente geschlossen und UI geleert vor Dateioperationen")
|
||||||
|
|||||||
Reference in New Issue
Block a user