From 8b576b3fa7c00f3c9ea03041f15a6fc3eb652568 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Sun, 10 Aug 2025 17:32:22 +0200 Subject: [PATCH] =?UTF-8?q?Hinzuf=C3=BCgen=20der=20XML-Dateien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Klassen zur besseren Unterscheidung umbenant --- src/conf.py | 6 +- src/ui/AppSettings.py | 4 +- src/ui/MainWindow.py | 157 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 135 insertions(+), 32 deletions(-) diff --git a/src/conf.py b/src/conf.py index f6a03af..6831a1a 100644 --- a/src/conf.py +++ b/src/conf.py @@ -82,7 +82,7 @@ class PostgreSqlDb(BaseModel): ssl_mode: SSLMode = SSLMode.PREFER -class PdfProject(BaseModel): +class Project(BaseModel): id: int = Field(..., description="Eindeutige Projekt-ID", gt=0) name: str = Field(..., description="Projekt-Name", min_length=1, max_length=255) project_dir: Path = Field(..., description="Pfad zum Projekt-Verzeichnis") @@ -136,7 +136,7 @@ class AppSettings(BaseSettings): saxon_jars: list[SaxonJar] = [] apache_fops: list[ApacheFop] = [] xsl_dirs: list[XslDir] = [] - pdf_projects: list[PdfProject] = [] + pdf_projects: list[Project] = [] postgresql_dbs: list[PostgreSqlDb] = [] theme: str | None = None @@ -186,7 +186,7 @@ class TreeNode(BaseModel): children: list["TreeNode|XslFile"] -class PdfProjectSettings(BaseModel): +class ProjectData(BaseModel): """ Speichert die Projekteinstellungen direkt im Projektordner in einer .yaml-Datei. """ diff --git a/src/ui/AppSettings.py b/src/ui/AppSettings.py index 7169912..7310a6a 100644 --- a/src/ui/AppSettings.py +++ b/src/ui/AppSettings.py @@ -10,7 +10,7 @@ from ui.ApacheFopConfigDialog import ApacheFopConfigDialog from ui.XslDirConfigDialog import XslDirConfigDialog from ui.PostgreSqlConfigDialog import PostgreSqlConfigDialog from ui.PdfProject import PdfProjectDlg -from conf import AppSettings, JavaVm, DiffPdf, SaxonJar, ApacheFop, XslDir, PdfProject, PostgreSqlDb +from conf import AppSettings, JavaVm, DiffPdf, SaxonJar, ApacheFop, XslDir, Project, PostgreSqlDb class AppSettingsDlg(QDialog): @@ -456,7 +456,7 @@ class AppSettingsDlg(QDialog): new_id = max([p.id for p in self.temp_pdf_projects], default=0) + 1 # Erstelle PdfProject-Objekt - new_project = PdfProject( + new_project = Project( id=new_id, name=project_data['name'], project_dir=Path(project_data['project_dir']), diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index 76e7fef..d75d4a9 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -2,16 +2,17 @@ import glob import os import time import polars as pl +import shutil from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QCursor, QPixmap, QPainter, QAction, QIcon -from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory, QMenu, QTreeWidgetItem, QMessageBox +from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory, QMenu, QTreeWidgetItem, QMessageBox, QFileDialog from PySide6.QtPdf import QPdfDocument from ui.MainWinddow_ui import Ui_MainWindow from ui.AppSettings import AppSettingsDlg from ui.PdfProject import PdfProjectDlg -from conf import app_settings, PdfProject, PdfProjectSettings, TreeNode, XslFile, XmlFile +from conf import app_settings, Project, ProjectData, TreeNode, XslFile, XmlFile from pathlib import Path @@ -54,7 +55,13 @@ class MainWindow(QMainWindow): self.last_drag_position = None self.drag_threshold = 3 # Mindestbewegung in Pixeln vor dem Scrollen self.scroll_sensitivity = 0.7 # Reduzierte Empfindlichkeit für sanfteres Scrollen - + + # Das aktuelle Projekt (Project) aus app_settings + self.project = None + + # Das aktuelle ProjectData + self.pdf_project = None + # Theme-Menü initialisieren self._setup_theme_menu() @@ -133,7 +140,7 @@ class MainWindow(QMainWindow): print(f"Projekte-Menü initialisiert mit {len(app_settings.pdf_projects)} Projekten") - def open_existing_project(self, project: PdfProject): + def open_existing_project(self, project: Project): """ Öffnet ein vorhandenes Projekt. @@ -151,12 +158,12 @@ class MainWindow(QMainWindow): if project_yaml_path.exists() and project_yaml_path.stat().st_size > 0: # Versuche die Projekt-Einstellungen zu laden - self.pdf_project = PdfProjectSettings.readSettings(project_dir=project.project_dir) + self.pdf_project = ProjectData.readSettings(project_dir=project.project_dir) print(f"Projekt-Einstellungen aus {project_yaml_path} geladen!") else: # Erstelle Standard-Projekt-Einstellungen wenn Datei leer oder nicht vorhanden print("project.yaml ist leer oder nicht vorhanden, erstelle Standard-Einstellungen") - self.pdf_project = PdfProjectSettings() + self.pdf_project = ProjectData() # Speichere die Standard-Einstellungen in die project.yaml self.pdf_project.writeSettings(project_dir=project.project_dir) @@ -169,7 +176,7 @@ class MainWindow(QMainWindow): print(f"Fehler beim Laden des Projekts '{project.name}': {e}") # Fallback: Erstelle Standard-Einstellungen try: - self.pdf_project = PdfProjectSettings() + self.pdf_project = ProjectData() print("Fallback: Standard-Projekt-Einstellungen erstellt") # Auch bei Fallback die Nodes laden self._load_nodes_to_tree() @@ -789,7 +796,7 @@ class MainWindow(QMainWindow): new_id = max([p.id for p in app_settings.pdf_projects], default=0) + 1 # Erstelle PdfProject-Objekt - new_project = PdfProject( + new_project = Project( id=new_id, name=project_data['name'], project_dir=Path(project_data['project_dir']), @@ -820,7 +827,7 @@ class MainWindow(QMainWindow): except Exception as e: print(f"Fehler beim Erstellen des neuen Projekts: {e}") - def _create_project_structure(self, project: PdfProject): + def _create_project_structure(self, project: Project): """ Erstellt die Ordnerstruktur und project.yaml-Datei für ein neues Projekt. @@ -842,7 +849,7 @@ class MainWindow(QMainWindow): # Erstelle Standard-Projekt-Einstellungen und speichere sie if not project_yaml_path.exists(): # Erstelle Standard-PdfProjectSettings - default_settings = PdfProjectSettings() + default_settings = ProjectData() # Speichere die Standard-Einstellungen in die project.yaml default_settings.writeSettings(project_dir=project_dir) @@ -1035,9 +1042,120 @@ class MainWindow(QMainWindow): # Kontextmenü-Aktionen für XslFile def _add_xml_file_to_xsl(self, parent_item): - """Fügt eine XML-Datei zu einer XSL-Datei hinzu.""" + """ + Fügt eine XML-Datei zu einer XSL-Datei hinzu. + + Args: + parent_item: Das TreeWidgetItem des XslFile-Nodes + """ print(f"XML-Datei zu XslFile hinzufügen: {parent_item.text(0)}") - # TODO: Dialog zum Auswählen der XML-Datei öffnen + + try: + # Prüfe ob ein Projekt geladen ist + if not hasattr(self, 'project') or not self.project: + QMessageBox.warning(self, "Warnung", "Kein Projekt geladen. Bitte öffnen Sie zuerst ein Projekt.") + return + + if not hasattr(self, 'pdf_project') or not self.pdf_project: + QMessageBox.warning(self, "Warnung", "Keine Projekt-Einstellungen geladen.") + return + + # Hole die XslFile-Node-ID aus dem TreeWidgetItem + xsl_node_id = parent_item.data(0, Qt.ItemDataRole.UserRole) + if not xsl_node_id: + QMessageBox.warning(self, "Warnung", "Keine Node-ID gefunden für das ausgewählte Element.") + return + + # Finde den XslFile-Node in den Projekt-Daten + xsl_node = self._find_node_by_id(self.pdf_project.nodes, xsl_node_id) + if not xsl_node or not isinstance(xsl_node, XslFile): + QMessageBox.warning(self, "Warnung", "XSL-Datei-Node nicht gefunden oder falscher Typ.") + return + + # Öffne Datei-Dialog zum Auswählen der XML-Datei + xml_file_path, _ = QFileDialog.getOpenFileName( + self, + "XML-Datei auswählen", + "", + "XML-Dateien (*.xml);;Alle Dateien (*)" + ) + + if not xml_file_path: + # Benutzer hat abgebrochen + return + + xml_file_path = Path(xml_file_path) + + # Prüfe ob die Datei existiert + if not xml_file_path.exists(): + QMessageBox.critical(self, "Fehler", f"Die ausgewählte XML-Datei existiert nicht:\n{xml_file_path}") + return + + # Erstelle xml-Ordner im Projekt-Verzeichnis falls er nicht existiert + xml_dir = Path(self.project.project_dir) / "xml" + xml_dir.mkdir(parents=True, exist_ok=True) + + # Bestimme den Ziel-Pfad in xml-Ordner + target_xml_path = xml_dir / xml_file_path.name + + # Prüfe ob eine Datei mit gleichem Namen bereits existiert + if target_xml_path.exists(): + reply = QMessageBox.question( + self, + "Datei existiert bereits", + f"Eine XML-Datei mit dem Namen '{xml_file_path.name}' existiert bereits im xml-Ordner.\n\n" + "Möchten Sie sie überschreiben?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No + ) + + if reply != QMessageBox.StandardButton.Yes: + return + + # Kopiere die XML-Datei in den xml-Ordner + shutil.copy2(xml_file_path, target_xml_path) + print(f"XML-Datei kopiert: {xml_file_path} -> {target_xml_path}") + + # Erstelle relatives Path zur XML-Datei (relativ zum xml-Ordner) + relative_xml_path = Path("xml") / xml_file_path.name + + # Prüfe ob diese XML-Datei bereits in der XslFile-Node vorhanden ist + existing_xml = None + for xml_file in xsl_node.xmls: + if xml_file.xml == relative_xml_path: + existing_xml = xml_file + break + + if existing_xml: + QMessageBox.information( + self, + "XML-Datei bereits vorhanden", + f"Die XML-Datei '{xml_file_path.name}' ist bereits in dieser XSL-Datei enthalten." + ) + return + + # Erstelle neues XmlFile-Objekt und füge es zur XslFile-Node hinzu + new_xml_file = XmlFile(xml=relative_xml_path) + xsl_node.xmls.append(new_xml_file) + + print(f"XML-Datei '{xml_file_path.name}' zu XslFile-Node '{xsl_node.bez}' hinzugefügt") + + # Speichere die aktualisierten Projekt-Einstellungen + self._save_project_settings() + + # Aktualisiere das TreeWidget + self._load_nodes_to_tree() + + QMessageBox.information( + self, + "Erfolg", + f"XML-Datei '{xml_file_path.name}' wurde erfolgreich hinzugefügt und in den xml-Ordner kopiert." + ) + + except Exception as e: + error_msg = f"Fehler beim Hinzufügen der XML-Datei: {str(e)}" + print(error_msg) + QMessageBox.critical(self, "Fehler", error_msg) def _edit_xsl_file(self, item): """Bearbeitet eine XSL-Datei.""" @@ -1113,21 +1231,6 @@ class MainWindow(QMainWindow): print(f"Fehler beim Laden aus FN2: {e}") QMessageBox.critical(self, "Fehler", f"Fehler beim Laden aus FN2:\n{str(e)}") - # def _get_current_project(self): - # """ - # Ermittelt das aktuell geladene Projekt aus app_settings. - - # Returns: - # PdfProject|None: Das aktuelle Projekt oder None - # """ - # # Da wir kein direktes Attribut für das aktuelle Projekt haben, - # # nehmen wir das erste Projekt als Fallback oder implementieren eine bessere Logik - # if app_settings.pdf_projects: - # # TODO: Hier sollte eine bessere Logik implementiert werden, - # # um das tatsächlich aktuelle Projekt zu ermitteln - # return app_settings.pdf_projects[0] - # return None - def _get_database_config(self, db_id): """ Holt die Datenbank-Konfiguration anhand der ID.