6 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
info 74b08e31c7 Version 1.7.1: Icon-System vereinfacht und gecacht
- icons.py: _ICON_MAP entfernt (war reine Identitätsabbildung), Pfad
  wird direkt aus dem Namen abgeleitet
- icons.py: Render-Cache mit Schlüssel (name, theme-farbe) ergänzt,
  vermeidet wiederholtes SVG-Rendering bei Baum-/Kontextmenü-Aufbau
- icons.py: Qt-Ressourcen-Registrierung gekapselt (Import aus main.py
  hierher verschoben)
- download_icons.py: toten folder-open-Eintrag entfernt (in Feather
  nicht vorhanden, nirgends genutzt)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 11:51:21 +02:00
info c837802fe7 docs(web): update download links and version to 1.7.0
Update index.html to reflect the latest release version 1.7.0,
including MSI and ZIP download links with corrected release tag format.
2026-05-30 16:50:05 +02:00
info 712bd8917e version-bump Skill: Git-Tag nach Commit setzen
Der Skill setzt jetzt nach einem erfolgreichen Commit mit Versionserhöhung
automatisch einen passenden annotated Git-Tag (z.B. v1.7.1).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 16:38:57 +02:00
18 changed files with 835 additions and 703 deletions
+14
View File
@@ -60,6 +60,20 @@ Führe zuerst `uv version --bump` aus, lese danach die neue Version aus `pyproje
Nachdem die Versionsdateien aktualisiert wurden (oder der Benutzer "Nein" gewählt hat), erstelle den Commit ganz normal nach den üblichen Commit-Konventionen. Falls die Version geändert wurde, füge die geänderten Versionsdateien zum Commit hinzu.
### Schritt 4: Git-Tag setzen (nur bei Versionserhöhung)
Wenn der Benutzer eine Versionserhöhung gewählt hat und der Commit erfolgreich war, setze einen annotated Git-Tag mit der neuen Version:
```bash
git tag -a "vX.Y.Z" -m "Version X.Y.Z"
```
Wobei `X.Y.Z` die neue Version aus `pyproject.toml` ist. Das Tag-Format ist immer `v` + Versionsnummer (z.B. `v1.7.1`).
Informiere den Benutzer danach kurz: „Tag `vX.Y.Z` gesetzt. Mit `git push origin vX.Y.Z` kannst du ihn pushen."
Wenn der Benutzer "Nein" gewählt hat, wird kein Tag gesetzt.
## Wichtige Hinweise
- `pyproject.toml` **niemals direkt bearbeiten** — immer `uv version --bump` verwenden
+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.0"
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
+2 -2
View File
@@ -262,6 +262,6 @@ HINWEISE
da sich diese ändern können.
================================================================================
Stand: April 2026
Erstellt für: DocuMentor v1.7.0
Stand: Mai 2026
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.0"
#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.0"
version = "1.7.3"
description = "Professionelle XSL-Transformations-Verwaltung und PDF-Generierung"
readme = "README.md"
license = {text = "MIT"}
+2 -1
View File
@@ -29,7 +29,8 @@ ICONS = [
"file-plus",
"columns",
"sliders",
"folder-open",
"database",
"activity",
]
BASE_URL = "https://raw.githubusercontent.com/feathericons/feather/master/icons/{name}.svg"
+65 -32
View File
@@ -1,38 +1,25 @@
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
import res.resources_rc # noqa: F401 # registriert die Qt-Ressourcen (Icons) beim Import
logger = logging.getLogger(__name__)
_ICON_MAP: dict[str, str] = {
"folder-plus": ":/icons/folder-plus.svg",
"log-out": ":/icons/log-out.svg",
"settings": ":/icons/settings.svg",
"folder": ":/icons/folder.svg",
"refresh-cw": ":/icons/refresh-cw.svg",
"plus-circle": ":/icons/plus-circle.svg",
"minus-circle": ":/icons/minus-circle.svg",
"play-circle": ":/icons/play-circle.svg",
"file": ":/icons/file.svg",
"check-circle": ":/icons/check-circle.svg",
"info": ":/icons/info.svg",
"git-branch": ":/icons/git-branch.svg",
"file-text": ":/icons/file-text.svg",
"code": ":/icons/code.svg",
"chevron-down": ":/icons/chevron-down.svg",
"chevron-up": ":/icons/chevron-up.svg",
"trash-2": ":/icons/trash-2.svg",
"file-plus": ":/icons/file-plus.svg",
"columns": ":/icons/columns.svg",
"sliders": ":/icons/sliders.svg",
}
# 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.
_ICON_CACHE: dict[tuple[str, bytes], QIcon] = {}
def icon(name: str) -> QIcon:
"""
Lädt ein Icon aus dem Qt-Ressource-System und färbt es mit der aktuellen Palette-Farbe.
Lädt ein Feather-Icon aus dem Qt-Ressource-System und färbt es mit der aktuellen Palette-Farbe.
Args:
name: Feather-Icon-Name (z.B. "folder-plus", "settings")
@@ -40,10 +27,7 @@ def icon(name: str) -> QIcon:
Returns:
QIcon in der Textfarbe des aktiven Themes, oder leerer QIcon bei unbekanntem Namen
"""
path = _ICON_MAP.get(name)
if path is None:
logger.warning(f"Unbekannter Icon-Name: {name!r}")
return QIcon()
path = f":/icons/{name}.svg"
app = QApplication.instance()
if app is None:
@@ -51,6 +35,10 @@ def icon(name: str) -> QIcon:
color = app.palette().color(QPalette.ColorRole.WindowText).name().encode()
cache_key = (name, color)
if (cached := _ICON_CACHE.get(cache_key)) is not None:
return cached
f = QFile(path)
if not f.open(QFile.OpenModeFlag.ReadOnly):
logger.warning(f"Icon konnte nicht geöffnet werden: {path}")
@@ -60,13 +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 = QPixmap(_RENDER_SIZE, _RENDER_SIZE)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
result.addPixmap(pixmap)
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()
-3
View File
@@ -98,9 +98,6 @@ def main():
if icon_path.exists():
app.setWindowIcon(QIcon(str(icon_path)))
# Qt-Ressourcen registrieren (Icons)
import res.resources_rc # noqa: F401
# Hauptfenster erstellen
window = MainWindow()
+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>
+85 -34
View File
@@ -168,6 +168,28 @@ h d=\x22M3.51 9a9 9\
4 4.36A9 9 0 0 0\
20.49 15\x22/></sv\
g>\
\x00\x00\x01>\
<\
svg xmlns=\x22http:\
//www.w3.org/200\
0/svg\x22 width=\x2224\
\x22 height=\x2224\x22 vi\
ewBox=\x220 0 24 24\
\x22 fill=\x22none\x22 st\
roke=\x22currentCol\
or\x22 stroke-width\
=\x222\x22 stroke-line\
cap=\x22round\x22 stro\
ke-linejoin=\x22rou\
nd\x22><ellipse cx=\
\x2212\x22 cy=\x225\x22 rx=\x22\
9\x22 ry=\x223\x22/><path\
d=\x22M21 12c0 1.6\
6-4 3-9 3s-9-1.3\
4-9-3\x22/><path d=\
\x22M3 5v14c0 1.66 \
4 3 9 3s9-1.34 9\
-3V5\x22/></svg>\
\x00\x00\x01\x12\
<\
svg xmlns=\x22http:\
@@ -395,6 +417,23 @@ y2=\x2217\x22/><polyli\
ne points=\x2210 9 \
9 9 8 9\x22/></svg>\
\
\x00\x00\x00\xef\
<\
svg xmlns=\x22http:\
//www.w3.org/200\
0/svg\x22 width=\x2224\
\x22 height=\x2224\x22 vi\
ewBox=\x220 0 24 24\
\x22 fill=\x22none\x22 st\
roke=\x22currentCol\
or\x22 stroke-width\
=\x222\x22 stroke-line\
cap=\x22round\x22 stro\
ke-linejoin=\x22rou\
nd\x22><polyline po\
ints=\x2222 12 18 1\
2 15 21 9 3 6 12\
2 12\x22/></svg>\
\x00\x00\x01q\
<\
svg xmlns=\x22http:\
@@ -524,6 +563,10 @@ qt_resource_name = b"\
\x04\x1b\xd7\x87\
\x00r\
\x00e\x00f\x00r\x00e\x00s\x00h\x00-\x00c\x00w\x00.\x00s\x00v\x00g\
\x00\x0c\
\x05\xc9\x15\xc7\
\x00d\
\x00a\x00t\x00a\x00b\x00a\x00s\x00e\x00.\x00s\x00v\x00g\
\x00\x0a\
\x0a\xc8\xf6\x87\
\x00f\
@@ -560,6 +603,10 @@ qt_resource_name = b"\
\x06\xf2R'\
\x00f\
\x00i\x00l\x00e\x00-\x00t\x00e\x00x\x00t\x00.\x00s\x00v\x00g\
\x00\x0c\
\x0cQ;g\
\x00a\
\x00c\x00t\x00i\x00v\x00i\x00t\x00y\x00.\x00s\x00v\x00g\
\x00\x0d\
\x09\xc3S\x87\
\x00f\
@@ -581,48 +628,52 @@ qt_resource_name = b"\
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x14\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x16\x00\x00\x00\x02\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x06\xa5\
\x00\x00\x01\x9ey\x0c6\xfa\
\x00\x00\x01L\x00\x00\x00\x00\x00\x01\x00\x00\x0e\xe4\
\x00\x00\x01\x9ey\x0c3\x89\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x00\x10&\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01\x9ey\x0c4\x94\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00\x07\xcd\
\x00\x00\x01\x9ey\x0c2\xb4\
\x00\x00\x02\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x1a\x0c\
\x00\x00\x01\x9ey\x0c:\x95\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x02\x98\x00\x00\x00\x00\x00\x01\x00\x00\x1cA\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x00v\x00\x00\x00\x00\x00\x01\x00\x00\x04\x1e\
\x00\x00\x01\x9ey\x0cB\x90\
\x00\x00\x01\xb2\x00\x00\x00\x00\x00\x01\x00\x00\x12c\
\x00\x00\x01\x9ey\x0c9c\
\x00\x00\x02*\x00\x00\x00\x00\x00\x01\x00\x00\x17~\
\x00\x00\x01\x9ey\x0c<\x93\
\x00\x00\x01p\x00\x00\x00\x00\x00\x01\x00\x00\x10\x0f\
\x00\x00\x01\x9ey\x0c.\xf3\
\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x00\x05B\
\x00\x00\x01\x9ey\x0c-\x81\
\x00\x00\x01\xea\x00\x00\x00\x00\x00\x01\x00\x00\x14t\
\x00\x00\x01\x9ey\x0c;i\
\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x00\x13\x91\
\x00\x00\x01\x9ey\x0c>\xf8\
\x00\x00\x02\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x16\x09\
\x00\x00\x01\x9ey\x0cA`\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x01\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x13\xa5\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x02f\x00\x00\x00\x00\x00\x01\x00\x00\x19\xb3\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x00\xee\x00\x00\x00\x00\x00\x01\x00\x00\x09$\
\x00\x00\x01\x9ey\x0c1c\
\x00\x00\x01\x9e}\xa1\x91F\
\x00\x00\x01\x8e\x00\x00\x00\x00\x00\x01\x00\x00\x11Q\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x00\x92\x00\x00\x00\x00\x00\x01\x00\x00\x05B\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x02\x08\x00\x00\x00\x00\x00\x01\x00\x00\x15\xb6\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x14\xd3\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x02F\x00\x00\x00\x00\x00\x01\x00\x00\x18>\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x0af\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x00R\x00\x00\x00\x00\x00\x01\x00\x00\x03\x16\
\x00\x00\x01\x9ey\x0c5\xc9\
\x00\x00\x01.\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x1c\
\x00\x00\x01\x9ey\x0c00\
\x00\x00\x02@\x00\x00\x00\x00\x00\x01\x00\x00\x18\x84\
\x00\x00\x01\x9ey\x0c?\xdf\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x01L\x00\x00\x00\x00\x00\x01\x00\x00\x0c^\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x02|\x00\x00\x00\x00\x00\x01\x00\x00\x1a\xb9\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x02(\x00\x00\x00\x00\x00\x01\x00\x00\x17K\
\x00\x00\x01\x9e}\xa1\x92!\
\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x01\x05\
\x00\x00\x01\x9ey\x0cC\xc7\
\x00\x00\x01\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x11L\
\x00\x00\x01\x9ey\x0c8.\
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x00\x0a:\
\x00\x00\x01\x9ey\x0c=\xc8\
\x00\x00\x01\x9ez2\xf1\x12\
\x00\x00\x01\xaa\x00\x00\x00\x00\x00\x01\x00\x00\x12\x8e\
\x00\x00\x01\x9ez2\xf1\x11\
\x00\x00\x01&\x00\x00\x00\x00\x00\x01\x00\x00\x0b|\
\x00\x00\x01\x9ez2\xf1\x11\
"
def qInitResources():
+17 -7
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,15 +157,22 @@ 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_worker_metrics.setIcon(icon("activity"))
self.action_xsl_dependencies.setIcon(icon("git-branch"))
if hasattr(self, "action_info"):
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)."""
global app_settings
@@ -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.0"
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.6.3.msi &mdash; ca. 255 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/1.6.3/DocuMentor-1.6.3.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.6.3.zip &mdash; ca. 315 MB</span>
<a href="https://code.vitaligraf.de/info/xsl-validator/releases/download/1.6.3/DocuMentor-1.6.3.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>