Files
Plugin_SN_Plan41/ui/layout.py
T

736 lines
28 KiB
Python
Raw Normal View History

"""
sn_plan41/ui/layout.py – Aufbau von Drucklayouts für Plan41.
"""
from __future__ import annotations
import math
2026-03-23 14:25:14 +01:00
import importlib
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."""
2026-03-23 10:48:32 +01:00
_TEXT_FORMAT_TEMPLATES = {
"Format_1": {
"font_family": "New Helvetica roman",
"font_size": 10,
2026-03-23 10:48:32 +01:00
"font_style": "normal",
},
"Format_2": {
"font_family": "New Helvetica roman",
"font_size": 14,
2026-03-23 10:48:32 +01:00
"font_style": "fett",
},
"Format_3": {
"font_family": "New Helvetica roman",
2026-03-23 10:48:32 +01:00
"font_size": 10,
"font_style": "normal",
},
}
2026-03-23 10:43:37 +01:00
_OBJECT_TEMPLATES = {
"hauptkarte": {
"id": "Hauptkarte",
2026-03-23 14:25:14 +01:00
"object_name": "Hauptkarte",
2026-03-23 10:43:37 +01:00
},
"quellenangabe": {
"id": "Quellenangabe",
2026-03-23 14:25:14 +01:00
"object_name": "Quellenangabe",
2026-03-23 10:43:37 +01:00
"size_mm": (180.0, 100.0),
2026-03-23 10:48:32 +01:00
"format": "Format_1",
2026-03-23 10:43:37 +01:00
},
"seitenzahl": {
"id": "Seitenzahl",
2026-03-23 14:25:14 +01:00
"object_name": "Seitenzahl",
2026-03-23 10:43:37 +01:00
"size_mm": (60.0, 8.0),
2026-03-23 10:48:32 +01:00
"format": "Format_1",
2026-03-23 10:43:37 +01:00
},
"textfeld": {
"id": "Textfeld",
2026-03-23 14:25:14 +01:00
"object_name": "Textfeld",
"size_mm": (60.0, 8.0),
"format": "Format_1",
"html_mode": True,
"text": "",
},
2026-03-23 10:43:37 +01:00
}
_QUELLENANGABE_TEXT = (
"Quelle Geobasisdaten: GeoSN, "
"<a href=\"https://www.govdata.de/dl-de/by-2-0\">dl-de/by-2-0</a><br>"
2026-03-23 10:43:37 +01:00
"Quelle Fachdaten: Darstellung auf der Grundlage von Daten und mit Erlaubnis des "
"Sächsischen Landesamtes für Umwelt, Landwirtschaft und Geologie<br>"
"Basemap:"
2026-03-23 10:43:37 +01:00
"© 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>"
)
_SEITENZAHL_TEXT = "Seite [% attribute(@atlas_feature, 'Seitenzahl') %] von [% @atlas_totalfeatures %]"
def __init__(self, project: Any | None = None) -> None:
self.project = project or QgsProject.instance()
self._current_layout: Any | None = None
2026-03-23 10:43:37 +01:00
def _create_map_item(
self,
layout: Any,
template_name: str,
x_mm: float,
y_mm: float,
width_mm: float,
height_mm: float,
2026-03-23 14:25:14 +01:00
object_name: str | None = None,
2026-03-23 10:43:37 +01:00
) -> Any:
template = self._OBJECT_TEMPLATES[template_name]
map_item = QgsLayoutItemMap(layout)
map_item.setId(template["id"])
2026-03-23 14:25:14 +01:00
set_object_name = getattr(map_item, "setObjectName", None)
if callable(set_object_name):
set_object_name(object_name or template.get("object_name", template["id"]))
2026-03-23 10:43:37 +01:00
layout.addLayoutItem(map_item)
map_item.attemptMove(QgsLayoutPoint(x_mm, y_mm, MM))
map_item.attemptResize(QgsLayoutSize(width_mm, height_mm, MM))
return map_item
2026-03-23 14:25:14 +01:00
def _set_item_frame_stroke_width(self, item: Any, stroke_width_mm: float) -> None:
set_frame_stroke_width = getattr(item, "setFrameStrokeWidth", None)
if not callable(set_frame_stroke_width):
return
try:
set_frame_stroke_width(stroke_width_mm)
return
except Exception:
pass
try:
qgis_core = importlib.import_module("qgis.core")
QgsLayoutMeasurement = getattr(qgis_core, "QgsLayoutMeasurement", None)
except Exception:
QgsLayoutMeasurement = None
if QgsLayoutMeasurement is not None:
try:
set_frame_stroke_width(QgsLayoutMeasurement(stroke_width_mm, MM))
return
except Exception:
pass
frame_stroke_width = getattr(item, "frameStrokeWidth", None)
if callable(frame_stroke_width):
try:
measurement = frame_stroke_width()
set_length = getattr(measurement, "setLength", None)
if callable(set_length):
set_length(stroke_width_mm)
set_frame_stroke_width(measurement)
except Exception:
pass
def add_text_label(
self,
text: str,
x_mm: float,
y_mm: float,
template_name: str = "textfeld",
text_format: str | None = None,
width_mm: float | None = None,
height_mm: float | None = None,
html_mode: bool = False,
expression_enabled: bool = False,
2026-03-23 14:25:14 +01:00
object_name: str | None = None,
) -> Any:
"""Einfache öffentliche Methode zum Hinzufügen eines Text-Labels.
Beispiel: add_text_label("Ausgabedatum: 2026-03-23", 100, 200, text_format="Format_1")
"""
if self._current_layout is None:
raise RuntimeError("Kein Layout aktiv. Bitte create_single_page_layout() oder create_atlas_layout() aufrufen.")
return self._create_label_item(
self._current_layout,
template_name,
text,
x_mm,
y_mm,
text_format=text_format,
width_mm=width_mm,
height_mm=height_mm,
html_mode=html_mode,
expression_enabled=expression_enabled,
2026-03-23 14:25:14 +01:00
object_name=object_name,
)
def _setup_layout_objects(self, kartenfenster_untere_rechte_ecke_mm: tuple, is_atlas: bool = False) -> None:
"""Zentrale Methode zum Hinzufügen aller Standard-Objekte ins Layout.
Diese Methode wird sowohl von create_single_page_layout() als auch von create_atlas_layout() aufgerufen.
2026-03-23 14:25:14 +01:00
Relatipunkt des Objekts ist die untere linke Ecke, berechnet von der unteren, rechten Ecke des Kartefensters
Args:
kartenfenster_untere_rechte_ecke_mm: Tupel (x_mm, y_mm) der unteren rechten Kartenecke
is_atlas: True für Atlas-Layout, False für Einzelblatt-Layout
"""
if self._current_layout is None:
raise RuntimeError("Kein Layout aktiv.")
mode_html = getattr(QgsLayoutItemLabel, "ModeHtml", None)
print(f"[Layout] QgsLayoutItemLabel.ModeHtml={mode_html!r}")
lower_left = getattr(getattr(QgsLayoutItem, "ReferencePoint", object), "LowerLeft", None)
print(f"[Layout] QgsLayoutItem.ReferencePoint.LowerLeft={lower_left!r}")
# Quellenangabe: unterschiedliche Positionen für Einzelblatt vs. Atlas
if is_atlas:
quellenangabe_x = kartenfenster_untere_rechte_ecke_mm[0] + 5.0
quellenangabe_y = kartenfenster_untere_rechte_ecke_mm[1]
else:
quellenangabe_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
quellenangabe_y = kartenfenster_untere_rechte_ecke_mm[1] - 120.0
self._create_label_item(
self._current_layout,
"quellenangabe",
self._QUELLENANGABE_TEXT,
quellenangabe_x,
quellenangabe_y,
html_mode=True,
)
print("[Layout] Quellenangabe zum Layout hinzugefügt")
2026-03-23 14:25:14 +01:00
# Kartenschild-Box, -Linien und -Text werden hier ergänzt (TODO)
#Kartenschild-Box
kartenschild_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
kartenschild_y = kartenfenster_untere_rechte_ecke_mm[1]
self._create_rectangle_box_item(
self._current_layout,
"Kartenschild",
kartenschild_x,
kartenschild_y,
180.0,
92.0,
stroke_width_mm= 1.0,
)
print("[Layout] Kartenschild-Box zum Layout hinzugefügt")
2026-03-23 14:25:14 +01:00
#Kartenschild-Linien
kartenschild_line_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0
kartenschild_line_offsets_y = (-10.0, -20.0, -30.0, -46.0, -62.0, -82.0)
for index, offset_y in enumerate(kartenschild_line_offsets_y, start=1):
self._create_rectangle_box_item(
self._current_layout,
f"Kartenschild_Linie_{index}",
kartenschild_line_x,
kartenfenster_untere_rechte_ecke_mm[1] + offset_y,
180.0,
0.0,
stroke_width_mm=0.1,
)
print("[Layout] 6 Kartenschild-Linien zum Layout hinzugefügt")
#Kartenschild-Text
#Herausgeber
self.add_text_label("@Amt", 10.0, -92.0, text_format="Format_1", object_name="Herausgeber")
#Bezeichnung_Flurbereinigung
#Name Flurbereinigung
#Stadt_Gemeinde
#Landkreis
#Kartenname
#Ergänzung(Seitenzahl)
#gefertigt
#geprüft
#Maßstab
#Ausgabedatum
#Aktenzeichen
# Seitenzahl nur bei Atlas-Layout
if is_atlas:
seitenzahl_x = kartenfenster_untere_rechte_ecke_mm[0] + 5.0
seitenzahl_y = kartenfenster_untere_rechte_ecke_mm[1] - 2.0
self._create_label_item(
self._current_layout,
"seitenzahl",
self._SEITENZAHL_TEXT,
seitenzahl_x,
seitenzahl_y,
expression_enabled=True,
)
print("[Layout] Seitenzahl zum Layout hinzugefügt")
2026-03-23 10:43:37 +01:00
def _create_label_item(
self,
layout: Any,
template_name: str,
text: str,
x_mm: float,
y_mm: float,
*,
2026-03-23 10:48:32 +01:00
text_format: str | None = None,
2026-03-23 10:43:37 +01:00
font_name: str | None = None,
font_size: int | None = None,
2026-03-23 10:48:32 +01:00
font_style: str | None = None,
2026-03-23 10:43:37 +01:00
width_mm: float | None = None,
height_mm: float | None = None,
html_mode: bool = False,
expression_enabled: bool = False,
2026-03-23 14:25:14 +01:00
object_name: str | None = None,
2026-03-23 10:43:37 +01:00
) -> Any:
template = self._OBJECT_TEMPLATES[template_name]
label = QgsLayoutItemLabel(layout)
label.setId(template["id"])
2026-03-23 14:25:14 +01:00
set_object_name = getattr(label, "setObjectName", None)
if callable(set_object_name):
set_object_name(object_name or template.get("object_name", template["id"]))
2026-03-23 10:43:37 +01:00
label.setText(text)
if html_mode:
set_mode = getattr(label, "setMode", None)
mode_html = getattr(QgsLayoutItemLabel, "ModeHtml", None)
if callable(set_mode) and mode_html is not None:
set_mode(mode_html)
if expression_enabled:
set_expr_enabled = getattr(label, "setExpressionEnabled", None)
if callable(set_expr_enabled):
try:
set_expr_enabled(True)
except Exception:
pass
2026-03-23 10:48:32 +01:00
format_name = text_format or template.get("format", "Format_1")
format_template = self._TEXT_FORMAT_TEMPLATES.get(format_name, self._TEXT_FORMAT_TEMPLATES["Format_1"])
resolved_font_name = font_name or format_template["font_family"]
resolved_font_size = font_size or format_template["font_size"]
resolved_font_style = font_style or format_template["font_style"]
font = QFont(resolved_font_name, resolved_font_size)
if hasattr(font, "setBold"):
font.setBold(resolved_font_style == "fett")
label.setFont(font)
2026-03-23 10:43:37 +01:00
set_reference_point = getattr(label, "setReferencePoint", None)
lower_left = getattr(getattr(QgsLayoutItem, "ReferencePoint", object), "LowerLeft", None)
if callable(set_reference_point) and lower_left is not None:
set_reference_point(lower_left)
template_width, template_height = template.get("size_mm", (0.0, 0.0))
label.attemptMove(QgsLayoutPoint(x_mm, y_mm, MM))
label.attemptResize(
QgsLayoutSize(
width_mm if width_mm is not None else template_width,
height_mm if height_mm is not None else template_height,
MM,
)
)
layout.addLayoutItem(label)
return label
2026-03-23 14:25:14 +01:00
def _create_rectangle_box_item(
self,
layout: Any,
object_name: str,
x_mm: float,
y_mm: float,
width_mm: float,
height_mm: float,
stroke_width_mm: float = 0.3,
) -> Any:
box = QgsLayoutItemLabel(layout)
box.setId(object_name)
set_object_name = getattr(box, "setObjectName", None)
if callable(set_object_name):
set_object_name(object_name)
box.setText("")
set_reference_point = getattr(box, "setReferencePoint", None)
lower_left = getattr(getattr(QgsLayoutItem, "ReferencePoint", object), "LowerLeft", None)
if callable(set_reference_point) and lower_left is not None:
set_reference_point(lower_left)
box.attemptMove(QgsLayoutPoint(x_mm, y_mm, MM))
box.attemptResize(QgsLayoutSize(width_mm, height_mm, MM))
set_frame_enabled = getattr(box, "setFrameEnabled", None)
if callable(set_frame_enabled):
try:
set_frame_enabled(True)
except Exception:
pass
self._set_item_frame_stroke_width(box, stroke_width_mm)
set_background_enabled = getattr(box, "setBackgroundEnabled", None)
if callable(set_background_enabled):
try:
set_background_enabled(False)
except Exception:
pass
layout.addLayoutItem(box)
return box
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)
self._current_layout = layout
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")
map_left_mm = 10.0
map_top_mm = 10.0
map_right_mm = map_left_mm + map_width_mm
map_bottom_mm = map_top_mm + map_height_mm
kartenfenster_untere_rechte_ecke_mm = (map_right_mm, map_bottom_mm)
print(
f"[Layout] Kartenbild-Kanten: rechts={map_right_mm:.1f} mm, unten={map_bottom_mm:.1f} mm"
)
2026-03-23 10:43:37 +01:00
hauptkarte = self._create_map_item(
layout,
"hauptkarte",
map_left_mm,
map_top_mm,
map_width_mm,
map_height_mm,
)
print(f"[Layout] QgsLayoutItemMap erstellt")
print("[Layout] Hauptkarte zum Layout hinzugefügt")
print(f"[Layout] Position und Größe gesetzt: pos=({map_left_mm}, {map_top_mm}), size=({map_width_mm:.1f}x{map_height_mm:.1f})")
# Extent und Maßstab setzen
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}")
if extent is not None and hasattr(extent, "isNull") and callable(extent.isNull) and not extent.isNull():
try:
hauptkarte.setExtent(extent)
print(f"[Layout] setExtent() erfolgreich")
except Exception as exc:
print(f"[Layout] WARN: setExtent() fehlgeschlagen: {exc}")
else:
print(f"[Layout] WARN: Extent nicht gültig/callable, setExtent übersprungen")
# Maßstab setzen (NACH Extent)
if isinstance(plotmassstab, (int, float)) and math.isfinite(plotmassstab) and plotmassstab > 0:
try:
hauptkarte.setScale(plotmassstab)
print(f"[Layout] setScale({plotmassstab}) erfolgreich")
except Exception as exc:
print(f"[Layout] WARN: setScale({plotmassstab}) fehlgeschlagen: {exc}")
else:
print(f"[Layout] WARN: ungültiger plotmassstab={plotmassstab!r}, setScale übersprungen")
# Rahmen aktivieren
set_frame_enabled = getattr(hauptkarte, "setFrameEnabled", None)
if callable(set_frame_enabled):
try:
set_frame_enabled(True)
print(f"[Layout] Rahmen aktiviert")
except Exception as exc:
print(f"[Layout] WARN: setFrameEnabled fehlgeschlagen: {exc}")
set_frame_stroke_width = getattr(hauptkarte, "setFrameStrokeWidth", None)
if callable(set_frame_stroke_width):
try:
2026-03-23 14:25:14 +01:00
self._set_item_frame_stroke_width(hauptkarte, 0.5)
print(f"[Layout] Rahmenstrichbreite auf 0.5 mm gesetzt")
except Exception as exc:
print(f"[Layout] WARN: setFrameStrokeWidth(0.5) fehlgeschlagen: {exc}")
# Kartenthema setzen
if thema and thema != "aktuell":
follow_theme = getattr(hauptkarte, "setFollowVisibilityPreset", None)
set_theme_name = getattr(hauptkarte, "setFollowVisibilityPresetName", None)
if callable(follow_theme):
try:
follow_theme(True)
print(f"[Layout] setFollowVisibilityPreset(True)")
except Exception as exc:
print(f"[Layout] WARN: setFollowVisibilityPreset fehlgeschlagen: {exc}")
if callable(set_theme_name):
try:
set_theme_name(thema)
print(f"[Layout] Kartenthema auf '{thema}' gesetzt")
except Exception as exc:
print(f"[Layout] WARN: setFollowVisibilityPresetName('{thema}') fehlgeschlagen: {exc}")
else:
print(f"[Layout] Kartenthema nicht gesetzt (thema='{thema}')")
# Erst nach allen Properties: LayerSet einfrieren für Export
set_keep_layer_set = getattr(hauptkarte, "setKeepLayerSet", None)
if callable(set_keep_layer_set):
try:
set_keep_layer_set(True)
print("[Layout] setKeepLayerSet(True) – Layerset für Export erhalten")
except Exception as exc:
print(f"[Layout] WARN: setKeepLayerSet fehlgeschlagen: {exc}")
# Refresh-Strategie (optional, nur wenn vorhanden)
set_refresh_strategy = getattr(hauptkarte, "setRefreshStrategy", None)
if callable(set_refresh_strategy):
refresh_cache = getattr(hauptkarte, "RefreshLaterOnly", None) or getattr(hauptkarte, "RefreshWhenRequested", None) or 0
if refresh_cache is not None:
try:
set_refresh_strategy(refresh_cache)
print(f"[Layout] setRefreshStrategy({refresh_cache}) gesetzt")
except Exception as exc:
print(f"[Layout] WARN: setRefreshStrategy fehlgeschlagen: {exc}")
print(f"[Layout] Hauptkarte vollständig konfiguriert")
self._setup_layout_objects(kartenfenster_untere_rechte_ecke_mm, is_atlas=False)
layout_manager.addLayout(layout)
print("[Layout] Layout zum LayoutManager hinzugefügt")
self._current_layout = None
open_layout_designer(layout)
print("[Layout] Layout Designer geöffnet")
return layout
def create_atlas_layout(
self,
name: str,
page_width_mm: float,
page_height_mm: float,
map_width_mm: float,
map_height_mm: float,
extent: Any,
plotmassstab: float,
atlas_layer: Any,
thema: str = "",
) -> Any:
"""Erzeugt ein Atlas-Layout mit Coverage-Layer ``Atlasobjekte``."""
layout_manager = self.project.layoutManager()
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)
self._current_layout = layout
page = layout.pageCollection().page(0)
page.setPageSize(QgsLayoutSize(page_width_mm, page_height_mm, MM))
# Verifiziere, dass QGIS die Größe akzeptiert hat
page_size = page.pageSize() if hasattr(page, 'pageSize') else None
if page_size is not None and hasattr(page_size, 'width'):
actual_w = float(page_size.width())
actual_h = float(page_size.height())
print(f"[Layout] Atlas Page gesetzt: x=0, y=0, width={page_width_mm:.1f}mm→{actual_w:.1f}mm, height={page_height_mm:.1f}mm→{actual_h:.1f}mm")
else:
print(f"[Layout] Atlas Page: x=0, y=0, width={page_width_mm:.1f}mm, height={page_height_mm:.1f}mm")
map_left_mm = 10.0
map_top_mm = 10.0
map_right_mm = map_left_mm + map_width_mm
map_bottom_mm = map_top_mm + map_height_mm
kartenfenster_untere_rechte_ecke_mm = (map_right_mm, map_bottom_mm)
2026-03-23 10:43:37 +01:00
hauptkarte = self._create_map_item(
layout,
"hauptkarte",
map_left_mm,
map_top_mm,
map_width_mm,
map_height_mm,
)
# Verifiziere mit Units-bewussten Methoden (rect() kann andere Units verwenden).
actual_w = None
actual_h = None
actual_x = None
actual_y = None
# Versuche zuerst, die Größe mit Unit-Methoden zu lesen
try:
if hasattr(hauptkarte, 'sizeWithUnits'):
size_item = hauptkarte.sizeWithUnits()
if hasattr(size_item, 'width') and hasattr(size_item, 'height'):
actual_w = float(size_item.width())
actual_h = float(size_item.height())
except Exception:
pass
try:
if hasattr(hauptkarte, 'positionWithUnits'):
pos_item = hauptkarte.positionWithUnits()
if hasattr(pos_item, 'x') and hasattr(pos_item, 'y'):
actual_x = float(pos_item.x())
actual_y = float(pos_item.y())
except Exception:
pass
# Fallback: nutze rect() und teile durch UnitFaktor, falls nötig
if actual_w is None or actual_h is None:
try:
actual_rect = hauptkarte.rect()
if actual_rect is not None:
actual_w = float(actual_rect.width())
actual_h = float(actual_rect.height())
actual_x = float(actual_rect.x())
actual_y = float(actual_rect.y())
except Exception:
pass
if actual_w is not None and actual_h is not None:
print(f"[Layout] Atlas Hauptkarte gesetzt: x={map_left_mm:.1f}mm→{actual_x:.1f}mm, y={map_top_mm:.1f}mm→{actual_y:.1f}mm, width={map_width_mm:.1f}mm→{actual_w:.1f}mm, height={map_height_mm:.1f}mm→{actual_h:.1f}mm")
else:
print(f"[Layout] Atlas Hauptkarte: x={map_left_mm:.1f}mm, y={map_top_mm:.1f}mm, width={map_width_mm:.1f}mm, height={map_height_mm:.1f}mm")
if extent is not None and hasattr(extent, "isNull") and callable(extent.isNull) and not extent.isNull():
try:
hauptkarte.setExtent(extent)
except Exception:
pass
if isinstance(plotmassstab, (int, float)) and math.isfinite(plotmassstab) and plotmassstab > 0:
try:
hauptkarte.setScale(plotmassstab)
except Exception:
pass
set_frame_enabled = getattr(hauptkarte, "setFrameEnabled", None)
if callable(set_frame_enabled):
try:
set_frame_enabled(True)
except Exception:
pass
set_frame_stroke_width = getattr(hauptkarte, "setFrameStrokeWidth", None)
if callable(set_frame_stroke_width):
try:
2026-03-23 14:25:14 +01:00
self._set_item_frame_stroke_width(hauptkarte, 0.5)
except Exception:
pass
if thema and thema != "aktuell":
follow_theme = getattr(hauptkarte, "setFollowVisibilityPreset", None)
set_theme_name = getattr(hauptkarte, "setFollowVisibilityPresetName", None)
if callable(follow_theme):
try:
follow_theme(True)
except Exception:
pass
if callable(set_theme_name):
try:
set_theme_name(thema)
except Exception:
pass
set_atlas_driven = getattr(hauptkarte, "setAtlasDriven", None)
if callable(set_atlas_driven):
try:
set_atlas_driven(True)
except Exception:
pass
# Fester Atlas-Maßstab: plotmassstab bleibt unverändert.
set_scaling_mode = getattr(hauptkarte, "setAtlasScalingMode", None)
if callable(set_scaling_mode):
fixed_mode = getattr(QgsLayoutItemMap, "Fixed", None)
if fixed_mode is not None:
try:
set_scaling_mode(fixed_mode)
except Exception:
pass
set_atlas_margin = getattr(hauptkarte, "setAtlasMargin", None)
if callable(set_atlas_margin):
try:
set_atlas_margin(0.0)
except Exception:
pass
set_keep_layer_set = getattr(hauptkarte, "setKeepLayerSet", None)
if callable(set_keep_layer_set):
try:
set_keep_layer_set(True)
except Exception:
pass
# Sicherheit: Größe nochmal nach Atlas-Konfiguration setzen, um sicherzustellen, dass sie nicht von der Atlas-Einstellung überschrieben wurde.
hauptkarte.attemptResize(QgsLayoutSize(map_width_mm, map_height_mm, MM))
print(f"[Layout] Atlas Hauptkarte Größe nach Atlas-Konfiguration erneut gesetzt: {map_width_mm:.1f}mm × {map_height_mm:.1f}mm")
self._setup_layout_objects(kartenfenster_untere_rechte_ecke_mm, is_atlas=True)
atlas = layout.atlas()
if atlas is not None:
set_enabled = getattr(atlas, "setEnabled", None)
set_coverage = getattr(atlas, "setCoverageLayer", None)
set_hide_coverage = getattr(atlas, "setHideCoverage", None)
set_filter_features = getattr(atlas, "setFilterFeatures", None)
set_page_name = getattr(atlas, "setPageNameExpression", None)
if callable(set_enabled):
set_enabled(True)
if callable(set_coverage):
set_coverage(atlas_layer)
if callable(set_hide_coverage):
set_hide_coverage(True)
if callable(set_filter_features):
set_filter_features(False)
if callable(set_page_name):
set_page_name("attribute(@atlas_feature, 'Seitenzahl')")
layout_manager.addLayout(layout)
print("[Layout] Layout zum LayoutManager hinzugefügt")
self._current_layout = None
open_layout_designer(layout)
print("[Layout] Layout Designer geöffnet")
return layout