import logging from PySide6.QtWidgets import QDialog, QTreeWidgetItem, QCheckBox, QMessageBox, QWidget, QHBoxLayout from PySide6.QtCore import Qt from pathlib import Path from ui.XmlToXslAssignDialog_ui import Ui_XmlToXslAssignDialog from conf import TreeNode, XslFile logger = logging.getLogger(__name__) class XmlToXslAssignDialog(QDialog): """Dialog zur Zuordnung einer XML-Datei zu XSL-Knoten.""" def __init__(self, parent=None, xml_file_path=None, project_nodes=None): """ Initialisiert den Dialog. Args: parent: Übergeordnetes Widget xml_file_path: Pfad zur XML-Datei project_nodes: Liste der Projekt-Knoten """ super().__init__(parent) # UI einrichten self.ui = Ui_XmlToXslAssignDialog() self.ui.setupUi(self) # Parameter speichern self.xml_file_path = Path(xml_file_path) if xml_file_path else None self.project_nodes = project_nodes or [] # Dictionary zum Speichern der Checkbox-Referenzen self.xsl_checkboxes = {} # {xsl_node_id: checkbox} # Signale verbinden self.ui.selectAllButton.clicked.connect(self.select_all) self.ui.deselectAllButton.clicked.connect(self.deselect_all) # Baum konfigurieren self._setup_tree() # Daten laden self._load_data() def _setup_tree(self): """Konfiguriert das TreeWidget.""" # Spaltenbreiten setzen self.ui.xslNodesTree.setColumnWidth(0, 300) # XSL-Knoten self.ui.xslNodesTree.setColumnWidth(1, 200) # Details self.ui.xslNodesTree.setColumnWidth(2, 100) # Auswählen # Header-Eigenschaften self.ui.xslNodesTree.header().setStretchLastSection(False) def _create_centered_checkbox(self): """ Erstellt eine zentrierte Checkbox in einem Widget. Returns: tuple: (widget, checkbox) - Das Container-Widget und die Checkbox """ # Erstelle Container-Widget widget = QWidget() # Erstelle horizontales Layout layout = QHBoxLayout(widget) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignmentFlag.AlignCenter) # Erstelle Checkbox checkbox = QCheckBox() checkbox.setChecked(False) # Füge Checkbox zum Layout hinzu layout.addWidget(checkbox) return widget, checkbox def _add_checkboxes_to_tree(self): """ Fügt Checkboxen zu allen XSL-Knoten im Tree hinzu. Diese Methode wird nach dem Hinzufügen aller Items aufgerufen. """ try: # Durchlaufe alle Items im Tree rekursiv root = self.ui.xslNodesTree.invisibleRootItem() self._add_checkboxes_recursive(root) logger.debug(f"Checkboxen zu {len(self.xsl_checkboxes)} XSL-Knoten hinzugefügt") except Exception as e: logger.error(f"Fehler beim Hinzufügen der Checkboxen: {e}") def _add_checkboxes_recursive(self, parent_item): """ Fügt rekursiv Checkboxen zu XSL-Knoten hinzu. Args: parent_item: Das Eltern-Item """ for i in range(parent_item.childCount()): item = parent_item.child(i) # Hole das Node-Objekt node = item.data(0, Qt.ItemDataRole.UserRole) if isinstance(node, XslFile): # Erstelle zentrierte Checkbox für XSL-Knoten checkbox_widget, checkbox = self._create_centered_checkbox() # Setze das Widget in Spalte 2 self.ui.xslNodesTree.setItemWidget(item, 2, checkbox_widget) # Speichere Checkbox-Referenz self.xsl_checkboxes[id(node)] = checkbox logger.debug(f"Checkbox für XSL-Knoten '{node.bez}' hinzugefügt") # Rekursiv für Kinder if item.childCount() > 0: self._add_checkboxes_recursive(item) def _load_data(self): """Lädt die Daten in den Dialog.""" # XML-Datei-Label setzen if self.xml_file_path: self.ui.xmlFileLabel.setText(f"XML-Datei: {self.xml_file_path.name}") # Projekt-Knoten in Baum laden self._load_project_nodes() def _load_project_nodes(self): """Lädt die Projekt-Knoten in das TreeWidget (ohne XML-Knoten). Sortiert die Items alphabetisch nach ihrer ID.""" if not self.project_nodes: return # TreeWidget leeren self.ui.xslNodesTree.clear() self.xsl_checkboxes.clear() # Sortiere Root-Nodes alphabetisch nach ID sorted_nodes = sorted(self.project_nodes, key=lambda node: node.id) # Alle Root-Nodes laden (sortiert) for node in sorted_nodes: tree_item = self._create_tree_item_from_node(node) if tree_item: # Nur hinzufügen wenn Item erstellt wurde self.ui.xslNodesTree.addTopLevelItem(tree_item) # Baum expandieren self.ui.xslNodesTree.expandAll() # Checkboxen nach dem Hinzufügen der Items erstellen self._add_checkboxes_to_tree() def _create_tree_item_from_node(self, node): """ Erstellt ein QTreeWidgetItem aus einem TreeNode oder XslFile. XML-Knoten werden ausgeschlossen. Args: node: TreeNode oder XslFile Objekt Returns: QTreeWidgetItem: Das erstellte Tree-Item oder None wenn ausgeschlossen """ try: # Erstelle Tree-Item item = QTreeWidgetItem() # Setze die Bezeichnung in Spalte 0 bez_text = str(node.bez) if node.bez else "" item.setText(0, bez_text) # Speichere das komplette Node-Objekt item.setData(0, Qt.ItemDataRole.UserRole, node) if isinstance(node, TreeNode): # TreeNode: Zeige Anzahl der Knoten child_count = len(node.children) if node.children else 0 item.setText(1, f"{child_count} Knoten") # Lade Knoten rekursiv (sortiert nach ID, nur TreeNode und XslFile, keine XML) if node.children: # Filtere und sortiere Kinder valid_children = [child for child in node.children if isinstance(child, (TreeNode, XslFile))] sorted_children = sorted(valid_children, key=lambda child: child.id) for child in sorted_children: child_item = self._create_tree_item_from_node(child) if child_item: item.addChild(child_item) elif isinstance(node, XslFile): # XslFile: Zeige XSL-Datei-Pfad item.setText(1, str(node.xsl_file)) # Checkbox wird später in _add_checkboxes_to_tree() hinzugefügt # Keine Knoten für XslFile hinzufügen (XML-Dateien werden ausgeschlossen) return item except Exception as e: logger.error(f"Fehler beim Erstellen des Tree-Items: {e}") return None def select_all(self): """Wählt alle XSL-Knoten aus.""" for checkbox in self.xsl_checkboxes.values(): checkbox.setChecked(True) def deselect_all(self): """Wählt alle XSL-Knoten ab.""" for checkbox in self.xsl_checkboxes.values(): checkbox.setChecked(False) def get_selected_xsl_nodes(self): """ Gibt die ausgewählten XSL-Knoten zurück. Returns: list[XslFile]: Liste der ausgewählten XSL-Knoten """ selected_nodes = [] try: # Durchlaufe alle XSL-Checkboxes for node_id, checkbox in self.xsl_checkboxes.items(): if checkbox.isChecked(): # Finde den entsprechenden XSL-Knoten xsl_node = self._find_xsl_node_by_id(node_id) if xsl_node: selected_nodes.append(xsl_node) return selected_nodes except Exception as e: logger.error(f"Fehler beim Sammeln der ausgewählten XSL-Knoten: {e}") return [] def _find_xsl_node_by_id(self, node_id): """ Findet einen XSL-Knoten anhand seiner ID. Args: node_id: Die ID des Knotens (Python id()) Returns: XslFile: Der gefundene XSL-Knoten oder None """ return self._find_xsl_node_recursive(self.project_nodes, node_id) def _find_xsl_node_recursive(self, nodes, target_id): """ Sucht rekursiv nach einem XSL-Knoten mit der angegebenen ID. Args: nodes: Liste der Nodes zum Durchsuchen target_id: Die zu suchende ID Returns: XslFile: Der gefundene XSL-Knoten oder None """ for node in nodes: if isinstance(node, XslFile) and id(node) == target_id: return node # Rekursiv in Knoten suchen (nur bei TreeNode) if isinstance(node, TreeNode) and node.children: found = self._find_xsl_node_recursive(node.children, target_id) if found: return found return None def get_xml_file_path(self): """ Gibt den Pfad zur XML-Datei zurück. Returns: Path: Pfad zur XML-Datei """ return self.xml_file_path def is_apply_to_all(self): """ Prüft, ob die Checkbox 'Alle XML-Dateien' aktiviert ist. Returns: bool: True wenn die Checkbox aktiviert ist, sonst False """ return self.ui.alle_xml.isChecked() def accept(self): """Überschreibt accept() um Validierung durchzuführen.""" selected_nodes = self.get_selected_xsl_nodes() if not selected_nodes: QMessageBox.warning( self, "Warnung", "Bitte wählen Sie mindestens einen XSL-Knoten aus." ) return super().accept()