Files
xsl-validator/openwiki/architecture/overview.md
T
info 4733ea6b82 chore: OpenWiki-Dokumentation einrichten, AGENTS.md ergänzen
Fügt OpenWiki-Setup (Workflow, Seiten, CLAUDE.md-Verweis) und AGENTS.md
hinzu, damit wiederkehrende Code-Dokumentation automatisch gepflegt wird.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 14:22:57 +02:00

114 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: Architecture Overview
title: Architektur-Überblick
description: PySide6-Mixin-Architektur, UI-Pattern (generierte UI vs. Implementierung), Thread-Modell und Konfigurationssystem von DocuMentor.
tags: [architektur, pyside6, mixins, threads, konfiguration]
---
# Architektur-Überblick
## Technologie-Stack
| Schicht | Technologie | Zweck |
|---------|------------|------|
| GUI | PySide6 (Qt 6) | Native Desktop-Oberfläche |
| Konfiguration | Pydantic + pydantic-settings | Typsichere Datenmodelle, JSON/YAML-Persistenz |
| Datenverarbeitung | Polars + ConnectorX | PostgreSQL-Abfragen, DataFrames |
| XSL-Parsing | lxml | XSL-Abhängigkeitsgraph |
| Hashing | hashlib (blake2b) | Duplikatserkennung |
| Build | PyInstaller + WiX/Inno Setup | Windows-Distribution |
| Paketverwaltung | uv | Dependency-Management |
## Einstiegspunkt
Die Anwendung startet in `src/main.py`:
1. Logging konfigurieren (Datei + Konsole, 24h Auto-Cleanup)
2. `QApplication` erstellen, App-Icon setzen
3. `MainWindow` instanziieren und anzeigen
4. Bei fehlender Tool-Konfiguration `AppSettingsDlg` modal öffnen
5. Qt-Event-Loop starten
## MainWindow Mixin-Architektur
`MainWindow` (`src/ui/MainWindow.py`) ist die zentrale Klasse und erbt von mehreren Mixins, um Funktionalität zu trennen:
```
MainWindow
├── IconRefreshMixin (src/icons.py) Theme-Wechsel → Icons neu einfärben
├── QMainWindow (PySide6) Qt-Basisfenster
├── TreeManagerMixin (mixins/tree_manager.py) Baum-Widget, Kontextmenüs, Node-Operationen
├── PdfViewerMixin (mixins/pdf_viewer.py) PDF-Rendering, Alpha-Blending, Zoom
├── WorkerPoolMixin (mixins/worker_pool.py) Saxon/FOP Worker-Pool Lebenszyklus
├── DatabaseMixin (mixins/database.py) PostgreSQL-Abfragen, Obsolete-Erkennung
├── DragDropMixin (mixins/drag_drop.py) Drag-and-Drop für XML-Dateien
├── HashCalculationMixin (mixins/hash_calculation.py) blake2b-Hash-Berechnung, Duplikatserkennung
└── TransformationMixin (mixins/transformation.py) XSL-Transformations-Ausführung
```
Jedes Mixin erwartet bestimmte Attribute (`self.ui`, `self.project`, `self.pdf_project`) und Methoden von anderen Mixins. Die Mixins sind in `src/ui/mixins/__init__.py` gebündelt.
### Mixin-Abhängigkeiten
`TransformationMixin` benötigt Methoden aus `WorkerPoolMixin` (`_initialize_saxon_worker_pool`, `_shutdown_saxon_worker_pool`) und `TreeManagerMixin` (`_collect_parent_params`, `_create_centered_progress_bar`). `HashCalculationMixin` und `DragDropMixin` benötigen `_save_project_settings()` und `_load_nodes_to_tree()` von MainWindow.
## UI-Pattern: Generierte UI vs. Implementierung
DocuMentor folgt einem strikten PySide6-Pattern:
1. **UI-Definitionsdateien** (`*_ui.py`, z.B. `MainWinddow_ui.py`): Aus `.ui`-Dateien von einer VS Code Extension generiert. Enthalten nur Widget-Layout-Definitionen als Klasse (`Ui_MainWindow`). **Nicht manuell bearbeiten.**
2. **Implementierungsdateien** (ohne `_ui`-Suffix, z.B. `MainWindow.py`): Importieren die UI-Klasse, instanziieren sie als `self.ui` und fügen Business-Logik hinzu.
```python
class JavaVmConfigDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_JavaVmConfigDialog()
self.ui.setupUi(self)
# Signale NACH setupUi() verbinden
self.ui.browseButton.clicked.connect(self._browse_file)
```
**Regeln:**
- UI-Klassen niemals direkt erben, nur als `self.ui`-Member verwenden
- Alle Widgets über `self.ui.widgetName` zugreifen
- Signal-Verbindungen immer nach `setupUi()`
## Thread-Modell
Asynchrone Operationen verwenden `QThread`-Subklassen aus `src/ui/threads.py`:
| Thread-Klasse | Zweck | Signale |
|---------------|------|---------|
| `XmlHashCalculatorThread` | blake2b-Hash-Berechnung für XML-Dateien | `hash_calculated`, `calculation_finished`, `error_occurred` |
| `XmlBatchProcessingThread` | Batch-Verarbeitung: Hash + Duplikatserkennung + Dateikopieren | `progress_update`, `file_processed`, `processing_finished` |
| `TransformationThread` | Ausführung der XSL-Transformations-Pipeline | Fortschritts- und Abschluss-Signale |
| `DatabaseQueryThread` | Asynchrone PostgreSQL-Abfragen (in `mixins/database.py`) | `query_completed` (DataFrame), `query_failed` |
**Wichtig:** Threads immer mit `.start()` starten, nie `.run()` direkt aufrufen.
## Konfigurationssystem
Die Konfiguration ist in [Datenmodelle & Konfiguration](../data-models.md) detailliert beschrieben. Zusammenfassung:
- **`AppSettings`** (`src/conf.py`): Globales Singleton (`app_settings`), gespeichert als JSON. Enthält Tool-Listen, UI-Zustand, Worker-Pool-Einstellungen.
- **`ProjectData`** (`src/conf.py`): Projektspezifisch, gespeichert als `project.yaml` im Projektverzeichnis. Enthält die Baumstruktur (`TreeNode → XslFile → XmlFile`).
## Icon-System
Icons werden über `src/icons.py` verwaltet:
- Feather Icons aus dem Qt-Ressource-System (`:/icons/<name>.svg`)
- Einmaliges Rendern bei 64px, Qt skaliert für 16/24/32px
- Theme-reactiv: Färbung nach `QPalette.WindowText`, Cache-Schlüssel enthält Theme-Farbe
- `IconRefreshMixin` erlaubt Theme-Wechsel zur Laufzeit ohne manuellen Cache-Reset
## XSL-Abhängigkeitsgraph
`src/xsl_dependencies.py` (`XslDependencyGraph`) parst XSL-Dateien mit lxml und baut einen Abhängigkeitsgraph für `<xsl:import/>` und `<xsl:include/>`. Der Graph wird über Datei-mtime automatisch invalidiert. Visualisiert wird er im Dialog `src/ui/XslDependencyDialog.py` via vis.js.
Der Abhängigkeitsgraph ist auch für die [Transformations-Pipeline](../workflows/transformation-pipeline.md) relevant: wenn eine importierte XSL-Datei neuer ist als das Output-PDF, gilt die Transformation als veraltet.
## Obsolete-Erkennung
`src/obsolete_detector.py` enthält reine Analyselogik (ohne Qt-Abhängigkeit) zum Finden veralteter `XslFile`-Einträge nach Datenbank-Import. Wird vom `DatabaseMixin` nach dem DB-Laden aufgerufen und im `ObsoleteEntriesDialog` (`src/ui/ObsoleteEntriesDialog.py`) angezeigt.