3 Commits

Author SHA1 Message Date
info 789fb5d77f docs: Projektnamen bereinigen, README und Web aktualisieren
Entfernt den Zusatz "ehemals xsl-validator" aus CLAUDE.md und README.
README mit korrekten Infos zu Theme, Worker-Pool und externen Tools ergänzt.
Download-Links auf Web-Seite auf Version 1.7.3 aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 19:37:17 +02:00
info d19c36191c Version 1.7.3: Fehlende Menü-Icons ergänzt
- MainWindow: Icons für drei bislang icon-lose Menü-Aktionen gesetzt
  - "Alle XML-Dateien transformieren" → play-circle
  - "Aus Datenbank laden" → database (neu)
  - "Worker-Pool-Metriken" → activity (neu)
- Zwei neue Feather-Icons (database, activity) ergänzt: Download-Skript,
  resources.qrc und resources_rc.py (via pyside6-rcc) aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:45:25 +02:00
info 3e20b186c7 Version 1.7.2: Icon-System – Theme-Reaktivität generalisiert, Render vereinfacht
- icons.py: IconRefreshMixin ergänzt – reagiert auf PaletteChange/StyleChange
  und färbt Icons nur bei tatsächlichem Textfarb-Wechsel neu ein (farb-gegated)
- icons.py: Render auf einen einzelnen 64px-Pixmap vereinfacht (Qt skaliert),
  statt drei fixe Größen (16/24/32)
- MainWindow: nutzt IconRefreshMixin; hasattr-Guards in _setup_icons entfernt
  (läuft jetzt nur im fertig initialisierten Zustand); change_theme entschlackt –
  Icon-/Baum-Aktualisierung erfolgt über changeEvent statt unbedingtem Aufruf
- XslDependencyDialog: nutzt IconRefreshMixin, färbt Button- und Baum-Icons bei
  Theme-Wechsel neu ein (bislang blieben offene non-modale Dialoge stale)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:36:58 +02:00
