feature/Druck_tab #14
125
ui/layout.py
Normal file
125
ui/layout.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
sn_plan41/ui/layout.py – Aufbau von Drucklayouts für Plan41.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import Any
|
||||
|
||||
from sn_basis.functions.qt_wrapper import QFont
|
||||
from sn_basis.functions.qgiscore_wrapper import (
|
||||
QgsLayoutItem,
|
||||
QgsLayoutItemLabel,
|
||||
QgsLayoutItemMap,
|
||||
QgsLayoutPoint,
|
||||
QgsLayoutSize,
|
||||
QgsPrintLayout,
|
||||
QgsProject,
|
||||
QgsUnitTypes,
|
||||
)
|
||||
from sn_basis.functions.qgisui_wrapper import open_layout_designer
|
||||
|
||||
|
||||
MM = QgsUnitTypes.LayoutMillimeters
|
||||
|
||||
|
||||
class Layout:
|
||||
"""Erzeugt ein QGIS-Layout für den Druck."""
|
||||
|
||||
def __init__(self, project: Any | None = None) -> None:
|
||||
self.project = project or QgsProject.instance()
|
||||
|
||||
def create_single_page_layout(
|
||||
self,
|
||||
name: str,
|
||||
page_width_mm: float,
|
||||
page_height_mm: float,
|
||||
map_width_mm: float,
|
||||
map_height_mm: float,
|
||||
extent: Any,
|
||||
plotmassstab: float,
|
||||
thema: str = "",
|
||||
) -> Any:
|
||||
"""Erzeugt ein einseitiges Layout und öffnet es im Designer."""
|
||||
print(f"[Layout] create_single_page_layout: name='{name}', "
|
||||
f"page=({page_width_mm}x{page_height_mm}), map=({map_width_mm:.1f}x{map_height_mm:.1f}), "
|
||||
f"massstab={plotmassstab}, thema='{thema}'")
|
||||
layout_manager = self.project.layoutManager()
|
||||
print(f"[Layout] layoutManager: {layout_manager!r}")
|
||||
existing_layout = layout_manager.layoutByName(name)
|
||||
if existing_layout is not None:
|
||||
raise ValueError(f"Eine Vorlage mit der Bezeichnung '{name}' existiert bereits.")
|
||||
|
||||
layout = QgsPrintLayout(self.project)
|
||||
layout.initializeDefaults()
|
||||
layout.setName(name)
|
||||
print(f"[Layout] QgsPrintLayout erstellt: {layout!r}")
|
||||
|
||||
page = layout.pageCollection().page(0)
|
||||
page.setPageSize(QgsLayoutSize(page_width_mm, page_height_mm, MM))
|
||||
print(f"[Layout] Seitengröße gesetzt: {page_width_mm}x{page_height_mm} mm")
|
||||
|
||||
hauptkarte = QgsLayoutItemMap(layout)
|
||||
hauptkarte.setId("Hauptkarte")
|
||||
set_rect = getattr(hauptkarte, "setRect", None)
|
||||
if callable(set_rect):
|
||||
set_rect(0.0, 0.0, map_width_mm, map_height_mm)
|
||||
hauptkarte.attemptMove(QgsLayoutPoint(10.0, 10.0, MM))
|
||||
hauptkarte.attemptResize(QgsLayoutSize(map_width_mm, map_height_mm, MM))
|
||||
x_min = getattr(extent, "xMinimum", lambda: float("nan"))()
|
||||
y_min = getattr(extent, "yMinimum", lambda: float("nan"))()
|
||||
x_max = getattr(extent, "xMaximum", lambda: float("nan"))()
|
||||
y_max = getattr(extent, "yMaximum", lambda: float("nan"))()
|
||||
print(f"[Layout] Extent input: xmin={x_min}, ymin={y_min}, xmax={x_max}, ymax={y_max}")
|
||||
hauptkarte.setExtent(extent)
|
||||
if isinstance(plotmassstab, (int, float)) and math.isfinite(plotmassstab) and plotmassstab > 0:
|
||||
hauptkarte.setScale(plotmassstab)
|
||||
else:
|
||||
print(f"[Layout] WARN: ungültiger plotmassstab={plotmassstab!r}, setScale übersprungen")
|
||||
print(f"[Layout] Hauptkarte angelegt: pos=(10,10), size=({map_width_mm:.1f}x{map_height_mm:.1f})")
|
||||
|
||||
if thema and thema != "aktuell":
|
||||
follow_theme = getattr(hauptkarte, "setFollowVisibilityPreset", None)
|
||||
set_theme_name = getattr(hauptkarte, "setFollowVisibilityPresetName", None)
|
||||
if callable(follow_theme):
|
||||
follow_theme(True)
|
||||
if callable(set_theme_name):
|
||||
set_theme_name(thema)
|
||||
print(f"[Layout] Kartenthema gesetzt: '{thema}'")
|
||||
|
||||
layout.addLayoutItem(hauptkarte)
|
||||
print("[Layout] Hauptkarte zum Layout hinzugefügt")
|
||||
|
||||
quellenangabe = QgsLayoutItemLabel(layout)
|
||||
quellenangabe.setId("Quellenangabe")
|
||||
quellenangabe.setText(
|
||||
"Quelle Geobasisdaten: GeoSN, "
|
||||
"<a href=\"https://www.govdata.de/dl-de/by-2-0\">dl-de/by-2-0</a><br><br>"
|
||||
"Quelle Fachdaten: Darstellung auf der Grundlage von Daten und mit Erlaubnis des "
|
||||
"Sächsischen Landesamtes für Umwelt, Landwirtschaft und Geologie<br><br>"
|
||||
"Basemap:<br><br>"
|
||||
"© GeoBasis-DE / <a href=\"https://www.bkg.bund.de\">BKG</a> ([%year($now)%]) "
|
||||
"<a href=\"https://creativecommons.org/licences/by/4.0/\">CC BY 4.0</a> "
|
||||
"mit teilweise angepasster Signatur<br>"
|
||||
)
|
||||
set_mode = getattr(quellenangabe, "setMode", None)
|
||||
mode_html = getattr(QgsLayoutItemLabel, "ModeHtml", None)
|
||||
print(f"[Layout] QgsLayoutItemLabel.ModeHtml={mode_html!r}")
|
||||
if callable(set_mode) and mode_html is not None:
|
||||
set_mode(mode_html)
|
||||
quellenangabe.setFont(QFont("Arial", 12))
|
||||
set_reference_point = getattr(quellenangabe, "setReferencePoint", None)
|
||||
lower_left = getattr(getattr(QgsLayoutItem, "ReferencePoint", object), "LowerLeft", None)
|
||||
print(f"[Layout] QgsLayoutItem.ReferencePoint.LowerLeft={lower_left!r}")
|
||||
if callable(set_reference_point) and lower_left is not None:
|
||||
set_reference_point(lower_left)
|
||||
quellenangabe.attemptMove(QgsLayoutPoint(map_width_mm + 30.0, page_height_mm - 120.0, MM))
|
||||
quellenangabe.attemptResize(QgsLayoutSize(180.0, 100.0, MM))
|
||||
layout.addLayoutItem(quellenangabe)
|
||||
print("[Layout] Quellenangabe zum Layout hinzugefügt")
|
||||
|
||||
layout_manager.addLayout(layout)
|
||||
print("[Layout] Layout zum LayoutManager hinzugefügt")
|
||||
open_layout_designer(layout)
|
||||
print("[Layout] Layout Designer geöffnet")
|
||||
return layout
|
||||
@@ -4,9 +4,10 @@ sn_plan41/ui/tab_b_logic.py – Fachlogik für Tab B (Druck)
|
||||
from __future__ import annotations
|
||||
import math
|
||||
from sn_basis.functions.variable_wrapper import set_variable, get_variable
|
||||
from sn_basis.functions.qgiscore_wrapper import QgsProject, get_layer_extent
|
||||
from sn_basis.functions.qgiscore_wrapper import get_layer_extent
|
||||
from sn_basis.modules.Pruefmanager import Pruefmanager
|
||||
from sn_basis.modules.layerpruefer import Layerpruefer
|
||||
from sn_plan41.ui.layout import Layout
|
||||
|
||||
|
||||
KARTENNAME_VAR = "sn_kartenname"
|
||||
@@ -91,6 +92,7 @@ class TabBLogic:
|
||||
|
||||
def druckvorlage_anlegen(
|
||||
self,
|
||||
layer: object,
|
||||
kartenname_auswahl: str,
|
||||
massstab_auswahl: str,
|
||||
zielgroesse: str,
|
||||
@@ -100,6 +102,11 @@ class TabBLogic:
|
||||
|
||||
Prüft Parameter, berechnet Plotgröße und stellt bei Bedarf Atlas-Rückfrage.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
layer:
|
||||
Aktuell gewählter Verfahrensgebiet-Layer aus Tab A.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
@@ -108,14 +115,6 @@ class TabBLogic:
|
||||
``atlas_seiten`` (int): Anzahl benötigter Seiten (1 = kein Atlas).
|
||||
"""
|
||||
# ─── 1. Verfahrensgebiet-Layer prüfen ─────────────────────────────
|
||||
layer_id = get_variable("tab_a_layer_id", scope="project") or ""
|
||||
layer = None
|
||||
if layer_id:
|
||||
try:
|
||||
layer = QgsProject.instance().mapLayer(layer_id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
lp = Layerpruefer(layer=layer)
|
||||
ergebnis = lp.pruefe()
|
||||
if not ergebnis.ok:
|
||||
@@ -125,6 +124,7 @@ class TabBLogic:
|
||||
)
|
||||
return {"ok": False, "switch_to_tab_a": True, "atlas_seiten": 0}
|
||||
|
||||
layer_id = getattr(layer, "id", lambda: "")() or ""
|
||||
set_variable("sn_verfahrensgebietslayer", layer_id, scope="project")
|
||||
|
||||
# ─── 2. Kartenname prüfen ─────────────────────────────────────────
|
||||
@@ -173,13 +173,55 @@ class TabBLogic:
|
||||
|
||||
# ─── 6. Zielgröße bestimmen ───────────────────────────────────────
|
||||
din_dims = DIN_GROESSEN.get(zielgroesse, DIN_GROESSEN[DIN_STANDARD])
|
||||
if formfaktor: # Endlosrolle: X-Richtung auf 2000 mm begrenzt
|
||||
ziel_w, ziel_h = 2000.0, float(din_dims[1])
|
||||
if formfaktor: # Endlosrolle: X-Richtung entspricht der Plotgröße
|
||||
ziel_w, ziel_h = plotgroesse_w, float(min(din_dims))
|
||||
else:
|
||||
ziel_w, ziel_h = float(din_dims[0]), float(din_dims[1])
|
||||
|
||||
# ─── 7. Passt auf ein Blatt? ──────────────────────────────────────
|
||||
# ─── 7. Passt auf ein Blatt? -> Layout erzeugen ───────────────────
|
||||
print(f"[TabBLogic] plotgroesse=({plotgroesse_w:.1f}x{plotgroesse_h:.1f}), "
|
||||
f"zielgroesse=({ziel_w:.1f}x{ziel_h:.1f}), passt={plotgroesse_w <= ziel_w and plotgroesse_h <= ziel_h}")
|
||||
if plotgroesse_w <= ziel_w and plotgroesse_h <= ziel_h:
|
||||
kartenname = get_variable(KARTENNAME_VAR, scope="project") or KARTENNAME_BY_AUSWAHL.get(
|
||||
kartenname_auswahl, "Vorlage"
|
||||
)
|
||||
thema = get_variable(VIEW_VAR, scope="project") or ""
|
||||
print(f"[TabBLogic] frage_text aufrufen, default='{kartenname}', thema='{thema}'")
|
||||
vorlage_name, bestaetigt = self.pruefmanager.frage_text(
|
||||
"Neue Vorlage anlegen",
|
||||
"Bezeichnung der Vorlage:",
|
||||
default_text=kartenname,
|
||||
)
|
||||
print(f"[TabBLogic] frage_text Ergebnis: name='{vorlage_name}', bestaetigt={bestaetigt}")
|
||||
if not bestaetigt:
|
||||
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
|
||||
|
||||
vorlage_name = (vorlage_name or "").strip() or kartenname
|
||||
|
||||
print(f"[TabBLogic] Rufe Layout().create_single_page_layout auf: name='{vorlage_name}', "
|
||||
f"page=({ziel_w}x{ziel_h}), map=({kartenbild_w:.1f}x{kartenbild_h:.1f}), "
|
||||
f"massstab={massstab_zahl}, thema='{thema}'")
|
||||
try:
|
||||
Layout().create_single_page_layout(
|
||||
name=vorlage_name,
|
||||
page_width_mm=ziel_w,
|
||||
page_height_mm=ziel_h,
|
||||
map_width_mm=kartenbild_w,
|
||||
map_height_mm=kartenbild_h,
|
||||
extent=extent,
|
||||
plotmassstab=massstab_zahl,
|
||||
thema=thema,
|
||||
)
|
||||
print("[TabBLogic] create_single_page_layout erfolgreich abgeschlossen")
|
||||
except ValueError as exc:
|
||||
print(f"[TabBLogic] ValueError: {exc}")
|
||||
self.pruefmanager.zeige_hinweis("Vorlage anlegen", str(exc))
|
||||
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
|
||||
except Exception as exc:
|
||||
print(f"[TabBLogic] Exception: {exc!r}")
|
||||
self.pruefmanager.zeige_hinweis("Vorlage anlegen", f"Die Vorlage konnte nicht angelegt werden: {exc}")
|
||||
return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0}
|
||||
|
||||
return {"ok": True, "switch_to_tab_a": False, "atlas_seiten": 1}
|
||||
|
||||
# ─── 8. Atlas: Anzahl Seiten berechnen ────────────────────────────
|
||||
|
||||
@@ -243,31 +243,71 @@ class TabB(QWidget):
|
||||
|
||||
def _on_vorlage_erstellen(self) -> None:
|
||||
"""Startet die Pipeline Druckvorlage_anlegen."""
|
||||
print("[TabB] _on_vorlage_erstellen aufgerufen")
|
||||
if not self.logic or not self.pruefmanager:
|
||||
print(f"[TabB] Abbruch: logic={self.logic}, pruefmanager={self.pruefmanager}")
|
||||
return
|
||||
|
||||
# Layer direkt aus Tab A lesen (unabhängig von Projektvariable)
|
||||
layer = None
|
||||
tab_a = self._get_tab_a_widget()
|
||||
print(f"[TabB] tab_a Widget: {tab_a!r}")
|
||||
if tab_a is not None:
|
||||
layer_combo = getattr(tab_a, "layer_combo", None)
|
||||
print(f"[TabB] layer_combo: {layer_combo!r}")
|
||||
if layer_combo is not None:
|
||||
layer = layer_combo.currentLayer()
|
||||
print(f"[TabB] layer: {layer!r} (Name: {getattr(layer, 'name', lambda: '–')()})")
|
||||
|
||||
print(f"[TabB] Rufe druckvorlage_anlegen auf mit layer={layer!r}, "
|
||||
f"kartenname={self._kartenname_combo.currentText() if self._kartenname_combo else '?'}, "
|
||||
f"massstab={self._massstab_combo.currentText() if self._massstab_combo else '?'}, "
|
||||
f"zielgroesse={self._zielgroesse_combo.currentText() if self._zielgroesse_combo else '?'}, "
|
||||
f"formfaktor={self._endlosrolle_cb.isChecked() if self._endlosrolle_cb else '?'}")
|
||||
|
||||
result = self.logic.druckvorlage_anlegen(
|
||||
layer=layer,
|
||||
kartenname_auswahl=self._kartenname_combo.currentText() if self._kartenname_combo else "",
|
||||
massstab_auswahl=self._massstab_combo.currentText() if self._massstab_combo else "",
|
||||
zielgroesse=self._zielgroesse_combo.currentText() if self._zielgroesse_combo else DIN_STANDARD,
|
||||
formfaktor=self._endlosrolle_cb.isChecked() if self._endlosrolle_cb else False,
|
||||
)
|
||||
|
||||
print(f"[TabB] druckvorlage_anlegen Ergebnis: {result}")
|
||||
if result.get("switch_to_tab_a"):
|
||||
self._aktiviere_tab_a()
|
||||
|
||||
def _aktiviere_tab_a(self) -> None:
|
||||
"""Wechselt zum Tab A im übergeordneten TabWidget."""
|
||||
def _get_tab_widget(self):
|
||||
"""Findet das übergeordnete QTabWidget anhand des ``tabBar``-Attributs."""
|
||||
try:
|
||||
widget = self.parent()
|
||||
while widget is not None:
|
||||
if hasattr(widget, "setCurrentIndex") and hasattr(widget, "count"):
|
||||
widget.setCurrentIndex(0)
|
||||
return
|
||||
if hasattr(widget, "tabBar") and hasattr(widget, "setCurrentIndex"):
|
||||
return widget
|
||||
parent_fn = getattr(widget, "parent", None)
|
||||
widget = parent_fn() if callable(parent_fn) else None
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _get_tab_a_widget(self):
|
||||
"""Gibt die Tab-A-Widget-Instanz zurück (Index 0 im übergeordneten QTabWidget)."""
|
||||
tab_widget = self._get_tab_widget()
|
||||
if tab_widget is None:
|
||||
return None
|
||||
try:
|
||||
return tab_widget.widget(0)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _aktiviere_tab_a(self) -> None:
|
||||
"""Wechselt den aktiven Reiter auf Tab A im übergeordneten QTabWidget."""
|
||||
tab_widget = self._get_tab_widget()
|
||||
if tab_widget is not None:
|
||||
try:
|
||||
tab_widget.setCurrentIndex(0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _connect_project_signals(self) -> None:
|
||||
"""Verbindet QgsProject-Signale für Projektwechsel/-neuladen."""
|
||||
|
||||
Reference in New Issue
Block a user