Files
xsl-validator/src/ui/PdfProject.py
T
info a0626a78a3 Feat: Projektweite XSLT-Parameter mit Vererbungshierarchie (v1.4.0)
Ermöglicht die Definition von XSLT-Parametern auf Projektebene, die als
Basis für alle Transformationen dienen und von TreeNode- bzw. XslFile-
Parametern überschrieben werden können (Projekt < TreeNode < XslFile).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 10:58:01 +02:00

306 lines
12 KiB
Python

import os
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox
from conf import app_settings
from ui.PdfProject_ui import Ui_projectDlg
from ui.ProjectXsltParamsDialog import ProjectXsltParamsDialog
class PdfProjectDlg(QDialog):
def __init__(self, parent=None, project_data=None, edit_mode=False):
"""
Konstruktor für den PDF-Projekt-Dialog.
Args:
parent: Übergeordnetes Widget
project_data: Bestehende Projektdaten zum Bearbeiten (optional)
edit_mode: Wenn True, wird der Projekt-Ordner deaktiviert (nur Name und Einstellungen ändern)
"""
super().__init__(parent)
# UI einrichten
self.ui = Ui_projectDlg()
self.ui.setupUi(self)
# Projektdaten speichern
self.project_data = project_data or {}
self.edit_mode = edit_mode
self.xslt_params: dict[str, str] = dict(self.project_data.get("xslt_params", {}))
# Dialog-Eigenschaften setzen
self.setModal(True)
self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
# Signale verbinden
self._connect_signals()
# ComboBoxen initialisieren
self._init_combo_boxes()
# Edit-Modus konfigurieren
if self.edit_mode:
self._configure_edit_mode()
# Bestehende Projektdaten laden, falls vorhanden
if self.project_data:
self._load_project_data()
def _connect_signals(self):
"""Verbindet die Signale mit den entsprechenden Slots."""
# Browse-Button für Projekt-Ordner
self.ui.pushButton.clicked.connect(self.browse_project_dir)
# Browse-Button für FOP-Config-Ordner
self.ui.btnBrowseFopConfig.clicked.connect(self.browse_fop_config_dir)
# XSLT-Parameter bearbeiten
self.ui.btnEditXsltParams.clicked.connect(self._edit_xslt_params)
# OK/Cancel Buttons sind bereits in der UI-Datei verbunden
# self.ui.buttonBox.accepted.connect(self.accept)
# self.ui.buttonBox.rejected.connect(self.reject)
# Überschreibe accept() für Validierung
self.ui.buttonBox.accepted.disconnect()
self.ui.buttonBox.accepted.connect(self.validate_and_accept)
def _init_combo_boxes(self):
"""Initialisiert die ComboBoxen mit Werten aus app_settings."""
# XSL-Ordner ComboBox
self.ui.cB_XslDir.clear()
for xsl_dir in app_settings.xsl_dirs:
self.ui.cB_XslDir.addItem(xsl_dir.name, xsl_dir.id)
if not app_settings.xsl_dirs:
self.ui.cB_XslDir.addItem("Keine XSL-Ordner konfiguriert", -1)
# Java VM ComboBox
self.ui.cB_JavaVm.clear()
for java_vm in app_settings.java_vms:
self.ui.cB_JavaVm.addItem(java_vm.version, java_vm.id)
if not app_settings.java_vms:
self.ui.cB_JavaVm.addItem("Keine Java VMs konfiguriert", -1)
# Saxon Jar ComboBox
self.ui.cB_SaxonJar.clear()
for saxon_jar in app_settings.saxon_jars:
self.ui.cB_SaxonJar.addItem(saxon_jar.version, saxon_jar.id)
if not app_settings.saxon_jars:
self.ui.cB_SaxonJar.addItem("Keine Saxon JARs konfiguriert", -1)
# Apache FOP ComboBox
self.ui.cB_ApacheFop.clear()
for apache_fop in app_settings.apache_fops:
self.ui.cB_ApacheFop.addItem(apache_fop.version, apache_fop.id)
if not app_settings.apache_fops:
self.ui.cB_ApacheFop.addItem("Keine Apache FOP-Instanzen konfiguriert", -1)
# diff-pdf ComboBox
self.ui.cB_Diff_Pdf.clear()
for postgres_db in app_settings.diff_pdfs:
self.ui.cB_Diff_Pdf.addItem(postgres_db.version, postgres_db.id)
if not app_settings.diff_pdfs:
self.ui.cB_Diff_Pdf.addItem("Keine diff-pdf-Instanzen konfiguriert", -1)
# Postgres ComboBox
self.ui.cB_Postgres.clear()
for postgres_db in app_settings.postgresql_dbs:
self.ui.cB_Postgres.addItem(postgres_db.name, postgres_db.id)
if not app_settings.postgresql_dbs:
self.ui.cB_Postgres.addItem("Keine Postgres-Datenbanken konfiguriert", -1)
def _load_project_data(self):
"""Lädt bestehende Projektdaten in die Eingabefelder."""
# Projekt-Name
if 'name' in self.project_data:
self.ui.lineProjectName.setText(self.project_data['name'])
# Projekt-Ordner
if 'project_dir' in self.project_data:
self.ui.lineProjectDir.setText(str(self.project_data['project_dir']))
elif 'directory' in self.project_data:
self.ui.lineProjectDir.setText(self.project_data['directory'])
# ComboBox-Werte basierend auf IDs
if 'xsl_dir_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_XslDir, self.project_data['xsl_dir_id'])
if 'java_vm_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_JavaVm, self.project_data['java_vm_id'])
if 'saxon_jar_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_SaxonJar, self.project_data['saxon_jar_id'])
if 'apache_fop_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_ApacheFop, self.project_data['apache_fop_id'])
if 'diff_pdf_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_Diff_Pdf, self.project_data['diff_pdf_id'])
if 'postgre_sql_db_id' in self.project_data:
self._select_combo_by_data(self.ui.cB_Postgres, self.project_data['postgre_sql_db_id'])
# FOP-Config-Ordner
if 'fop_config_dir' in self.project_data and self.project_data['fop_config_dir']:
self.ui.lineFopConfigDir.setText(str(self.project_data['fop_config_dir']))
def _select_combo_by_data(self, combo_box, data_value):
"""
Wählt einen ComboBox-Eintrag basierend auf dem data-Wert aus.
Args:
combo_box: Die ComboBox
data_value: Der zu suchende data-Wert
"""
for i in range(combo_box.count()):
if combo_box.itemData(i) == data_value:
combo_box.setCurrentIndex(i)
break
def browse_project_dir(self):
"""Öffnet einen Dialog zum Auswählen des Projekt-Ordners."""
current_dir = self.ui.lineProjectDir.text()
if not current_dir or not os.path.exists(current_dir):
current_dir = os.path.expanduser("~")
selected_dir = QFileDialog.getExistingDirectory(
self,
"Projekt-Ordner auswählen",
current_dir,
QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks
)
if selected_dir:
self.ui.lineProjectDir.setText(selected_dir)
# Automatisch Projekt-Name generieren, wenn leer
if not self.ui.lineProjectName.text():
project_name = os.path.basename(selected_dir)
self.ui.lineProjectName.setText(project_name)
def browse_fop_config_dir(self):
"""Öffnet einen Dialog zum Auswählen des FOP-Config-Ordners."""
current_dir = self.ui.lineFopConfigDir.text()
if not current_dir or not os.path.exists(current_dir):
current_dir = os.path.expanduser("~")
selected_dir = QFileDialog.getExistingDirectory(
self,
"FOP-Config-Ordner auswählen",
current_dir,
QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks
)
if selected_dir:
self.ui.lineFopConfigDir.setText(selected_dir)
def _edit_xslt_params(self):
"""Öffnet den Dialog zur Bearbeitung der projektweiten XSLT-Parameter."""
dialog = ProjectXsltParamsDialog(self, self.xslt_params)
if dialog.exec() == ProjectXsltParamsDialog.DialogCode.Accepted:
self.xslt_params = dialog.get_params()
def validate_and_accept(self):
"""Validiert die Eingaben und akzeptiert den Dialog."""
# Projekt-Name prüfen
project_name = self.ui.lineProjectName.text().strip()
if not project_name:
QMessageBox.warning(
self,
"Ungültige Eingabe",
"Bitte geben Sie einen Projekt-Namen ein."
)
self.ui.lineProjectName.setFocus()
return
# Projekt-Ordner prüfen
project_dir = self.ui.lineProjectDir.text().strip()
if not project_dir:
QMessageBox.warning(
self,
"Ungültige Eingabe",
"Bitte wählen Sie einen Projekt-Ordner aus."
)
self.ui.pushButton.setFocus()
return
# Prüfen, ob der Ordner existiert
if not os.path.exists(project_dir):
reply = QMessageBox.question(
self,
"Ordner nicht gefunden",
f"Der Ordner '{project_dir}' existiert nicht.\n"
"Möchten Sie ihn erstellen?",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No
)
if reply == QMessageBox.StandardButton.Yes:
try:
os.makedirs(project_dir, exist_ok=True)
except OSError as e:
QMessageBox.critical(
self,
"Fehler",
f"Der Ordner konnte nicht erstellt werden:\n{str(e)}"
)
return
else:
return
# Prüfen, ob der Ordner beschreibbar ist
if not os.access(project_dir, os.W_OK):
QMessageBox.warning(
self,
"Ungültiger Ordner",
f"Der Ordner '{project_dir}' ist nicht beschreibbar.\n"
"Bitte wählen Sie einen anderen Ordner aus."
)
return
# Alle Validierungen bestanden
self.accept()
def get_project_data(self):
"""
Gibt die eingegebenen Projektdaten zurück.
Returns:
dict: Dictionary mit allen Projektdaten
"""
fop_config_dir = self.ui.lineFopConfigDir.text().strip()
return {
'name': self.ui.lineProjectName.text().strip(),
'project_dir': self.ui.lineProjectDir.text().strip(),
'xsl_dir_id': self.ui.cB_XslDir.currentData(),
'java_vm_id': self.ui.cB_JavaVm.currentData(),
'saxon_jar_id': self.ui.cB_SaxonJar.currentData(),
'apache_fop_id': self.ui.cB_ApacheFop.currentData(),
'diff_pdf_id': self.ui.cB_Diff_Pdf.currentData(),
'postgre_sql_db_id': self.ui.cB_Postgres.currentData(),
'fop_config_dir': fop_config_dir if fop_config_dir else None,
'xslt_params': self.xslt_params,
}
def _configure_edit_mode(self):
"""Konfiguriert den Dialog für den Edit-Modus (nur Einstellungen ändern)."""
# Projekt-Ordner und Browse-Button deaktivieren
self.ui.lineProjectDir.setEnabled(False)
self.ui.pushButton.setEnabled(False)
# Dialog-Titel ändern
self.setWindowTitle("Projekt-Einstellungen bearbeiten")
def set_project_data(self, project_data):
"""
Setzt die Projektdaten in den Dialog.
Args:
project_data: Dictionary mit Projektdaten
"""
self.project_data = project_data
self._load_project_data()