From e6b27436777a642ef1d96d849a8e5f96246c3910 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Sun, 1 Feb 2026 15:44:55 +0100 Subject: [PATCH] Feature: Expand-Status von Tree-Knoten bei jedem Speichern persistent sichern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProjectData um optionales Feld 'expanded_nodes' erweitert (abwärtskompatibel) - _save_project_settings() speichert nun automatisch den Expand-Status - Expand-Status wird bei allen Speicheroperationen gesichert: * Beim Bearbeiten von TreeNodes und XslFiles * Bei Drag&Drop-Operationen im Tree * Bei Hash-Berechnungen für XML-Dateien * Beim Laden von Daten aus der Datenbank * Beim Beenden der Anwendung - Beim Laden eines Projekts werden aufgeklappte Knoten wiederhergestellt - Rekursive Speicherung und Wiederherstellung für TreeNode und XslFile - Umfassendes Logging für Debugging und Fehlerbehandlung --- src/conf.py | 2 + src/ui/MainWindow.py | 8 +++ src/ui/mixins/tree_manager.py | 103 ++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/src/conf.py b/src/conf.py index 6bde665..35beca2 100644 --- a/src/conf.py +++ b/src/conf.py @@ -75,6 +75,7 @@ class SSLMode(str, Enum): class XsltVersion(str, Enum): """XSLT-Version für Saxon-Transformationen.""" + XSLT_1_0 = "1.0" # JAXP API (nur XSLT 1.0) XSLT_2_0_3_0 = "2.0/3.0" # s9api (XSLT 2.0 und 3.0) @@ -216,6 +217,7 @@ class ProjectData(BaseModel): """ nodes: list[TreeNode] = [] + expanded_nodes: list[tuple] | None = None # Optional: IDs der aufgeklappten Knoten (TreeNode und XslFile) @classmethod def readSettings(cls, project_dir: Path): diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index 6f09d6e..8ef64c0 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -964,6 +964,14 @@ class MainWindow( # UI-Zustände speichern self._save_ui_state() + # Speichere Projekt-Einstellungen inkl. Expand-Status (falls Projekt geladen) + if hasattr(self, "project") and self.project and hasattr(self, "pdf_project") and self.pdf_project: + try: + self._save_project_settings() + logger.info("Projekt-Einstellungen beim Beenden gespeichert") + except Exception as e: + logger.error(f"Fehler beim Speichern der Projekt-Einstellungen: {e}") + # Stoppe Hash-Berechnungs-Thread falls noch aktiv if ( hasattr(self, "hash_calculator_thread") diff --git a/src/ui/mixins/tree_manager.py b/src/ui/mixins/tree_manager.py index c7fa4a7..b05afd8 100644 --- a/src/ui/mixins/tree_manager.py +++ b/src/ui/mixins/tree_manager.py @@ -519,6 +519,9 @@ class TreeManagerMixin: self._update_all_diff_pdf_counts() self._update_diff_icons_for_existing_pdfs() + # Stelle Expand-Status wieder her + self._restore_expanded_state() + except Exception as e: logger.error(f"Fehler beim Laden der Nodes in TreeWidget: {e}") @@ -1177,6 +1180,9 @@ class TreeManagerMixin: start_time = time.time() + # Speichere Expand-Status der Tree-Knoten vor dem Schreiben + self._save_expanded_state() + # Speichere in project.yaml im Projekt-Verzeichnis self.pdf_project.writeSettings(project_dir=self.project.project_dir) @@ -1186,3 +1192,100 @@ class TreeManagerMixin: except Exception as e: logger.error(f"Fehler beim Speichern der Projekt-Einstellungen: {e}") raise + + def _save_expanded_state(self): + """ + Speichert die IDs aller aufgeklappten Knoten (TreeNode und XslFile) in den Projekteinstellungen. + """ + if not hasattr(self, "pdf_project") or not self.pdf_project: + logger.warning("Keine Projekt-Einstellungen zum Speichern des Expand-Status") + return + + try: + expanded_node_ids = [] + + # Durchlaufe alle Top-Level-Items + root_count = self.ui.treeWidget.topLevelItemCount() + for i in range(root_count): + item = self.ui.treeWidget.topLevelItem(i) + self._collect_expanded_items(item, expanded_node_ids) + + # Speichere in Projekteinstellungen + self.pdf_project.expanded_nodes = expanded_node_ids + logger.info(f"Expand-Status gespeichert: {len(expanded_node_ids)} aufgeklappte Knoten") + logger.debug(f"Aufgeklappte Knoten-IDs: {expanded_node_ids}") + + except Exception as e: + logger.error(f"Fehler beim Speichern des Expand-Status: {e}") + + def _collect_expanded_items(self, item: QTreeWidgetItem, expanded_ids: list): + """ + Sammelt rekursiv die IDs aller aufgeklappten Items. + + Args: + item: Das zu prüfende TreeWidgetItem + expanded_ids: Liste zum Sammeln der IDs + """ + # Hole Node-Objekt + node = item.data(0, Qt.ItemDataRole.UserRole) + if not node: + return + + # Wenn Item aufgeklappt ist, speichere ID + if item.isExpanded() and hasattr(node, "id"): + expanded_ids.append(node.id) + + # Rekursiv alle Kinder durchlaufen + child_count = item.childCount() + for i in range(child_count): + child_item = item.child(i) + self._collect_expanded_items(child_item, expanded_ids) + + def _restore_expanded_state(self): + """ + Stellt den Expand-Status aller Knoten aus den Projekteinstellungen wieder her. + """ + if not hasattr(self, "pdf_project") or not self.pdf_project: + logger.warning("Keine Projekt-Einstellungen zum Wiederherstellen des Expand-Status") + return + + if not self.pdf_project.expanded_nodes: + logger.debug("Keine gespeicherten Expand-Status-Informationen vorhanden") + return + + try: + expanded_node_ids = set(self.pdf_project.expanded_nodes) + logger.info(f"Stelle Expand-Status wieder her: {len(expanded_node_ids)} Knoten") + logger.debug(f"Aufzuklappende Knoten-IDs: {list(expanded_node_ids)}") + + # Durchlaufe alle Top-Level-Items + root_count = self.ui.treeWidget.topLevelItemCount() + for i in range(root_count): + item = self.ui.treeWidget.topLevelItem(i) + self._expand_items_by_id(item, expanded_node_ids) + + except Exception as e: + logger.error(f"Fehler beim Wiederherstellen des Expand-Status: {e}") + + def _expand_items_by_id(self, item: QTreeWidgetItem, expanded_ids: set): + """ + Klappt Items rekursiv auf, wenn ihre ID in expanded_ids enthalten ist. + + Args: + item: Das zu prüfende TreeWidgetItem + expanded_ids: Set der IDs, die aufgeklappt werden sollen + """ + # Hole Node-Objekt + node = item.data(0, Qt.ItemDataRole.UserRole) + if not node or not hasattr(node, "id"): + return + + # Wenn ID in der Liste ist, klappe Item auf + if node.id in expanded_ids: + item.setExpanded(True) + + # Rekursiv alle Kinder durchlaufen + child_count = item.childCount() + for i in range(child_count): + child_item = item.child(i) + self._expand_items_by_id(child_item, expanded_ids)