--- 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/.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 `` und ``. 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.