2025-12-20 19:39:23 +01:00
|
|
|
|
import logging
|
2025-08-31 17:37:51 +02:00
|
|
|
|
from PySide6.QtWidgets import QDialog, QTreeWidgetItem, QCheckBox, QMessageBox, QWidget, QHBoxLayout
|
2025-08-31 17:04:22 +02:00
|
|
|
|
from PySide6.QtCore import Qt
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
from ui.XmlToXslAssignDialog_ui import Ui_XmlToXslAssignDialog
|
2025-08-31 17:06:24 +02:00
|
|
|
|
from conf import TreeNode, XslFile
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
class XmlToXslAssignDialog(QDialog):
|
|
|
|
|
|
"""Dialog zur Zuordnung einer XML-Datei zu XSL-Knoten."""
|
|
|
|
|
|
|
2026-04-18 21:10:34 +02:00
|
|
|
|
def __init__(
|
|
|
|
|
|
self,
|
|
|
|
|
|
parent=None,
|
|
|
|
|
|
xml_file_path=None,
|
|
|
|
|
|
project_nodes=None,
|
|
|
|
|
|
preselected_xsl_ids: set | None = None,
|
|
|
|
|
|
edit_mode: bool = False,
|
|
|
|
|
|
):
|
2025-08-31 17:04:22 +02:00
|
|
|
|
"""
|
|
|
|
|
|
Initialisiert den Dialog.
|
2026-04-18 21:10:34 +02:00
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
Args:
|
|
|
|
|
|
parent: Übergeordnetes Widget
|
|
|
|
|
|
xml_file_path: Pfad zur XML-Datei
|
|
|
|
|
|
project_nodes: Liste der Projekt-Knoten
|
2026-04-18 21:10:34 +02:00
|
|
|
|
preselected_xsl_ids: Set von XslFile-IDs (tuple), deren Checkbox initial angehakt sein soll
|
|
|
|
|
|
edit_mode: True = Bearbeiten-Modus (Zuordnungen ändern), False = Neu-Zuordnen
|
2025-08-31 17:04:22 +02:00
|
|
|
|
"""
|
|
|
|
|
|
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 []
|
2026-04-18 21:10:34 +02:00
|
|
|
|
self.preselected_xsl_ids: set = set(preselected_xsl_ids) if preselected_xsl_ids else set()
|
|
|
|
|
|
self.edit_mode = edit_mode
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
# Dictionary zum Speichern der Checkbox-Referenzen
|
2026-04-18 21:10:34 +02:00
|
|
|
|
self.xsl_checkboxes = {} # {python_id(node): checkbox}
|
|
|
|
|
|
self.xsl_nodes = {} # {python_id(node): XslFile}
|
|
|
|
|
|
# Initial ausgewählte XslFile-IDs (tuple), um Diff beim Accept zu berechnen
|
|
|
|
|
|
self._initial_selected_ids: set = set()
|
|
|
|
|
|
|
|
|
|
|
|
# Edit-Modus: UI anpassen
|
|
|
|
|
|
if self.edit_mode:
|
|
|
|
|
|
self.setWindowTitle("XML-Zuordnungen bearbeiten")
|
|
|
|
|
|
self.ui.infoLabel.setText(
|
|
|
|
|
|
"Passen Sie die Zuordnungen der XML-Datei an. Hinzufügen per Haken, "
|
|
|
|
|
|
"Entfernen durch Abhaken (zugehörige PDFs werden gelöscht):"
|
|
|
|
|
|
)
|
|
|
|
|
|
self.ui.alle_xml.setVisible(False)
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
# 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()
|
|
|
|
|
|
|
2026-04-18 21:10:34 +02:00
|
|
|
|
# Duplikat-Warnung nur im Edit-Modus (um bestehende Aufrufer nicht bei jeder XML zu stören)
|
|
|
|
|
|
if self.edit_mode:
|
|
|
|
|
|
self._warn_on_duplicate_xsl_ids()
|
|
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
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)
|
|
|
|
|
|
|
2025-08-31 17:37:51 +02:00
|
|
|
|
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)
|
|
|
|
|
|
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger.debug(f"Checkboxen zu {len(self.xsl_checkboxes)} XSL-Knoten hinzugefügt")
|
2025-08-31 17:37:51 +02:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger.error(f"Fehler beim Hinzufügen der Checkboxen: {e}")
|
2025-08-31 17:37:51 +02:00
|
|
|
|
|
|
|
|
|
|
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()
|
2026-04-18 21:10:34 +02:00
|
|
|
|
|
|
|
|
|
|
# Vorauswahl setzen, wenn ID in preselected_xsl_ids
|
|
|
|
|
|
if node.id in self.preselected_xsl_ids:
|
|
|
|
|
|
checkbox.setChecked(True)
|
|
|
|
|
|
self._initial_selected_ids.add(node.id)
|
|
|
|
|
|
|
2025-08-31 17:37:51 +02:00
|
|
|
|
# Setze das Widget in Spalte 2
|
|
|
|
|
|
self.ui.xslNodesTree.setItemWidget(item, 2, checkbox_widget)
|
2026-04-18 21:10:34 +02:00
|
|
|
|
|
|
|
|
|
|
# Speichere Checkbox- und Node-Referenzen (id() als Key, da XSL-IDs theoretisch doppelt sein können)
|
2025-08-31 17:37:51 +02:00
|
|
|
|
self.xsl_checkboxes[id(node)] = checkbox
|
2026-04-18 21:10:34 +02:00
|
|
|
|
self.xsl_nodes[id(node)] = node
|
|
|
|
|
|
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger.debug(f"Checkbox für XSL-Knoten '{node.bez}' hinzugefügt")
|
2025-08-31 17:37:51 +02:00
|
|
|
|
|
|
|
|
|
|
# Rekursiv für Kinder
|
|
|
|
|
|
if item.childCount() > 0:
|
|
|
|
|
|
self._add_checkboxes_recursive(item)
|
|
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
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):
|
2025-09-19 20:29:56 +02:00
|
|
|
|
"""Lädt die Projekt-Knoten in das TreeWidget (ohne XML-Knoten).
|
|
|
|
|
|
Sortiert die Items alphabetisch nach ihrer ID."""
|
2025-08-31 17:04:22 +02:00
|
|
|
|
if not self.project_nodes:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# TreeWidget leeren
|
|
|
|
|
|
self.ui.xslNodesTree.clear()
|
|
|
|
|
|
self.xsl_checkboxes.clear()
|
2026-04-18 21:10:34 +02:00
|
|
|
|
self.xsl_nodes.clear()
|
|
|
|
|
|
self._initial_selected_ids.clear()
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
2025-09-19 20:29:56 +02:00
|
|
|
|
# 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:
|
2025-08-31 17:04:22 +02:00
|
|
|
|
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()
|
2025-08-31 17:37:51 +02:00
|
|
|
|
|
|
|
|
|
|
# Checkboxen nach dem Hinzufügen der Items erstellen
|
|
|
|
|
|
self._add_checkboxes_to_tree()
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
2025-09-19 20:29:56 +02:00
|
|
|
|
# Lade Knoten rekursiv (sortiert nach ID, nur TreeNode und XslFile, keine XML)
|
2025-08-31 17:04:22 +02:00
|
|
|
|
if node.children:
|
2025-09-19 20:29:56 +02:00
|
|
|
|
# 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)
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
elif isinstance(node, XslFile):
|
2025-08-31 17:37:51 +02:00
|
|
|
|
# XslFile: Zeige XSL-Datei-Pfad
|
2025-08-31 17:04:22 +02:00
|
|
|
|
item.setText(1, str(node.xsl_file))
|
|
|
|
|
|
|
2025-08-31 17:37:51 +02:00
|
|
|
|
# Checkbox wird später in _add_checkboxes_to_tree() hinzugefügt
|
2025-08-31 17:04:22 +02:00
|
|
|
|
# Keine Knoten für XslFile hinzufügen (XML-Dateien werden ausgeschlossen)
|
|
|
|
|
|
|
|
|
|
|
|
return item
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger.error(f"Fehler beim Erstellen des Tree-Items: {e}")
|
2025-08-31 17:04:22 +02:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def select_all(self):
|
|
|
|
|
|
"""Wählt alle XSL-Knoten aus."""
|
2025-08-31 17:37:51 +02:00
|
|
|
|
for checkbox in self.xsl_checkboxes.values():
|
|
|
|
|
|
checkbox.setChecked(True)
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
def deselect_all(self):
|
|
|
|
|
|
"""Wählt alle XSL-Knoten ab."""
|
2025-08-31 17:37:51 +02:00
|
|
|
|
for checkbox in self.xsl_checkboxes.values():
|
|
|
|
|
|
checkbox.setChecked(False)
|
2025-08-31 17:04:22 +02:00
|
|
|
|
|
|
|
|
|
|
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:
|
2025-08-31 17:37:51 +02:00
|
|
|
|
# Durchlaufe alle XSL-Checkboxes
|
|
|
|
|
|
for node_id, checkbox in self.xsl_checkboxes.items():
|
|
|
|
|
|
if checkbox.isChecked():
|
2025-08-31 17:04:22 +02:00
|
|
|
|
# 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:
|
2025-12-20 19:39:23 +01:00
|
|
|
|
logger.error(f"Fehler beim Sammeln der ausgewählten XSL-Knoten: {e}")
|
2025-08-31 17:04:22 +02:00
|
|
|
|
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.
|
2025-12-27 20:01:46 +01:00
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
Returns:
|
|
|
|
|
|
Path: Pfad zur XML-Datei
|
|
|
|
|
|
"""
|
|
|
|
|
|
return self.xml_file_path
|
|
|
|
|
|
|
2025-12-27 20:01:46 +01:00
|
|
|
|
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()
|
|
|
|
|
|
|
2026-04-18 21:10:34 +02:00
|
|
|
|
def get_selection_diff(self) -> tuple[list, list]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Gibt die Änderungen der Auswahl gegenüber der Vorauswahl zurück.
|
|
|
|
|
|
Nützlich im Edit-Modus, um nur die tatsächlich geänderten Zuordnungen zu verarbeiten.
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
tuple[list[XslFile], list[XslFile]]: (added_nodes, removed_nodes)
|
|
|
|
|
|
- added_nodes: XslFile-Objekte, die neu angehakt wurden
|
|
|
|
|
|
- removed_nodes: XslFile-Objekte, deren Haken entfernt wurde
|
|
|
|
|
|
"""
|
|
|
|
|
|
added_nodes = []
|
|
|
|
|
|
removed_nodes = []
|
|
|
|
|
|
|
|
|
|
|
|
for py_id, checkbox in self.xsl_checkboxes.items():
|
|
|
|
|
|
node = self.xsl_nodes.get(py_id)
|
|
|
|
|
|
if node is None:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
was_selected = node.id in self._initial_selected_ids
|
|
|
|
|
|
is_selected = checkbox.isChecked()
|
|
|
|
|
|
|
|
|
|
|
|
if is_selected and not was_selected:
|
|
|
|
|
|
added_nodes.append(node)
|
|
|
|
|
|
elif not is_selected and was_selected:
|
|
|
|
|
|
removed_nodes.append(node)
|
|
|
|
|
|
|
|
|
|
|
|
return added_nodes, removed_nodes
|
|
|
|
|
|
|
|
|
|
|
|
def _warn_on_duplicate_xsl_ids(self):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Zeigt eine Warnung, wenn im Projekt mehrere XslFile-Instanzen mit identischer ID existieren.
|
|
|
|
|
|
Die ID soll eindeutig sein - Duplikate weisen auf einen Datenfehler hin.
|
|
|
|
|
|
"""
|
|
|
|
|
|
id_to_nodes: dict = {}
|
|
|
|
|
|
for py_id, node in self.xsl_nodes.items():
|
|
|
|
|
|
id_to_nodes.setdefault(node.id, []).append(node)
|
|
|
|
|
|
|
|
|
|
|
|
duplicates = {xsl_id: nodes for xsl_id, nodes in id_to_nodes.items() if len(nodes) > 1}
|
|
|
|
|
|
if not duplicates:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
dup_lines = [f" - ID {xsl_id}: {len(nodes)}× ({', '.join(n.bez for n in nodes)})" for xsl_id, nodes in duplicates.items()]
|
|
|
|
|
|
logger.warning(f"Doppelte XSL-IDs im Projekt gefunden:\n{chr(10).join(dup_lines)}")
|
|
|
|
|
|
QMessageBox.warning(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"Doppelte XSL-IDs",
|
|
|
|
|
|
"Im Projekt existieren XSL-Knoten mit identischer ID. "
|
|
|
|
|
|
"Die IDs sollten eindeutig sein:\n\n" + "\n".join(dup_lines),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
def accept(self):
|
|
|
|
|
|
"""Überschreibt accept() um Validierung durchzuführen."""
|
2026-04-18 21:10:34 +02:00
|
|
|
|
# Im Edit-Modus: 0 Auswahlen erlauben (bedeutet: XML überall entfernen)
|
|
|
|
|
|
if self.edit_mode:
|
|
|
|
|
|
super().accept()
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
selected_nodes = self.get_selected_xsl_nodes()
|
|
|
|
|
|
if not selected_nodes:
|
2026-04-18 21:10:34 +02:00
|
|
|
|
QMessageBox.warning(self, "Warnung", "Bitte wählen Sie mindestens einen XSL-Knoten aus.")
|
2025-08-31 17:04:22 +02:00
|
|
|
|
return
|
2026-04-18 21:10:34 +02:00
|
|
|
|
|
2025-08-31 17:04:22 +02:00
|
|
|
|
super().accept()
|