Files
Projektwissen/docs/architektur/schnittstellen.md
T
DanielandClaude Opus 5.5 9c7c4b900d Kartenrand-Anordnung (Basis #93) dokumentiert
- schnittstellen.md Abschnitt 12: Element-IDs, die print_kartenrand braucht; Seitengröße nur Mindestgröße
- fallstricke.md: minimumSize liefert vor dem ersten Zeichnen die ungefilterte Legendenhöhe
- modulkarte.md: print_kartenrand, aktualisierte Zeilenzahlen
- STATUS.md: Punkt 15 (Stand #88/#90/#92, QGIS 4 geprüft), neuer Punkt 16
- Testkatalog neu erzeugt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:07:03 +02:00

164 lines
12 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.
# Schnittstellen und Verträge
Lebende Referenz der **impliziten Verträge** zwischen den Komponenten. Wer eine dieser Stellen ändert, ändert eine
Schnittstelle und muss alle Nutzer mit anpassen. Stand 02.09.2026.
## 1. Plugin-Lebenszyklus: sn_basis ↔ Fachplugin
```mermaid
sequenceDiagram
participant QGIS
participant B as sn_basis.BasisPlugin
participant F as Fachplugin (z. B. Verfahrensgebiet)
QGIS->>B: classFactory(iface) / initGui()
B->>B: Navigation().init_ui() (Menü + Toolbar "LNO Sachsen")
B->>B: setup_verfahrensgebiet_listener()
loop für jedes Plugin in qgis.utils.plugins mit Namen sn_* (außer sn_basis)
B->>F: initGui()
F->>B: basis.ui.add_action(name, run, tooltip, priority)
end
B->>B: finalize_menu_and_toolbar() (sortiert nach priority)
QGIS->>F: Nutzer klickt Action → run()
F->>F: DockWidget(...) mit objectName "sn_dock_<name>"
F->>B: DockManager.show(dock) (entfernt andere sn_dock_*)
F->>B: basis.ui.set_active_plugin(action)
QGIS->>F: unload() → removeDockWidget, basis.ui.remove_action(action)
```
Vertrag für ein Fachplugin (Muster: `Plugin_SN_Widmung/main.py`):
- Paketname beginnt mit `sn_` (nur dann ruft `sn_basis` das `initGui()` nach).
- `initGui()` holt `plugins.get("sn_basis")`; ohne `basis.ui` nichts tun (QGIS lädt Plugins in nicht garantierter Reihenfolge; `sn_basis` ruft später nach).
- `add_action(text, callback, tooltip="", priority=100)` → `QAction` (checkable, in exklusiver Gruppe). Vergebene Prioritäten: Verfahrensgebiet 10, Plan41 20, Widmung 30.
- Dock-Objektname `sn_dock_<pluginname klein>`; nur ein `sn_dock_*` ist gleichzeitig sichtbar.
- `unload()` muss Dock entfernen und Action abmelden, sonst bleiben Reste in Menü/Toolbar.
## 2. DockWidget und Tabs
- Fachplugin-Dock erbt von `sn_basis.ui.base_dockwidget.BaseDockWidget`; Klassenattribut `tabs = [TabKlasse, ...]`.
- Tab-Klasse: `QWidget`-Unterklasse mit Klassenattribut `tab_title`; Konstruktor `Tab(parent)`.
Optional `set_services(pruefmanager=..., data_grabber=...)` (wird von Fachplugin-Docks aufgerufen) und `cleanup()` (wird beim Schließen des Docks aufgerufen – Signale trennen!).
- Gemeinsame Tabs aus `sn_basis`: `ui.tabs.print_tab.PrintTab` (Druck), `ui.tabs.settings_tab.SettingsTab` (Projekteigenschaften).
- `dock.action` = Referenz auf die Toolbar-Action; `BaseDockWidget.closeEvent` setzt sie zurück.
## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt)
Zugriff nur über `sn_basis.functions.variable_wrapper.get_variable(key, scope="project")` / `set_variable(...)`.
Der Wrapper setzt das Präfix `sn_` selbst; Schlüssel **ohne** Präfix übergeben.
| Schlüssel (ohne `sn_`) | Gesetzt von | Gelesen von | Bedeutung |
|---|---|---|---|
| `amt`, `behoerde`, `landkreis_user`, `sachgebiet` | SettingsTab | Druck (Schriftfeld) | benutzerspezifische Angaben |
| `bezeichnung`, `name`, `verfahrensnummer`, `gemeinden`, `landkreise_proj`, `landkreis_kennzahl` | SettingsTab | Verfahrensgebiet (ALKIS-Suche), Druck | Verfahrensangaben; `verfahrensnummer` = VKZ |
| `verfahrensgebiet_layer` | `verfahrensgebiet_manager`, WorkingTab | alle Fachplugins, Druck | Layer-ID des aktuellen Verfahrensgebiets |
| `verfahrens_db`, `tab_a_verfahrens_db` | Plan41 Tab A | Plan41 | Pfad des Verfahrens-GeoPackages |
| `linkliste`, `tab_a_linkliste` | Plan41 Tab A | Plan41 | Pfad der Linkliste (XLSX) |
| `tab_a_layer_id`, `Raumfilter` | Plan41 Tab A | Plan41 | Raumfilter-Einstellung |
| `druckrechteck_xmin/xmax/ymin/ymax` | PrintTab/PrintLogic | Druck | gespeicherter Druckbereich |
| `basemap_auswertebereich` | Basemap-Bereich im Druck-Tab | `basemap_legende` | `verfahrensgebiet` (Standard) oder `druckbereich` (Basis #88) |
| `basemap_legende_erzeugen` | Basemap-Bereich | `print_logic` | `1`/`0`, Standard `1`: Basemap-Legende beim Erstellen einer §-38/§-41-Vorlage |
| `basemap_legende_auswahl` | `basemap_legende`, Stilvorlage laden | Auswahldialog | JSON `{"ausgeblendet": [...], "eingeblendet": [...]}` mit Eintragsschlüsseln des Legendenkatalogs |
| `sn_abdeckungslayer` ⚠ | PrintTab/PrintLogic | Druck (Atlas) | Atlas-Abdeckungslayer. **Schlüssel enthält bereits `sn_` → tatsächliche Variable `sn_sn_abdeckungslayer`**; funktioniert, ist aber inkonsistent |
| `verfahrensgebietslayer` ⚠ | `print_logic.py` | `print_logic.py` | **Zweite Schreibweise neben `verfahrensgebiet_layer`** – vermutlich Altlast, beim Refactoring zusammenführen |
Benutzer-/Maschinendaten außerhalb des Projekts: `QSettings` in `sn_plan41` (`mail`, `api_key` der VLN-API).
## 4. Prüf- und Entscheidungsmodell
- `pruef_ergebnis(ok: bool, meldung, aktion: PruefAktion, kontext)` ist das einheitliche Rückgabeobjekt aller Prüfer (`Dateipruefer`, `Layerpruefer`, `Linkpruefer`, `Stilpruefer`, `Datenbankpruefer`) und vieler Fachfunktionen.
- `PruefAktion` ist ein `Literal` mit Codes wie `datei_nicht_gefunden`, `layer_leer`, `datei_existiert_ueberschreiben`, `verfahrensgebiet_ergaenzen`. Neue Codes dort ergänzen, nicht als freie Strings verwenden.
- `Pruefmanager(ui_modus="qgis")`: `verarbeite(ergebnis)` entscheidet je Aktion (Dialog, Meldung, Standardwert). Mit `ui_modus != "qgis"` (Tests) werden Standardantworten ohne Dialog geliefert.
- Fachplugins erhalten **eine** `Pruefmanager`- und **eine** `DataGrabber`-Instanz aus `main.py` (Konstruktor) und reichen sie an Tabs weiter.
## 5. Datenpipeline Plan41 (Fachdaten aus Linkliste)
```mermaid
flowchart LR
XLSX[assets/Linkliste.xlsx] --> DG[DataGrabber.run]
DG -->|Excel| LA[sn_plan41.Listenauswerter.validate_rows]
LA --> DA[Datenabruf.datenabruf]
VG[(Verfahrensgebiet / Puffer)] --> DA
DA --> LL[LayerLoader: create_layer, filter_by_layer/extent, apply_style]
LL --> DS[Datenschreiber.schreibe_Daten → GeoPackage]
DS --> P[Datenschreiber.lade_Layer + schreibe_log HTML]
```
- Erwartetes Spaltenschema der XLSX: `ident, gruppe, kartenebene, inhalt, link, provider, stildatei`. `provider` ∈ WFS, REST (ArcGIS), OGR.
- Das zentrale Repository `Linkliste` (`linkliste.csv`: `id;gruppe;ebene;inhalt;url;provider;stil`) ist **nicht** angebunden → zwei Wahrheiten, siehe `fallstricke.md`.
- Die einzige Rückwärtsabhängigkeit `sn_basis → sn_plan41` ist der Lazy-Import von `Listenauswerter` in `DataGrabber._process_excel_source` (seit PR #57 abgesichert, damit `sn_verfahrensgebiet` ohne `sn_plan41` lädt).
## 6. Verfahrensgebiet-Abläufe (Fachplugin)
- Zentrale Kennung des Verfahrens: Projektvariable `verfahrensnummer` (VKZ) und/oder `landkreis_kennzahl`.
- ALKIS-Quellen: vereinfachtes ALKIS (`.../public_alkis/vereinf/wfs`) für Gemarkungen/BROR, ALKIS-SF (`.../public_alkis/sf/wfs`) für Flurstücke und `PunktortTA`.
- Layer-Namenskonventionen (String-Konstanten in den Modulen): `BROR_LAYER_NAME`, `FLST_LAYER_NAME`, `JOINED_LAYER_NAME`, „Verfahrensgebiet“, „Neuordnungsgebiet (NoBIS)“, „Neuvermessungsgebiet (NoBIS)“. Andere Module finden Layer **über den Namen** – Umbenennungen sind Schnittstellenänderungen.
- Ablaufdokumentation mit Diagrammen: `Plugin_SN_Verfahrensgebiet/docs/`.
## 7. VLN-Karten-API (Plan41)
- Basis-URL `https://api.flurneuordnung-sachsen.de/v2`; Login per E-Mail/Passwort, danach Header `X-API-Key`.
- Laden: Verfahren (VKZ) → Plan41-GeoJSON → Memory-Layer je Geometriefamilie (`vln_layer_manager`). Upload: alle Teillayer → eine FeatureCollection, `PUT` ersetzt den Serverbestand der VKZ vollständig.
- Arbeits-CRS EPSG:25833.
## 8. Mock-Modus (Tests ohne QGIS)
- Jeder Wrapper versucht den Import von `qgis`/`PyQt6`/`PyQt5`; schlägt das fehl, definiert er `_Mock*`-Klassen. Kennzeichen: `qgiscore_wrapper.QGIS_AVAILABLE`, `qt_wrapper.QT_VERSION == 0`.
- Tests importieren Module über den Paketnamen (`from sn_basis...`), deshalb müssen die Repos unter ihrem Paketnamen erreichbar sein → `scripts/run_tests.py` erledigt das über ein temporäres Verzeichnis.
- Tests, die echtes Layer- oder Netzwerkverhalten brauchen, laufen nur in QGIS (`tests/test_qgis.bat`, Windows).
## 9. Release-Vertrag
```mermaid
flowchart LR
T["Tag v26.9.1-u auf Plugin-Repo"] --> W1["Plugin-Workflow release.yaml"]
W1 -->|"workflow_dispatch + Payload"| W2["Repository, Branch hidden/workflows"]
W2 --> C["checkout Branch = Kanal<br/>(unstable / testing / stable)"]
C --> M["metadata.template → metadata.txt:<br/>version, experimental, repository-URL"]
M --> Z["rsync ohne .git .gitea .gitignore __pycache__ → ZIP_FOLDER.zip"]
Z --> R["Gitea-Release + Asset"]
R --> X["plugins-&lt;kanal&gt;.xml aktualisieren"]
```
- Tagformat `v<JJ.M.N>-u` (unstable), `-t` (testing), ohne Suffix (stable). Version = Jahr.Monat.Laufnummer.
- **Gebaut wird der Kopf des Kanal-Branches, nicht der getaggte Commit.** Tag also nur auf dem aktuellen Kopf von `unstable` bzw. `testing` setzen.
- Paketname im ZIP kommt aus der Gitea-Repo-Variable `ZIP_FOLDER` (z. B. `sn_basis`).
- `metadata.txt` im Repo ist nur ein Abbild des letzten Builds; gepflegt wird `.gitea/templates/metadata.template`.
- Release-Body = Block über dem ersten `---` in `changelog.txt`.
- Neue Workspace-Dateien (`AGENTS.md`, `CLAUDE.md`, `.agents/`, `.claude/`, `docs/` der Repos) landen derzeit im ZIP → Excludes im zentralen Workflow ergänzen (offener Punkt in `STATUS.md`).
## 10. Linkliste-Repository
`linkliste.csv` (Semikolon, Spalten `id;gruppe;ebene;inhalt;url;provider;stil`) → Gitea Action → `linkliste.xml/json/yaml` auf `main`. Die Plan41-Laufzeit nutzt stattdessen die mitgelieferte `assets/Linkliste.xlsx` mit abweichenden Spaltennamen.
## 11. Basemap-Legende im Druck (Basis #88)
- **Welche Layouts:** `print_logic.LAYOUTS_MIT_BASEMAP_LEGENDE` (heute §38, §41). Ein Layout nutzt die Funktion,
wenn es die Layout-Eigenschaft `sn_basemap_legende = 1` trägt (`basemap_legende_layout.markiere_layout`);
`PrintLayout.create_single_page_layout(..., basemap_legende=True)` setzt sie und erzeugt die Legende vor dem
Öffnen des Designers. Neue Layouts: in die Konstante aufnehmen bzw. das Kennzeichen setzen.
- **Layout-Elemente nach ID:** `Hauptkarte` (Bezugskarte), `Legende` (Hauptlegende, Spalte und Oberkante),
`Quellenangabe` (Unterkante der Basemap-Legende), `Legende Basemap` (das Basemap-Element selbst).
Umbenennen dieser IDs bricht die Platzierung.
- **Weitere Layout-Eigenschaften:** `sn_basemap_bereich`, `sn_basemap_massstab`, `sn_basemap_eintraege` (JSON),
`sn_basemap_hilfslayer` (IDs der Hilfslayer, werden beim Aktualisieren ersetzt).
- **Hilfslayer:** leere Speicherlayer, nicht im Layerbaum, Eigenschaften `sn_basemap_legende = <Layoutname>` und
`skipMemoryLayersCheck = 1`. Code, der über `QgsProject.mapLayers()` iteriert, sieht sie.
- **Basemap-Layer-Erkennung:** Vektorkachel-Layer, Quelle enthält `gdz_basemapde_vektor`; Name „basemap.de“ nur
als Rückfall.
- **Legendenkatalog:** `sn_basis/data/basemap_legende.json` (Style-ID → Eintrag), erzeugt/aktualisiert mit
`python modules/basemap_katalog.py`; gehört zur Style-Version `bm_web_col` v2.
- **Stilvorlagen:** lokal unter `<QGIS-Profil>/sn_basis/basemap_stile/`, Standard in `QSettings`
`sn_basis/basemap_standardstil`.
- **Neue öffentliche Funktion:** `dialog_wrapper.ask_mehrfachauswahl(titel, hinweis, eintraege, vorauswahl)` –
Liste mit Häkchen, Gruppen, Suche; liefert gewählte Schlüssel oder `None` bei Abbruch.
## 12. Kartenrand im Standardlayout (Basis #93)
`print_kartenrand.ordne_kartenrand_an(layout)` läuft am Ende von `PrintLayout.create_single_page_layout` und findet
die Elemente **über ihre IDs**: `Hauptkarte`, `Legende`, `Legende Basemap`, `Quellenangabe`, `Kartenschild`,
`Kartenschild_Linie_1` … `_6` und die Schriftfeld-Beschriftungen (`Herausgeber` … `Aktenzeichen`, Liste
`UNTERE_ELEMENTE`). Wer im Layoutaufbau eine dieser IDs ändert oder ein neues Element unter die Legenden setzt,
muss die Liste anpassen. Die Seitengröße aus `print_logic` ist danach nur noch eine Mindestgröße: Breite und Höhe
können wachsen, die Karte bleibt bei 10 mm / 10 mm mit unveränderter Größe.