16 changed files with 808 additions and 671 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ Spreche mit mir auf Deutsch! (Communicate with me in German!)
## Projektübersicht
DocuMentor (ehemals xsl-validator) ist eine PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit XML-Dateien. Sie bietet eine GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung von PDF-Generierungsprojekten mit PostgreSQL-Datenbankintegration.
DocuMentor ist eine PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit XML-Dateien. Sie bietet eine GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung von PDF-Generierungsprojekten mit PostgreSQL-Datenbankintegration.
## Anvisiertes Nutzungsszenario
Der primäre Einsatz ist die kontinuierliche Weiterentwicklung von PDF-Dokumenten in Flexnow (Software zur Prüfungsverwaltung). Dabei handelt es sich beispielsweise um amtliche Urkunden, Zeugnisse und Bescheide.
+1 -1
View File
@@ -4,7 +4,7 @@
<!-- Paket-Definition (ersetzt Product in v4) -->
<Package
Name="DocuMentor"
Version="1.7.1"
Version="1.7.3"
Manufacturer="Vitali Graf / Software- und Datenbankentwicklung"
UpgradeCode="F498B66C-726D-44AA-95F4-CB4FBDCEF26E"
Language="1031"
+10 -7
View File
@@ -2,7 +2,7 @@
**Professionelle XSL-Transformations-Verwaltung und PDF-Generierung**
DocuMentor (ehemals xsl-validator) ist eine leistungsstarke PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit automatischer PDF-Generierung. Die Anwendung bietet eine intuitive GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung komplexer PDF-Generierungsprojekte mit PostgreSQL-Datenbankintegration.
DocuMentor ist eine leistungsstarke PySide6-basierte Desktop-Anwendung zur Verwaltung und Validierung von XSL-Transformationen mit automatischer PDF-Generierung. Die Anwendung bietet eine intuitive GUI zur Konfiguration von Transformations-Toolchains (Saxon, Apache FOP, diff-pdf) und zur Verwaltung komplexer PDF-Generierungsprojekte mit PostgreSQL-Datenbankintegration.
## Features
@@ -14,7 +14,7 @@ DocuMentor (ehemals xsl-validator) ist eine leistungsstarke PySide6-basierte Des
### ⚡ Asynchrone Batch-Verarbeitung
- Verarbeiten Sie große Mengen von XML-Dateien im Hintergrund
- Fortschrittsanzeige für lange Transformationen
- 4x schnellere XSLT-Transformationen durch Worker-Pool-Architektur
- Parallelisierte XSLT-Transformationen durch eine Worker-Pool-Architektur
### 🔍 Intelligente Duplikatserkennung
- Automatische Hash-basierte Erkennung von identischen XML-Dateien (Blake2b)
@@ -37,7 +37,7 @@ DocuMentor (ehemals xsl-validator) ist eine leistungsstarke PySide6-basierte Des
- Plattformübergreifende Unterstützung (Linux, Windows, macOS)
### 🎨 Modernes UI
- Dark-Theme-Unterstützung via `qdarktheme`
- Dark/Light-Theme-Unterstützung
- Drag-and-Drop für XML-Dateien
- Responsive und intuitive Benutzeroberfläche
@@ -58,13 +58,14 @@ uv sync
pip install -e .
```
### Externe Tools (optional)
### Externe Tools
Für die volle Funktionalität benötigen Sie:
- **Saxon-HE**: XSLT 3.0 Prozessor ([Download](https://www.saxonica.com/download/))
- **Apache FOP**: PDF-Generierung aus XSL-FO ([Download](https://xmlgraphics.apache.org/fop/download.html))
- **diff-pdf**: PDF-Vergleich ([GitHub](https://github.com/vslavik/diff-pdf))
- **OpenJDK/JRE**: für Saxon und Apache FOP. JDK empfohlen für Worker-Pools ([Eclipse Temurin](https://adoptium.net))
## Verwendung
@@ -74,12 +75,14 @@ Für die volle Funktionalität benötigen Sie:
uv run python src/main.py
```
### Erste Start
Konfigurieren Sie Ihre Tools (Saxon, Apache FOP, diff-pdf) in den Einstellungen
### Projekt erstellen
1. Legen Sie ein neues Projekt an
2. Konfigurieren Sie Ihre Tools (Saxon, Apache FOP) in den Einstellungen
3. Organisieren Sie XSL-Stylesheets und XML-Dateien in der Baumstruktur
4. Führen Sie Transformationen aus
2. Organisieren Sie XSL-Stylesheets und XML-Dateien in der Baumstruktur
3. Führen Sie Transformationen aus
### Konfiguration
+1 -1
View File
@@ -263,5 +263,5 @@ HINWEISE
================================================================================
Stand: Mai 2026
Erstellt für: DocuMentor v1.7.1
Erstellt für: DocuMentor v1.7.3
================================================================================
+1 -1
View File
@@ -10,7 +10,7 @@
; Build-Befehl: iscc installer.iss
#define MyAppName "DocuMentor"
#define MyAppVersion "1.7.1"
#define MyAppVersion "1.7.3"
#define MyAppPublisher "Ihr Name/Organisation"
#define MyAppURL "https://github.com/yourusername/xsl-validator"
#define MyAppExeName "DocuMentor.exe"
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "DocuMentor"
version = "1.7.1"
version = "1.7.3"
description = "Professionelle XSL-Transformations-Verwaltung und PDF-Generierung"
readme = "README.md"
license = {text = "MIT"}
+2
View File
@@ -29,6 +29,8 @@ ICONS = [
"file-plus",
"columns",
"sliders",
"database",
"activity",
]
BASE_URL = "https://raw.githubusercontent.com/feathericons/feather/master/icons/{name}.svg"
+57 -9
View File
@@ -1,5 +1,5 @@
import logging
from PySide6.QtCore import QByteArray, QFile, Qt
from PySide6.QtCore import QByteArray, QEvent, QFile, Qt
from PySide6.QtGui import QIcon, QPainter, QPixmap, QPalette
from PySide6.QtSvg import QSvgRenderer
from PySide6.QtWidgets import QApplication
@@ -7,6 +7,10 @@ import res.resources_rc # noqa: F401 # registriert die Qt-Ressourcen (Icons) b
logger = logging.getLogger(__name__)
# Basisgröße für das einmalige Rendern; Qt skaliert daraus die benötigten Icon-Größen
# (16/24/32 px in Menüs, Bäumen, Buttons). 64 px bietet genug Reserve auch für HiDPI.
_RENDER_SIZE = 64
# Cache: (Icon-Name, Theme-Textfarbe) → fertig gerendertes QIcon.
# Der Farb-Anteil im Schlüssel sorgt dafür, dass ein Theme-Wechsel automatisch
# neue Einträge erzeugt, ohne dass der Cache explizit geleert werden muss.
@@ -44,14 +48,58 @@ def icon(name: str) -> QIcon:
f.close()
renderer = QSvgRenderer(svg_data)
result = QIcon()
for size in (16, 24, 32):
pixmap = QPixmap(size, size)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
result.addPixmap(pixmap)
pixmap = QPixmap(_RENDER_SIZE, _RENDER_SIZE)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
result = QIcon(pixmap)
_ICON_CACHE[cache_key] = result
return result
class IconRefreshMixin:
"""
Mischklasse für Top-Level-Widgets, deren Icons sich beim Theme-Wechsel automatisch neu einfärben.
Voraussetzungen für die nutzende Klasse:
- Sie stellt eine Methode ``_setup_icons()`` bereit, die alle ihre Icons neu setzt.
- Nach abgeschlossener Initialisierung setzt sie ``self._icons_ready = True``
(idealerweise zusammen mit ``self._last_icon_color``, siehe ``mark_icons_ready()``).
Bei einem Style-/Palette-Wechsel wird nur wenn sich die Theme-Textfarbe tatsächlich
geändert hat ``_on_icon_theme_changed()`` aufgerufen (Standard: ``_setup_icons()``).
Unterklassen können ``_on_icon_theme_changed()`` überschreiben, um zusätzlich z.B.
Baum-Icons neu zu zeichnen.
Hinweis: ``IconRefreshMixin`` muss in der Klassenbasis VOR der Qt-Basisklasse
(``QMainWindow``/``QDialog``) stehen, damit das überschriebene ``changeEvent`` greift.
"""
_icons_ready: bool = False
_last_icon_color: str | None = None
@staticmethod
def _current_text_color() -> str | None:
app = QApplication.instance()
if app is None:
return None
return app.palette().color(QPalette.ColorRole.WindowText).name()
def mark_icons_ready(self):
"""Schaltet die Theme-Reaktivität scharf (nach dem ersten ``_setup_icons()`` aufrufen)."""
self._last_icon_color = self._current_text_color()
self._icons_ready = True
def changeEvent(self, event):
if event.type() in (QEvent.Type.PaletteChange, QEvent.Type.StyleChange) and self._icons_ready:
color = self._current_text_color()
if color is not None and color != self._last_icon_color:
self._last_icon_color = color
self._on_icon_theme_changed()
super().changeEvent(event)
def _on_icon_theme_changed(self):
"""Reaktion auf eine geänderte Theme-Textfarbe. Standard: alle Icons neu setzen."""
self._setup_icons()
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>

