""" sn_basis/modules/print_layout.py - Aufbau von Einzelblatt-Drucklayouts. """ from __future__ import annotations import importlib import math from typing import Any from sn_basis.functions.qt_wrapper import QFont, Qt from sn_basis.functions.qgiscore_wrapper import ( MAP_GRID_ANNOTATION_DISPLAY_HIDE_ALL, MAP_GRID_ANNOTATION_DISPLAY_SHOW_X, MAP_GRID_ANNOTATION_DISPLAY_SHOW_Y, MAP_GRID_ANNOTATION_FORMAT_DECIMAL, MAP_GRID_ANNOTATION_POSITION_INSIDE_FRAME, MAP_GRID_BORDER_BOTTOM, MAP_GRID_BORDER_LEFT, MAP_GRID_BORDER_RIGHT, MAP_GRID_BORDER_TOP, MAP_GRID_FRAME_STYLE_INTERIOR_TICKS, MAP_GRID_STYLE_FRAME_ANNOTATIONS, MAP_GRID_UNIT_MAP_UNIT, TEXT_BACKGROUND_SHAPE_RECTANGLE, TEXT_BACKGROUND_SIZE_BUFFER, TEXT_RENDER_UNIT_MILLIMETERS, TEXT_RENDER_UNIT_POINTS, QgsLayoutItem, QgsLayoutItemLabel, QgsLayoutItemLegend, QgsLayoutItemMap, QgsLayoutItemMapGrid, QgsLayoutPoint, QgsLayoutSize, QgsPrintLayout, QgsProject, QgsTextBackgroundSettings, QgsTextFormat, QgsUnitTypes, ) from sn_basis.functions.qgisui_wrapper import open_layout_designer MM = QgsUnitTypes.LayoutMillimeters class PrintLayout: """Erzeugt ein QGIS-Einzelblatt-Layout fuer den Druck.""" _FRAME_STROKE_WIDTH_MM = 0.5 _LEGEND_WIDTH_MM = 180.0 _GRID_INTERVAL_MAP_UNITS = 500.0 _TEXT_FORMAT_TEMPLATES = { "Format_1": {"font_family": "Arial", "font_size": 11, "font_style": "normal"}, "Format_2": {"font_family": "Arial", "font_size": 13, "font_style": "bold"}, "Format_3": {"font_family": "Arial", "font_size": 16, "font_style": "bold"}, } _OBJECT_TEMPLATES = { "hauptkarte": {"id": "Hauptkarte", "object_name": "Hauptkarte"}, "quellenangabe": { "id": "Quellenangabe", "object_name": "Quellenangabe", "size_mm": (180.0, 100.0), "format": "Format_1", }, "textfeld": { "id": "Textfeld", "object_name": "Textfeld", "size_mm": (60.0, 8.0), "format": "Format_1", "html_mode": True, "text": "", }, "legende": { "id": "Legende", "object_name": "Legende", "size_mm": (180.0, 60.0), }, } _QUELLENANGABE_TEXT = ( "Quelle Geobasisdaten: GeoSN, " "dl-de/by-2-0
" "Quelle Fachdaten: Darstellung auf der Grundlage von Daten und mit Erlaubnis des " "Sächsischen Landesamtes für Umwelt, Landwirtschaft und Geologie
" "Basemap:" "© GeoBasis-DE / BKG ([%year($now)%]) " "CC BY 4.0 " "mit teilweise angepasster Signatur
" ) def __init__(self, project: Any | None = None) -> None: self.project = project or QgsProject.instance() self._current_layout: Any | None = None def _create_map_item( self, layout: Any, template_name: str, x_mm: float, y_mm: float, width_mm: float, height_mm: float, object_name: str | None = None, ) -> Any: template = self._OBJECT_TEMPLATES[template_name] map_item = QgsLayoutItemMap(layout) map_item.setId(template["id"]) 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"])) 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 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 _fit_extent_to_map_item(self, extent: Any, map_width_mm: float, map_height_mm: float) -> Any: if extent is None: return extent if map_width_mm <= 0 or map_height_mm <= 0: return extent try: extent_width = float(extent.width()) extent_height = float(extent.height()) except Exception: return extent if extent_width <= 0 or extent_height <= 0: return extent target_ratio = map_width_mm / map_height_mm current_ratio = extent_width / extent_height if not math.isfinite(target_ratio) or not math.isfinite(current_ratio) or target_ratio <= 0: return extent try: center_x = (float(extent.xMinimum()) + float(extent.xMaximum())) / 2.0 center_y = (float(extent.yMinimum()) + float(extent.yMaximum())) / 2.0 except Exception: return extent fitted_width = extent_width fitted_height = extent_height if not math.isclose(current_ratio, target_ratio, rel_tol=1e-9, abs_tol=1e-9): if current_ratio > target_ratio: fitted_height = extent_width / target_ratio else: fitted_width = extent_height * target_ratio safety_factor = 1.002 half_width = (fitted_width * safety_factor) / 2.0 half_height = (fitted_height * safety_factor) / 2.0 try: qgis_core = importlib.import_module("qgis.core") qgs_rectangle = getattr(qgis_core, "QgsRectangle", None) except Exception: qgs_rectangle = None if qgs_rectangle is None: return extent try: return qgs_rectangle( center_x - half_width, center_y - half_height, center_x + half_width, center_y + half_height, ) except Exception: return extent 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, object_name: str | None = None, v_align: str = "top", ) -> Any: if self._current_layout is None: raise RuntimeError("Kein Layout aktiv. Bitte create_single_page_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, object_name=object_name, v_align=v_align, ) def _setup_layout_objects( self, kartenfenster_untere_rechte_ecke_mm: tuple, hauptkarte: Any, map_top_mm: float, map_height_mm: float, kartenname_auswahl: str = "", ) -> None: if self._current_layout is None: raise RuntimeError("Kein Layout aktiv.") def _coords_rel_untere_rechte_ecke(rel_x_mm: float, rel_y_mm: float) -> tuple[float, float]: return ( kartenfenster_untere_rechte_ecke_mm[0] + rel_x_mm, kartenfenster_untere_rechte_ecke_mm[1] + rel_y_mm, ) legende_x = kartenfenster_untere_rechte_ecke_mm[0] + 10.0 legende_height_mm = max(map_height_mm - 122.0, 20.0) legende_y = map_top_mm legende = self._create_legend_item( self._current_layout, "legende", hauptkarte, legende_x, legende_y, width_mm=self._LEGEND_WIDTH_MM, height_mm=legende_height_mm, object_name="Legende", ) legende_hoehe_mm = self._configure_legend_size( legende, width_limit_mm=self._LEGEND_WIDTH_MM, column_count=1, equal_column_width=False, ) quellenangabe_x, quellenangabe_y = _coords_rel_untere_rechte_ecke(10.0, -102.0) quellenangabe_hoehe_mm = 20.0 quellenangabe_obere_y = quellenangabe_y - quellenangabe_hoehe_mm legende_untere_y = legende_y + legende_hoehe_mm if legende_untere_y > quellenangabe_obere_y: self._configure_legend_size( legende, width_limit_mm=self._LEGEND_WIDTH_MM, column_count=2, equal_column_width=True, ) self.add_text_label( self._QUELLENANGABE_TEXT, quellenangabe_x, quellenangabe_y, width_mm=self._LEGEND_WIDTH_MM, height_mm=20.0, text_format="Format_1", html_mode=True, expression_enabled=True, object_name="Quellenangabe", ) kartenschild_x, kartenschild_y = _coords_rel_untere_rechte_ecke(10.0, 0.0) self._create_rectangle_box_item( self._current_layout, "Kartenschild", kartenschild_x, kartenschild_y, self._LEGEND_WIDTH_MM, 92.0, stroke_width_mm=self._FRAME_STROKE_WIDTH_MM, ) kartenschild_line_x, _ = _coords_rel_untere_rechte_ecke(10.0, 0.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): _, line_y = _coords_rel_untere_rechte_ecke(0.0, offset_y) self._create_rectangle_box_item( self._current_layout, f"Kartenschild_Linie_{index}", kartenschild_line_x, line_y, self._LEGEND_WIDTH_MM, 0.0, stroke_width_mm=0.1, ) if kartenname_auswahl == "§38": herausgeber_text = "[%@sn_landkreis_user%] [%@sn_behoerde%]" else: herausgeber_text = "Teilnehmergemeinschaft [%@sn_bezeichnung %] [%@sn_name %]" labels = [ (herausgeber_text, 16.0, -84.0, 171.0, 4.0, "Format_1", "Herausgeber"), ("[% @sn_bezeichnung %]", 16.0, -75.0, 171.0, 4.0, "Format_1", "Bezeichnung_Flurbereinigung"), ("[% @sn_name %]", 16.0, -64.0, 171.0, 5.0, "Format_2", "Name_Flurbereinigung"), ("[% @sn_gemeinden %]", 16.0, -56.0, 171.0, 4.0, "Format_1", "Stadt_Gemeinde"), ("[% @sn_landkreise_proj %]", 16.0, -48.0, 171.0, 4.0, "Format_1", "Landkreis"), ("[% @sn_kartenname %]", 16.0, -32.0, 137.0, 12.0, "Format_3", "Kartenname"), ("Gefertigt: ", 16.0, -23.0, 81.0, 4.0, "Format_1", "Gefertigt"), ("Geprueft: ", 101.0, -23.0, 81.0, 4.0, "Format_1", "Geprüft"), ( "Maßstab: 1 : [%round(map_get(item_variables('Hauptkarte'),'map_scale'),0)%]", 16.0, -13.0, 81.0, 4.0, "Format_1", "Massstab", ), ("Ausgabedatum: [%format_date(now(), 'dd.MM.yyyy')%]", 101.0, -13.0, 81.0, 4.0, "Format_1", "Ausgabedatum"), ("Aktenzeichen: ", 16.0, -3.0, 171.0, 4.0, "Format_1", "Aktenzeichen"), ] for entry in labels: text, rx, ry, w, h, fmt, obj_name = entry[:7] v_align = entry[7] if len(entry) > 7 else "top" x, y = _coords_rel_untere_rechte_ecke(rx, ry) self.add_text_label( text, x, y, width_mm=w, height_mm=h, text_format=fmt, expression_enabled=True, object_name=obj_name, v_align=v_align, ) def _create_label_item( self, layout: Any, template_name: str, text: str, x_mm: float, y_mm: float, *, text_format: str | None = None, font_name: str | None = None, font_size: int | None = None, font_style: str | None = None, width_mm: float | None = None, height_mm: float | None = None, html_mode: bool = False, expression_enabled: bool = False, object_name: str | None = None, v_align: str = "top", ) -> Any: template = self._OBJECT_TEMPLATES[template_name] label = QgsLayoutItemLabel(layout) item_id = object_name or template["id"] label.setId(item_id) set_object_name = getattr(label, "setObjectName", None) if callable(set_object_name): set_object_name(item_id) 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 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 == "bold") if hasattr(font, "setItalic"): font.setItalic(resolved_font_style == "italic") label.setFont(font) 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) align_map = { "top": "AlignTop", "center": "AlignVCenter", "bottom": "AlignBottom", } align_attr = align_map.get(str(v_align).lower(), "AlignTop") align_qt = getattr(Qt, align_attr, None) if align_qt is None: alignment_flag = getattr(Qt, "AlignmentFlag", None) if alignment_flag is not None: align_qt = getattr(alignment_flag, align_attr, None) set_v_align = getattr(label, "setVAlign", None) if callable(set_v_align) and align_qt is not None: try: set_v_align(align_qt) except Exception: pass 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, ) ) set_locked = getattr(label, "setLocked", None) if callable(set_locked): try: set_locked(True) except Exception: pass layout.addLayoutItem(label) return label def _resolve_visible_map_layers(self, map_item: Any) -> list[Any]: layers_method = getattr(map_item, "layers", None) if callable(layers_method): try: resolved_layers = layers_method() if isinstance(resolved_layers, (list, tuple)): layers = [layer for layer in resolved_layers if layer is not None] if layers: return layers except Exception: pass layer_tree_root_method = getattr(self.project, "layerTreeRoot", None) if not callable(layer_tree_root_method): return [] try: root = layer_tree_root_method() except Exception: return [] find_layers = getattr(root, "findLayers", None) if not callable(find_layers): return [] visible_layers: list[Any] = [] try: tree_layers = find_layers() if not isinstance(tree_layers, (list, tuple)): return [] for tree_layer in tree_layers: is_visible = getattr(tree_layer, "itemVisibilityChecked", None) if callable(is_visible) and not is_visible(): continue layer = getattr(tree_layer, "layer", None) resolved_layer = layer() if callable(layer) else None if resolved_layer is not None: visible_layers.append(resolved_layer) except Exception: return [] return visible_layers def _create_legend_item( self, layout: Any, template_name: str, linked_map: Any, x_mm: float, y_mm: float, *, width_mm: float | None = None, height_mm: float | None = None, object_name: str | None = None, ) -> Any: template = self._OBJECT_TEMPLATES[template_name] legend = QgsLayoutItemLegend(layout) item_id = object_name or template["id"] legend.setId(item_id) set_object_name = getattr(legend, "setObjectName", None) if callable(set_object_name): set_object_name(item_id) set_linked_map = getattr(legend, "setLinkedMap", None) if callable(set_linked_map): try: set_linked_map(linked_map) except Exception: pass set_title = getattr(legend, "setTitle", None) if callable(set_title): try: set_title("Legende") except Exception: pass set_auto_update_model = getattr(legend, "setAutoUpdateModel", None) if callable(set_auto_update_model): try: set_auto_update_model(True) except Exception: pass set_filter_by_map = getattr(legend, "setLegendFilterByMapEnabled", None) if callable(set_filter_by_map): try: set_filter_by_map(True) except Exception: pass set_reference_point = getattr(legend, "setReferencePoint", None) upper_left = getattr(getattr(QgsLayoutItem, "ReferencePoint", object), "UpperLeft", None) if callable(set_reference_point) and upper_left is not None: set_reference_point(upper_left) template_width, template_height = template.get("size_mm", (0.0, 0.0)) legend.attemptMove(QgsLayoutPoint(x_mm, y_mm, MM)) legend.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, ) ) set_frame_enabled = getattr(legend, "setFrameEnabled", None) if callable(set_frame_enabled): try: set_frame_enabled(True) except Exception: pass self._set_item_frame_stroke_width(legend, self._FRAME_STROKE_WIDTH_MM) set_background_enabled = getattr(legend, "setBackgroundEnabled", None) if callable(set_background_enabled): try: set_background_enabled(False) except Exception: pass set_locked = getattr(legend, "setLocked", None) if callable(set_locked): try: set_locked(True) except Exception: pass layout.addLayoutItem(legend) refresh = getattr(legend, "refresh", None) if callable(refresh): try: refresh() except Exception: pass return legend def _get_item_size_mm(self, item: Any) -> tuple[float | None, float | None]: def _to_float(value: Any) -> float | None: if isinstance(value, (int, float)): return float(value) if isinstance(value, str): try: return float(value) except Exception: return None return None size_with_units = getattr(item, "sizeWithUnits", None) if callable(size_with_units): try: size = size_with_units() get_width = getattr(size, "width", None) get_height = getattr(size, "height", None) width = _to_float(get_width()) if callable(get_width) else None height = _to_float(get_height()) if callable(get_height) else None if width is not None and height is not None: return width, height except Exception: pass rect = getattr(item, "rect", None) if callable(rect): try: item_rect = rect() get_width = getattr(item_rect, "width", None) get_height = getattr(item_rect, "height", None) width = _to_float(get_width()) if callable(get_width) else None height = _to_float(get_height()) if callable(get_height) else None return width, height except Exception: pass return None, None def _configure_legend_size( self, legend: Any, *, width_limit_mm: float, column_count: int, equal_column_width: bool, ) -> float: set_locked = getattr(legend, "setLocked", None) if callable(set_locked): try: set_locked(False) except Exception: pass set_column_count = getattr(legend, "setColumnCount", None) if callable(set_column_count): try: set_column_count(column_count) except Exception: pass set_equal_column_width = getattr(legend, "setEqualColumnWidth", None) if callable(set_equal_column_width): try: set_equal_column_width(equal_column_width) except Exception: pass refresh = getattr(legend, "refresh", None) if callable(refresh): try: refresh() except Exception: pass adjust_box_size = getattr(legend, "adjustBoxSize", None) if callable(adjust_box_size): try: adjust_box_size() except Exception: pass _, updated_height_mm = self._get_item_size_mm(legend) if updated_height_mm is None: updated_height_mm = 20.0 if callable(set_locked): try: set_locked(True) except Exception: pass return updated_height_mm def _configure_hauptkarte_grids(self, hauptkarte: Any) -> None: grids_method = getattr(hauptkarte, "grids", None) if not callable(grids_method): return try: grid_stack = grids_method() except Exception: return if grid_stack is None: return remove_grid = getattr(grid_stack, "removeGrid", None) if callable(remove_grid): for grid_id in ("SN_FrameAnnotationGrid", "SN_MarkerGrid"): try: remove_grid(grid_id) except Exception: pass add_grid = getattr(grid_stack, "addGrid", None) if not callable(add_grid): return if QgsLayoutItemMapGrid is object: return frame_annotation_grid = QgsLayoutItemMapGrid("SN_FrameAnnotationGrid", hauptkarte) self._apply_grid_common_settings(frame_annotation_grid) self._safe_grid_call(frame_annotation_grid, "setStyle", MAP_GRID_STYLE_FRAME_ANNOTATIONS) self._safe_grid_call(frame_annotation_grid, "setFrameStyle", MAP_GRID_FRAME_STYLE_INTERIOR_TICKS) self._safe_grid_call(frame_annotation_grid, "setAnnotationEnabled", True) self._safe_grid_call(frame_annotation_grid, "setAnnotationFormat", MAP_GRID_ANNOTATION_FORMAT_DECIMAL) self._safe_grid_call(frame_annotation_grid, "setAnnotationPrecision", 0) self._safe_grid_call( frame_annotation_grid, "setAnnotationDisplay", MAP_GRID_ANNOTATION_DISPLAY_HIDE_ALL, MAP_GRID_BORDER_LEFT, ) self._safe_grid_call( frame_annotation_grid, "setAnnotationDisplay", MAP_GRID_ANNOTATION_DISPLAY_HIDE_ALL, MAP_GRID_BORDER_TOP, ) self._safe_grid_call( frame_annotation_grid, "setAnnotationDisplay", MAP_GRID_ANNOTATION_DISPLAY_SHOW_Y, MAP_GRID_BORDER_RIGHT, ) self._safe_grid_call( frame_annotation_grid, "setAnnotationDisplay", MAP_GRID_ANNOTATION_DISPLAY_SHOW_X, MAP_GRID_BORDER_BOTTOM, ) self._safe_grid_call( frame_annotation_grid, "setAnnotationPosition", MAP_GRID_ANNOTATION_POSITION_INSIDE_FRAME, MAP_GRID_BORDER_RIGHT, ) self._safe_grid_call( frame_annotation_grid, "setAnnotationPosition", MAP_GRID_ANNOTATION_POSITION_INSIDE_FRAME, MAP_GRID_BORDER_BOTTOM, ) self._apply_grid_annotation_text_style(frame_annotation_grid) add_grid(frame_annotation_grid) update_bounding_rect = getattr(hauptkarte, "updateBoundingRect", None) if callable(update_bounding_rect): try: update_bounding_rect() except Exception: pass refresh = getattr(hauptkarte, "refresh", None) if callable(refresh): try: refresh() except Exception: pass def _apply_grid_common_settings(self, grid: Any) -> None: self._safe_grid_call(grid, "setEnabled", True) self._safe_grid_call(grid, "setUnits", MAP_GRID_UNIT_MAP_UNIT) self._safe_grid_call(grid, "setIntervalX", self._GRID_INTERVAL_MAP_UNITS) self._safe_grid_call(grid, "setIntervalY", self._GRID_INTERVAL_MAP_UNITS) def _safe_grid_call(self, grid: Any, method_name: str, *args: Any) -> None: if any(arg is None for arg in args): return method = getattr(grid, method_name, None) if not callable(method): return try: method(*args) except Exception: pass def _apply_grid_annotation_text_style(self, grid: Any) -> None: if QgsTextFormat is object: self._safe_grid_call( grid, "setAnnotationTextFormat", { "font_family": "Arial", "font_size": 10, "background": { "enabled": True, "shape": "rectangle", "size_type": "buffer", "buffer_mm": 1.0, "fill_color": (255, 255, 255), }, }, ) return try: text_format = QgsTextFormat() text_format.setFont(QFont("Arial", 10)) if TEXT_RENDER_UNIT_POINTS is not None: set_size_unit = getattr(text_format, "setSizeUnit", None) if callable(set_size_unit): set_size_unit(TEXT_RENDER_UNIT_POINTS) set_size = getattr(text_format, "setSize", None) if callable(set_size): set_size(10) if QgsTextBackgroundSettings is not object: try: qt_core = importlib.import_module("qgis.PyQt.QtCore") qt_gui = importlib.import_module("qgis.PyQt.QtGui") QSizeF = getattr(qt_core, "QSizeF", None) QPointF = getattr(qt_core, "QPointF", None) QColor = getattr(qt_gui, "QColor", None) except Exception: QSizeF = None QPointF = None QColor = None background = QgsTextBackgroundSettings() set_enabled = getattr(background, "setEnabled", None) if callable(set_enabled): set_enabled(True) set_type = getattr(background, "setType", None) if callable(set_type) and TEXT_BACKGROUND_SHAPE_RECTANGLE is not None: set_type(TEXT_BACKGROUND_SHAPE_RECTANGLE) set_size_type = getattr(background, "setSizeType", None) if callable(set_size_type) and TEXT_BACKGROUND_SIZE_BUFFER is not None: set_size_type(TEXT_BACKGROUND_SIZE_BUFFER) #steuert die Größe des Hintergrunds, wenn die Größe auf "Puffer" eingestellt ist set_size = getattr(background, "setSize", None) if callable(set_size) and QSizeF is not None: set_size(QSizeF(0.0, 0.0)) set_size_unit = getattr(background, "setSizeUnit", None) if callable(set_size_unit) and TEXT_RENDER_UNIT_MILLIMETERS is not None: set_size_unit(TEXT_RENDER_UNIT_MILLIMETERS) set_offset = getattr(background, "setOffset", None) if callable(set_offset): try: if QPointF is not None: set_offset(QPointF(0.0, -1.0)) elif QSizeF is not None: set_offset(QSizeF(0.0, -1.0)) else: set_offset((0.0, -1.0)) except Exception: set_offset_x = getattr(background, "setOffsetX", None) set_offset_y = getattr(background, "setOffsetY", None) if callable(set_offset_x) and callable(set_offset_y): try: set_offset_x(0.0) set_offset_y(-1.0) except Exception: pass set_offset_unit = getattr(background, "setOffsetUnit", None) if callable(set_offset_unit) and TEXT_RENDER_UNIT_MILLIMETERS is not None: set_offset_unit(TEXT_RENDER_UNIT_MILLIMETERS) set_fill_color = getattr(background, "setFillColor", None) if callable(set_fill_color) and QColor is not None: set_fill_color(QColor(255, 255, 255)) set_background = getattr(text_format, "setBackground", None) if callable(set_background): set_background(background) self._safe_grid_call(grid, "setAnnotationTextFormat", text_format) except Exception: pass 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 set_locked = getattr(box, "setLocked", None) if callable(set_locked): try: set_locked(True) 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, kartenname_auswahl: str = "", thema: str = "", fit_extent_to_map: bool = True, ) -> Any: 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)) 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) hauptkarte = self._create_map_item( layout, "hauptkarte", map_left_mm, map_top_mm, map_width_mm, map_height_mm, ) fitted_extent = extent if fit_extent_to_map: fitted_extent = self._fit_extent_to_map_item(extent, map_width_mm, map_height_mm) if fitted_extent is not None and hasattr(fitted_extent, "isNull") and callable(fitted_extent.isNull) and not fitted_extent.isNull(): try: hauptkarte.setExtent(fitted_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: self._set_item_frame_stroke_width(hauptkarte, self._FRAME_STROKE_WIDTH_MM) 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_keep_layer_set = getattr(hauptkarte, "setKeepLayerSet", None) if callable(set_keep_layer_set): try: set_keep_layer_set(True) except Exception: pass 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 ) try: set_refresh_strategy(refresh_cache) except Exception: pass self._configure_hauptkarte_grids(hauptkarte) self._setup_layout_objects( kartenfenster_untere_rechte_ecke_mm, hauptkarte, map_top_mm, map_height_mm, kartenname_auswahl, ) layout_manager.addLayout(layout) self._current_layout = None open_layout_designer(layout) return layout