from PySide6.QtWidgets import QDialog, QTableWidgetItem, QHeaderView, QAbstractItemView from PySide6.QtCore import Qt from pathlib import Path from ui.AppSettings_ui import Ui_Dialog from ui.JavaVmConfigDialog import JavaVmConfigDialog from ui.DiffPdfConfigDialog import DiffPdfConfigDialog from ui.SaxonJarConfigDialog import SaxonJarConfigDialog 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, Project, PostgreSqlDb, XsltVersion class AppSettingsDlg(QDialog): """Dialog für die Anwendungseinstellungen mit vollständiger Funktionalität.""" def __init__( self, parent=None, settings: AppSettings = AppSettings(java_vms=[], diff_pdfs=[], saxon_jars=[], apache_fops=[], xsl_dirs=[]), ): super().__init__(parent) self.ui = Ui_Dialog() self.ui.setupUi(self) self.settings = settings self.temp_java_vms = self.settings.java_vms.copy() self.temp_diff_pdfs = self.settings.diff_pdfs.copy() self.temp_saxon_jars = self.settings.saxon_jars.copy() self.temp_apache_fops = self.settings.apache_fops.copy() self.temp_xsl_dirs = self.settings.xsl_dirs.copy() self.temp_pdf_projects = self.settings.pdf_projects.copy() self.temp_postgresql_dbs = self.settings.postgresql_dbs.copy() self._connect_signals() self._setup_tables() self._populate_tables() self._populate_performance_tab() # --- Generische Helfer --- def _update_remove_button(self, table, button): """Aktiviert/deaktiviert einen Entfernen-Button je nach Tabellenauswahl.""" button.setEnabled(table.currentRow() >= 0) def _add_item(self, dialog_class, temp_list, settings_attr, factory_fn, populate_fn): """Öffnet einen Dialog, erstellt ein neues Objekt und fügt es der Liste hinzu.""" dialog = dialog_class(self) if dialog.exec() == QDialog.DialogCode.Accepted: data = dialog.get_data() if data: new_id = max((x.id for x in temp_list), default=0) + 1 temp_list.append(factory_fn(new_id, data)) populate_fn() setattr(self.settings, settings_attr, temp_list.copy()) self.settings.save() self._refresh_main_window_projects_menu() def _remove_item(self, table, temp_list, settings_attr, update_fn, populate_fn): """Entfernt das ausgewählte Element aus der Liste und speichert die Einstellungen.""" row = table.currentRow() if row >= 0: del temp_list[row] populate_fn() update_fn() setattr(self.settings, settings_attr, temp_list.copy()) self.settings.save() self._refresh_main_window_projects_menu() def _edit_item(self, index, temp_list, dialog_class, fields, populate_fn): """Öffnet einen Bearbeitungs-Dialog für das gewählte Element.""" row = index.row() if 0 <= row < len(temp_list): item = temp_list[row] dialog = dialog_class(self) dialog.set_data({f: getattr(item, f) for f in fields}) if dialog.exec() == QDialog.DialogCode.Accepted: new_data = dialog.get_data() if new_data: for f in fields: setattr(item, f, new_data[f]) populate_fn() # --- Signale und Tabellen-Setup --- def _connect_signals(self): """Verbindet die Signale der UI-Elemente.""" self.ui.addXsl.clicked.connect(self._add_xsl_dir) self.ui.removeXsl.clicked.connect(self._remove_xsl_dir) self.ui.tableXsls.itemSelectionChanged.connect(self._update_xsl_buttons) self.ui.addJavaVm.clicked.connect(self._add_java_vm) self.ui.removeJavaVm.clicked.connect(self._remove_java_vm) self.ui.tableJavaVms.itemSelectionChanged.connect(self._update_java_vm_buttons) self.ui.addSaxon.clicked.connect(self._add_saxon) self.ui.removeSaxon.clicked.connect(self._remove_saxon) self.ui.tableSaxons.itemSelectionChanged.connect(self._update_saxon_buttons) self.ui.addApacheFop.clicked.connect(self._add_apache_fop) self.ui.removeApacheFop.clicked.connect(self._remove_apache_fop) self.ui.tableApacheFops.itemSelectionChanged.connect(self._update_apache_fop_buttons) self.ui.addDiffPdf.clicked.connect(self._add_diff_pdf) self.ui.removeDiffPdf.clicked.connect(self._remove_diff_pdf) self.ui.tableDiffPdfs.itemSelectionChanged.connect(self._update_diff_pdf_buttons) self.ui.removeProject.clicked.connect(self._remove_pdf_project) self.ui.addProject.clicked.connect(self._add_pdf_project) self.ui.tablePdfProjects.itemSelectionChanged.connect(self._update_pdf_project_buttons) self.ui.addPostgreSql.clicked.connect(self._add_postgresql_db) self.ui.removePostgreSql.clicked.connect(self._remove_postgresql_db) self.ui.tablePostgreSqlDbs.itemSelectionChanged.connect(self._update_postgresql_db_buttons) def _setup_tables(self): """Richtet die Tabellen-Header ein und macht sie unveränderbar.""" self.ui.tableXsls.setHorizontalHeaderLabels(["Name", "Pfad"]) self.ui.tableXsls.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tableXsls.doubleClicked.connect(self._edit_xsl_dir) self.ui.tableJavaVms.setHorizontalHeaderLabels(["Version", "Pfad"]) self.ui.tableJavaVms.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tableJavaVms.doubleClicked.connect(self._edit_java_vm) self.ui.tableSaxons.setHorizontalHeaderLabels(["Version", "JAR-Pfad", "Erweiterung"]) self.ui.tableSaxons.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tableSaxons.doubleClicked.connect(self._edit_saxon) self.ui.tableApacheFops.setHorizontalHeaderLabels(["Version", "Pfad", "Erweiterung"]) self.ui.tableApacheFops.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tableApacheFops.doubleClicked.connect(self._edit_apache_fop) self.ui.tableDiffPdfs.setHorizontalHeaderLabels(["Version", "Pfad", "Parameter", "Erweiterung"]) self.ui.tableDiffPdfs.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tableDiffPdfs.doubleClicked.connect(self._edit_diff_pdf) self.ui.tablePdfProjects.setHorizontalHeaderLabels( ["Name", "Projekt-Ordner", "XSL-Ordner", "Java-VM", "Saxon", "Apache FOP", "Diff-PDF"] ) self.ui.tablePdfProjects.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tablePdfProjects.doubleClicked.connect(self._edit_pdf_project) self.ui.tablePostgreSqlDbs.setHorizontalHeaderLabels(["Name", "Host", "Port", "Datenbank", "Benutzer"]) self.ui.tablePostgreSqlDbs.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers) self.ui.tablePostgreSqlDbs.doubleClicked.connect(self._edit_postgresql_db) # --- Tabellen befüllen --- def _populate_tables(self): """Füllt alle Tabellen mit den aktuellen Einstellungen.""" self._populate_xsl_table() self._populate_java_vm_table() self._populate_saxon_table() self._populate_apache_fop_table() self._populate_diff_pdf_table() self._populate_pdf_project_table() self._populate_postgresql_db_table() def _make_centered_item(self, text: str) -> QTableWidgetItem: item = QTableWidgetItem(text) item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) return item def _populate_xsl_table(self): """Füllt die XSL-Ordner Tabelle.""" self.ui.tableXsls.setRowCount(len(self.temp_xsl_dirs)) for row, x in enumerate(self.temp_xsl_dirs): self.ui.tableXsls.setItem(row, 0, self._make_centered_item(x.name)) self.ui.tableXsls.setItem(row, 1, self._make_centered_item(str(x.path_to_root_dir))) self.ui.tableXsls.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_java_vm_table(self): """Füllt die Java VM Tabelle.""" self.ui.tableJavaVms.setRowCount(len(self.temp_java_vms)) for row, x in enumerate(self.temp_java_vms): self.ui.tableJavaVms.setItem(row, 0, self._make_centered_item(x.version)) self.ui.tableJavaVms.setItem(row, 1, self._make_centered_item(str(x.path_to_binary_file))) self.ui.tableJavaVms.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_saxon_table(self): """Füllt die Saxon Tabelle.""" self.ui.tableSaxons.setRowCount(len(self.temp_saxon_jars)) for row, x in enumerate(self.temp_saxon_jars): self.ui.tableSaxons.setItem(row, 0, self._make_centered_item(x.version)) self.ui.tableSaxons.setItem(row, 1, self._make_centered_item(str(x.path_to_jar_file))) self.ui.tableSaxons.setItem(row, 2, self._make_centered_item(x.output_file_extension)) self.ui.tableSaxons.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_apache_fop_table(self): """Füllt die Apache FOP Tabelle.""" self.ui.tableApacheFops.setRowCount(len(self.temp_apache_fops)) for row, x in enumerate(self.temp_apache_fops): self.ui.tableApacheFops.setItem(row, 0, self._make_centered_item(x.version)) self.ui.tableApacheFops.setItem(row, 1, self._make_centered_item(str(x.path_to_dir))) self.ui.tableApacheFops.setItem(row, 2, self._make_centered_item(x.output_file_extension)) self.ui.tableApacheFops.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_diff_pdf_table(self): """Füllt die Diff PDF Tabelle.""" self.ui.tableDiffPdfs.setRowCount(len(self.temp_diff_pdfs)) for row, x in enumerate(self.temp_diff_pdfs): self.ui.tableDiffPdfs.setItem(row, 0, self._make_centered_item(x.version)) self.ui.tableDiffPdfs.setItem(row, 1, self._make_centered_item(str(x.path_to_binary_file))) self.ui.tableDiffPdfs.setItem(row, 2, self._make_centered_item(", ".join(x.default_params))) self.ui.tableDiffPdfs.setItem(row, 3, self._make_centered_item(x.output_file_extension)) self.ui.tableDiffPdfs.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_pdf_project_table(self): """Füllt die PDF-Projekte Tabelle.""" self.ui.tablePdfProjects.setRowCount(len(self.temp_pdf_projects)) for row, p in enumerate(self.temp_pdf_projects): self.ui.tablePdfProjects.setItem(row, 0, self._make_centered_item(p.name)) self.ui.tablePdfProjects.setItem(row, 1, self._make_centered_item(str(p.project_dir))) self.ui.tablePdfProjects.setItem(row, 2, self._make_centered_item(p.getXsl())) self.ui.tablePdfProjects.setItem(row, 3, self._make_centered_item(p.getJavaVm())) self.ui.tablePdfProjects.setItem(row, 4, self._make_centered_item(p.getSaxon())) self.ui.tablePdfProjects.setItem(row, 5, self._make_centered_item(p.getApacheFop())) self.ui.tablePdfProjects.setItem(row, 6, self._make_centered_item(p.getDiffPdf())) self.ui.tablePdfProjects.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_postgresql_db_table(self): """Füllt die PostgreSQL-Datenbank Tabelle.""" self.ui.tablePostgreSqlDbs.setRowCount(len(self.temp_postgresql_dbs)) for row, x in enumerate(self.temp_postgresql_dbs): self.ui.tablePostgreSqlDbs.setItem(row, 0, self._make_centered_item(x.name)) self.ui.tablePostgreSqlDbs.setItem(row, 1, self._make_centered_item(x.host)) self.ui.tablePostgreSqlDbs.setItem(row, 2, self._make_centered_item(str(x.port))) self.ui.tablePostgreSqlDbs.setItem(row, 3, self._make_centered_item(x.database)) self.ui.tablePostgreSqlDbs.setItem(row, 4, self._make_centered_item(x.username)) self.ui.tablePostgreSqlDbs.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) def _populate_performance_tab(self): """Initialisiert den Performance-Tab mit den aktuellen Einstellungen.""" self.ui.spinBoxWorkerCount.setValue(self.settings.max_workers) self.ui.checkBoxUseSaxonPool.setChecked(self.settings.use_saxon_worker_pool) self.ui.comboBoxXsltVersion.setCurrentIndex( 0 if self.settings.saxon_xslt_version == XsltVersion.XSLT_1_0 else 1 ) self.ui.checkBoxUseFopPool.setChecked(self.settings.use_fop_worker_pool) def _refresh_main_window_projects_menu(self): """Aktualisiert das Projekte-Menü im MainWindow, falls vorhanden.""" if self.parent() and hasattr(self.parent(), "_setup_projects_menu"): self.parent()._setup_projects_menu() # --- XSL-Ordner --- def _add_xsl_dir(self): self._add_item( XslDirConfigDialog, self.temp_xsl_dirs, "xsl_dirs", lambda id, d: XslDir(id=id, name=d["name"], path_to_root_dir=d["path_to_root_dir"]), self._populate_xsl_table, ) def _remove_xsl_dir(self): self._remove_item( self.ui.tableXsls, self.temp_xsl_dirs, "xsl_dirs", self._update_xsl_buttons, self._populate_xsl_table, ) def _update_xsl_buttons(self): self._update_remove_button(self.ui.tableXsls, self.ui.removeXsl) def _edit_xsl_dir(self, index): self._edit_item(index, self.temp_xsl_dirs, XslDirConfigDialog, ["name", "path_to_root_dir"], self._populate_xsl_table) # --- Java VM --- def _add_java_vm(self): self._add_item( JavaVmConfigDialog, self.temp_java_vms, "java_vms", lambda id, d: JavaVm(id=id, version=d["version"], path_to_binary_file=d["path_to_binary_file"]), self._populate_java_vm_table, ) def _remove_java_vm(self): self._remove_item( self.ui.tableJavaVms, self.temp_java_vms, "java_vms", self._update_java_vm_buttons, self._populate_java_vm_table, ) def _update_java_vm_buttons(self): self._update_remove_button(self.ui.tableJavaVms, self.ui.removeJavaVm) def _edit_java_vm(self, index): self._edit_item(index, self.temp_java_vms, JavaVmConfigDialog, ["version", "path_to_binary_file"], self._populate_java_vm_table) # --- Saxon --- def _add_saxon(self): self._add_item( SaxonJarConfigDialog, self.temp_saxon_jars, "saxon_jars", lambda id, d: SaxonJar( id=id, version=d["version"], path_to_jar_file=d["path_to_jar_file"], output_file_extension=d["output_file_extension"], ), self._populate_saxon_table, ) def _remove_saxon(self): self._remove_item( self.ui.tableSaxons, self.temp_saxon_jars, "saxon_jars", self._update_saxon_buttons, self._populate_saxon_table, ) def _update_saxon_buttons(self): self._update_remove_button(self.ui.tableSaxons, self.ui.removeSaxon) def _edit_saxon(self, index): self._edit_item(index, self.temp_saxon_jars, SaxonJarConfigDialog, ["version", "path_to_jar_file", "output_file_extension"], self._populate_saxon_table) # --- Apache FOP --- def _add_apache_fop(self): self._add_item( ApacheFopConfigDialog, self.temp_apache_fops, "apache_fops", lambda id, d: ApacheFop( id=id, version=d["version"], path_to_dir=d["path_to_dir"], output_file_extension=d["output_file_extension"], ), self._populate_apache_fop_table, ) def _remove_apache_fop(self): self._remove_item( self.ui.tableApacheFops, self.temp_apache_fops, "apache_fops", self._update_apache_fop_buttons, self._populate_apache_fop_table, ) def _update_apache_fop_buttons(self): self._update_remove_button(self.ui.tableApacheFops, self.ui.removeApacheFop) def _edit_apache_fop(self, index): self._edit_item(index, self.temp_apache_fops, ApacheFopConfigDialog, ["version", "path_to_dir", "output_file_extension"], self._populate_apache_fop_table) # --- Diff PDF --- def _add_diff_pdf(self): self._add_item( DiffPdfConfigDialog, self.temp_diff_pdfs, "diff_pdfs", lambda id, d: DiffPdf( id=id, version=d["version"], path_to_binary_file=d["path_to_binary_file"], default_params=d["default_params"], output_file_extension=d["output_file_extension"], ), self._populate_diff_pdf_table, ) def _remove_diff_pdf(self): self._remove_item( self.ui.tableDiffPdfs, self.temp_diff_pdfs, "diff_pdfs", self._update_diff_pdf_buttons, self._populate_diff_pdf_table, ) def _update_diff_pdf_buttons(self): self._update_remove_button(self.ui.tableDiffPdfs, self.ui.removeDiffPdf) def _edit_diff_pdf(self, index): self._edit_item(index, self.temp_diff_pdfs, DiffPdfConfigDialog, ["version", "path_to_binary_file", "default_params", "output_file_extension"], self._populate_diff_pdf_table) # --- PDF-Projekte (Sonderbehandlung wegen komplexer Logik) --- def _add_pdf_project(self): """Fügt ein neues PDF-Projekt hinzu.""" dialog = PdfProjectDlg(self) if dialog.exec() == PdfProjectDlg.DialogCode.Accepted: project_data = dialog.get_project_data() new_id = max((p.id for p in self.temp_pdf_projects), default=0) + 1 new_project = Project( id=new_id, name=project_data["name"], project_dir=Path(project_data["project_dir"]), java_vm_id=project_data["java_vm_id"] if project_data["java_vm_id"] != -1 else 1, diff_pdf_id=project_data["diff_pdf_id"] if project_data["diff_pdf_id"] != -1 else 1, saxon_jar_id=project_data["saxon_jar_id"] if project_data["saxon_jar_id"] != -1 else 1, apache_fop_id=project_data["apache_fop_id"] if project_data["apache_fop_id"] != -1 else 1, xsl_dir_id=project_data["xsl_dir_id"] if project_data["xsl_dir_id"] != -1 else 1, postgre_sql_db_id=project_data["postgre_sql_db_id"] if project_data["postgre_sql_db_id"] != -1 else 1, fop_config_dir=Path(project_data["fop_config_dir"]) if project_data.get("fop_config_dir") else None, xslt_params=project_data.get("xslt_params", {}), ) self.temp_pdf_projects.append(new_project) self._populate_pdf_project_table() self.settings.pdf_projects = self.temp_pdf_projects.copy() self.settings.save() self._refresh_main_window_projects_menu() def _remove_pdf_project(self): self._remove_item( self.ui.tablePdfProjects, self.temp_pdf_projects, "pdf_projects", self._update_pdf_project_buttons, self._populate_pdf_project_table, ) def _update_pdf_project_buttons(self): self._update_remove_button(self.ui.tablePdfProjects, self.ui.removeProject) def _edit_pdf_project(self, index): """Bearbeitet ein PDF-Projekt per Doppelklick (nur Einstellungen).""" row = index.row() if 0 <= row < len(self.temp_pdf_projects): pdf_project = self.temp_pdf_projects[row] project_data = { "name": pdf_project.name, "project_dir": str(pdf_project.project_dir), "java_vm_id": pdf_project.java_vm_id, "diff_pdf_id": pdf_project.diff_pdf_id, "saxon_jar_id": pdf_project.saxon_jar_id, "apache_fop_id": pdf_project.apache_fop_id, "xsl_dir_id": pdf_project.xsl_dir_id, "postgre_sql_db_id": pdf_project.postgre_sql_db_id, "fop_config_dir": str(pdf_project.fop_config_dir) if pdf_project.fop_config_dir else None, "xslt_params": dict(pdf_project.xslt_params), } dialog = PdfProjectDlg(self, project_data, edit_mode=True) if dialog.exec() == PdfProjectDlg.DialogCode.Accepted: new_data = dialog.get_project_data() pdf_project.name = new_data["name"] pdf_project.java_vm_id = ( new_data["java_vm_id"] if new_data["java_vm_id"] != -1 else pdf_project.java_vm_id ) pdf_project.diff_pdf_id = ( new_data["diff_pdf_id"] if new_data["diff_pdf_id"] != -1 else pdf_project.diff_pdf_id ) pdf_project.saxon_jar_id = ( new_data["saxon_jar_id"] if new_data["saxon_jar_id"] != -1 else pdf_project.saxon_jar_id ) pdf_project.apache_fop_id = ( new_data["apache_fop_id"] if new_data["apache_fop_id"] != -1 else pdf_project.apache_fop_id ) pdf_project.xsl_dir_id = ( new_data["xsl_dir_id"] if new_data["xsl_dir_id"] != -1 else pdf_project.xsl_dir_id ) pdf_project.postgre_sql_db_id = ( new_data["postgre_sql_db_id"] if new_data["postgre_sql_db_id"] != -1 else pdf_project.postgre_sql_db_id ) pdf_project.fop_config_dir = ( Path(new_data["fop_config_dir"]) if new_data.get("fop_config_dir") else None ) pdf_project.xslt_params = new_data.get("xslt_params", {}) self._populate_pdf_project_table() self.settings.pdf_projects = self.temp_pdf_projects.copy() self.settings.save() self._refresh_main_window_projects_menu() # --- PostgreSQL --- def _add_postgresql_db(self): self._add_item( PostgreSqlConfigDialog, self.temp_postgresql_dbs, "postgresql_dbs", lambda id, d: PostgreSqlDb( id=id, name=d["name"], host=d["host"], port=d["port"], database=d["database"], username=d["username"], password=d["password"], ), self._populate_postgresql_db_table, ) def _remove_postgresql_db(self): self._remove_item( self.ui.tablePostgreSqlDbs, self.temp_postgresql_dbs, "postgresql_dbs", self._update_postgresql_db_buttons, self._populate_postgresql_db_table, ) def _update_postgresql_db_buttons(self): self._update_remove_button(self.ui.tablePostgreSqlDbs, self.ui.removePostgreSql) def _edit_postgresql_db(self, index): self._edit_item(index, self.temp_postgresql_dbs, PostgreSqlConfigDialog, ["name", "host", "port", "database", "username", "password"], self._populate_postgresql_db_table) # --- Dialog-Abschluss --- def accept(self): """Übernimmt die Änderungen und schließt den Dialog.""" self.settings.java_vms = self.temp_java_vms.copy() self.settings.diff_pdfs = self.temp_diff_pdfs.copy() self.settings.saxon_jars = self.temp_saxon_jars.copy() self.settings.apache_fops = self.temp_apache_fops.copy() self.settings.xsl_dirs = self.temp_xsl_dirs.copy() self.settings.pdf_projects = self.temp_pdf_projects.copy() self.settings.max_workers = self.ui.spinBoxWorkerCount.value() self.settings.use_saxon_worker_pool = self.ui.checkBoxUseSaxonPool.isChecked() self.settings.saxon_xslt_version = ( XsltVersion.XSLT_1_0 if self.ui.comboBoxXsltVersion.currentIndex() == 0 else XsltVersion.XSLT_2_0_3_0 ) self.settings.use_fop_worker_pool = self.ui.checkBoxUseFopPool.isChecked() self.settings.save() self._refresh_main_window_projects_menu() super().accept() def get_settings(self) -> AppSettings: """Gibt die aktuellen Einstellungen zurück.""" return self.settings