After

Width:  |  Height:  |  Size: 239 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>

After

Width:  |  Height:  |  Size: 318 B

+2
View File
@@ -21,5 +21,7 @@
<file>icons/file-plus.svg</file>
<file>icons/columns.svg</file>
<file>icons/sliders.svg</file>
<file>icons/database.svg</file>
<file>icons/activity.svg</file>
</qresource>
</RCC>
+685 -634
View File
File diff suppressed because it is too large Load Diff
+19 -9
View File
@@ -25,7 +25,7 @@ from ui.mixins import (
TransformationMixin,
)
from conf import app_settings, Project, ProjectData, TreeNode, XslFile
from icons import icon
from icons import icon, IconRefreshMixin
from pathlib import Path
@@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
class MainWindow(
IconRefreshMixin,
QMainWindow,
TreeManagerMixin,
PdfViewerMixin,
@@ -141,8 +142,11 @@ class MainWindow(
# Zoom per Mausrad (STRG+Mausrad) für PDF-Viewer aktivieren
self._setup_scroll_area_zoom()
# Icons setzen (überschreibt fromTheme()-Icons aus _ui.py)
# Icons setzen (überschreibt fromTheme()-Icons aus _ui.py). Läuft nach
# _connect_signals(), daher existieren auch die programmatisch erzeugten Aktionen.
self._setup_icons()
# Ab hier reagiert das Fenster auf Theme-Wechsel (siehe IconRefreshMixin)
self.mark_icons_ready()
# Gespeicherte UI-Zustände wiederherstellen
self._restore_ui_state()
@@ -153,14 +157,21 @@ class MainWindow(
self.ui.actionBeenden.setIcon(icon("log-out"))
self.ui.actionEinstellungen.setIcon(icon("settings"))
self.ui.actionVorhandene_Projekte.setIcon(icon("folder"))
self.ui.actionAlle_XML_Dateien_transformieren.setIcon(icon("play-circle"))
self.ui.actionAlle_XML_Dateien_neu_transformieren_force.setIcon(icon("refresh-cw"))
self.ui.actionAus_Datenbank_laden.setIcon(icon("database"))
self.ui.view_ref_pdf.setIcon(icon("file"))
self.ui.view_new_pdf.setIcon(icon("file"))
self.ui.accept_changes.setIcon(icon("check-circle"))
if hasattr(self, "action_xsl_dependencies"):
self.action_xsl_dependencies.setIcon(icon("git-branch"))
if hasattr(self, "action_info"):
self.action_info.setIcon(icon("info"))
self.action_worker_metrics.setIcon(icon("activity"))
self.action_xsl_dependencies.setIcon(icon("git-branch"))
self.action_info.setIcon(icon("info"))
def _on_icon_theme_changed(self):
"""Theme-Wechsel: Icons neu einfärben und falls ein Projekt offen ist die Baum-Icons."""
self._setup_icons()
if getattr(self, "pdf_project", None):
self._load_nodes_to_tree()
def _restore_ui_state(self):
"""Stellt die gespeicherten UI-Zustände wieder her (Fenstergeometrie, Splitter, TreeWidget-Spalten)."""
@@ -358,9 +369,8 @@ class MainWindow(
logger.info(f"Theme erfolgreich gewechselt zu: {theme_name}")
app_settings.theme = theme_name
app_settings.save()
self._setup_icons()
if hasattr(self, "pdf_project") and self.pdf_project:
self._load_nodes_to_tree()
# Icons/Baum werden über changeEvent (PaletteChange/StyleChange) neu eingefärbt,
# siehe IconRefreshMixin._on_icon_theme_changed()
else:
logger.error(f"Fehler: Theme '{theme_name}' konnte nicht erstellt werden")
+21 -2
View File
@@ -12,7 +12,7 @@ import tempfile
from pathlib import Path
from PySide6.QtCore import QUrl, Qt
from icons import icon
from icons import icon, IconRefreshMixin
from PySide6.QtWidgets import (
QComboBox,
QDialog,
@@ -120,7 +120,7 @@ except ImportError:
logger.warning("PySide6-WebEngine nicht verfügbar — Netzwerkgraph-Tab deaktiviert")
class XslDependencyDialog(QDialog):
class XslDependencyDialog(IconRefreshMixin, QDialog):
"""Dialog zur Anzeige des vollständigen XSL-Abhängigkeitsgraphen."""
def __init__(
@@ -185,6 +185,25 @@ class XslDependencyDialog(QDialog):
self._build_layout_controls()
self._restore_graph_layout_settings()
# Ab hier auf Theme-Wechsel reagieren (siehe IconRefreshMixin)
self.mark_icons_ready()
def _setup_icons(self):
"""Färbt alle Icons gemäß aktuellem Theme neu ein (Button und Baum-Icons)."""
self.ui.settingsButton.setIcon(icon("sliders"))
# Datei-Baum neu einfärben, dabei die aktuelle Auswahl erhalten
current = self.ui.fileTree.currentItem()
current_path = current.data(0, Qt.ItemDataRole.UserRole) if current else None
self._populate_file_tree()
if current_path is not None:
for i in range(self.ui.fileTree.topLevelItemCount()):
item = self.ui.fileTree.topLevelItem(i)
if item.data(0, Qt.ItemDataRole.UserRole) == current_path:
# Setzt die Auswahl → _on_file_selected zeichnet den Abhängigkeitsbaum neu ein
self.ui.fileTree.setCurrentItem(item)
break
def reject(self):
"""Speichert Layout-Einstellungen und räumt temporäre Dateien und QWebEngineView auf."""
self._save_graph_layout_settings()
Generated
+1 -1
View File
@@ -39,7 +39,7 @@ wheels = [
[[package]]
name = "documentor"
version = "1.7.1"
version = "1.7.3"
source = { virtual = "." }
dependencies = [
{ name = "connectorx" },
+4 -4
View File
@@ -1873,8 +1873,8 @@
<span class="download-card-badge">EMPFOHLEN</span>
</div>
<p class="download-card-desc">Windows-Installer mit automatischer Einrichtung. Erstellt Startmenü-Einträge und ermöglicht saubere Deinstallation über die Systemsteuerung.</p>
<span class="download-card-meta">DocuMentor-1.7.0.msi &mdash; ca. 255 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/v1.7.0/DocuMentor-1.7.0.msi" class="btn-download">&#9660; MSI herunterladen</a>
<span class="download-card-meta">DocuMentor-1.7.3.msi &mdash; ca. 255 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/v1.7.3/DocuMentor-1.7.3.msi" class="btn-download">&#9660; MSI herunterladen</a>
</div>
<div class="download-card corner-brackets">
<div class="download-card-header">
@@ -1882,8 +1882,8 @@
<span class="download-card-badge">PORTABEL</span>
</div>
<p class="download-card-desc">Portable Version ohne Installation. Entpacken und direkt starten &mdash; ideal für eingeschränkte Umgebungen ohne Administratorrechte.</p>
<span class="download-card-meta">DocuMentor-1.7.0.zip &mdash; ca. 315 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/v1.7.0/DocuMentor-1.7.0.zip" class="btn-download">&#9660; ZIP herunterladen</a>
<span class="download-card-meta">DocuMentor-1.7.3.zip &mdash; ca. 315 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/v1.7.3/DocuMentor-1.7.3.zip" class="btn-download">&#9660; ZIP herunterladen</a>
</div>
</div>
</div>