diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index c7bf710..d02595c 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -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( @@ -4233,7 +4248,7 @@ class MainWindow(QMainWindow): bool: True wenn erfolgreich, False bei Fehler """ pdf_basename = diff_pdf_path.name # Initialisiere am Anfang für Exception-Handler - + try: # Prüfe ob new-PDF existiert if not new_pdf_path.exists(): @@ -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")