diff --git a/modules/print_layout.py b/modules/print_layout.py index 6e9380d..b4cf731 100644 --- a/modules/print_layout.py +++ b/modules/print_layout.py @@ -128,6 +128,63 @@ class PrintLayout: 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, @@ -680,6 +737,7 @@ class PrintLayout: extent: Any, plotmassstab: float, thema: str = "", + fit_extent_to_map: bool = True, ) -> Any: layout_manager = self.project.layoutManager() existing_layout = layout_manager.layoutByName(name) @@ -709,9 +767,12 @@ class PrintLayout: map_height_mm, ) - if extent is not None and hasattr(extent, "isNull") and callable(extent.isNull) and not extent.isNull(): + 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(extent) + hauptkarte.setExtent(fitted_extent) except Exception: pass diff --git a/modules/print_logic.py b/modules/print_logic.py index 195df68..acdba86 100644 --- a/modules/print_logic.py +++ b/modules/print_logic.py @@ -3,9 +3,11 @@ sn_basis/modules/print_logic.py - Fachlogik fuer den Druck-Tab (nur Einzelblatt) """ from __future__ import annotations -from typing import Any +import importlib +from typing import Any, cast from sn_basis.functions.qgiscore_wrapper import QgsProject, get_layer_extent +from sn_basis.functions.qgisui_wrapper import iface from sn_basis.functions.variable_wrapper import get_variable, set_variable from sn_basis.modules.layerpruefer import Layerpruefer from sn_basis.modules.Pruefmanager import Pruefmanager @@ -17,11 +19,19 @@ PLOTMASSSTAB_VAR = "plotmassstab" VIEW_VAR = "view" ZIELGROESSE_VAR = "zielgroesse" FORMFAKTOR_VAR = "formfaktor" +DRUCKBEREICH_VAR = "druckbereich" +DRUCKPUFFER_VAR = "druckpuffer" +DRUCKRECHTECK_LAYER_NAME = "Druckrechteck" + KARTENNAME_38 = "§38" KARTENNAME_41 = "§41" MASSSTAB_WIE_KARTENFENSTER = "Wie Kartenfenster" THEMA_WIE_KARTENFENSTER = "wie kartenfenster" +DRUCKBEREICH_VERFAHRENSGEBIET = "Verfahrensgebiet" +DRUCKBEREICH_AKTUELLE_AUSWAHL = "aktuelle Auswahl" +DRUCKBEREICH_RECHTECK = "Rechteck" + KARTENNAME_BY_AUSWAHL = { KARTENNAME_38: "Planungsuebersicht §38 FlurbG", KARTENNAME_41: "Karte zum Plan ueber die gemeinschaftlichen und oeffentlichen Anlagen (§ 41 FlurbG)", @@ -45,6 +55,7 @@ DIN_GROESSEN: dict[str, tuple[int, int]] = { "DIN A4": (210, 297), } DIN_STANDARD = "DIN A0" +DRUCKPUFFER_STANDARD_CM = 1 class PrintLogic: @@ -52,6 +63,8 @@ class PrintLogic: def __init__(self, pruefmanager: Pruefmanager) -> None: self.pruefmanager = pruefmanager + self._rectangle_tool: Any | None = None + self._previous_map_tool: Any | None = None def _resolve_layer_from_project(self) -> object | None: project = QgsProject.instance() @@ -69,6 +82,463 @@ class PrintLogic: except Exception: return None + def _resolve_active_layer(self) -> object | None: + active_layer_fn = getattr(iface, "activeLayer", None) + if not callable(active_layer_fn): + return None + try: + return active_layer_fn() + except Exception: + return None + + def _get_druckpuffer_cm(self) -> int: + raw_value = get_variable(DRUCKPUFFER_VAR, scope="project") + try: + value = int(str(raw_value).strip()) + except (TypeError, ValueError): + value = DRUCKPUFFER_STANDARD_CM + return max(0, value) + + def _get_druckpuffer_m(self, plotmassstab: float) -> float: + return (self._get_druckpuffer_cm() * 10.0 * plotmassstab) / 1000.0 + + def _import_qgis_symbol(self, module_name: str, symbol_name: str) -> Any | None: + try: + module = importlib.import_module(module_name) + except Exception: + return None + return getattr(module, symbol_name, None) + + def _build_rectangle(self, x_min: float, y_min: float, x_max: float, y_max: float) -> Any | None: + qgs_rectangle = self._import_qgis_symbol("qgis.core", "QgsRectangle") + if qgs_rectangle is None: + return None + try: + return qgs_rectangle(x_min, y_min, x_max, y_max) + except Exception: + return None + + def _clone_extent(self, extent: Any) -> Any | None: + if extent is None: + return None + + buffered = getattr(extent, "buffered", None) + if callable(buffered): + try: + return buffered(0.0) + except Exception: + pass + + x_min = getattr(extent, "xMinimum", lambda: None)() + y_min = getattr(extent, "yMinimum", lambda: None)() + x_max = getattr(extent, "xMaximum", lambda: None)() + y_max = getattr(extent, "yMaximum", lambda: None)() + if None in (x_min, y_min, x_max, y_max): + return None + assert x_min is not None + assert y_min is not None + assert x_max is not None + assert y_max is not None + x_min_f = float(x_min) + y_min_f = float(y_min) + x_max_f = float(x_max) + y_max_f = float(y_max) + return self._build_rectangle(x_min_f, y_min_f, x_max_f, y_max_f) + + def _buffer_extent(self, extent: Any, plotmassstab: float) -> Any | None: + if extent is None: + return None + + buffer_m = self._get_druckpuffer_m(plotmassstab) + if buffer_m <= 0: + return extent + + buffered = getattr(extent, "buffered", None) + if callable(buffered): + try: + return buffered(buffer_m) + except Exception: + pass + + x_min = getattr(extent, "xMinimum", lambda: None)() + y_min = getattr(extent, "yMinimum", lambda: None)() + x_max = getattr(extent, "xMaximum", lambda: None)() + y_max = getattr(extent, "yMaximum", lambda: None)() + if None in (x_min, y_min, x_max, y_max): + return extent + assert x_min is not None + assert y_min is not None + assert x_max is not None + assert y_max is not None + + x_min_f = float(x_min) + y_min_f = float(y_min) + x_max_f = float(x_max) + y_max_f = float(y_max) + + expanded = self._build_rectangle( + x_min_f - buffer_m, + y_min_f - buffer_m, + x_max_f + buffer_m, + y_max_f + buffer_m, + ) + return expanded or extent + + def _get_existing_layer_by_name(self, layer_name: str) -> object | None: + project = QgsProject.instance() + map_layers_by_name = getattr(project, "mapLayersByName", None) + if not callable(map_layers_by_name): + return None + try: + matches = map_layers_by_name(layer_name) + except Exception: + return None + if not isinstance(matches, (list, tuple)) or not matches: + return None + return matches[0] + + def _create_druckrechteck_layer(self, reference_layer: object | None) -> object | None: + existing_layer = self._get_existing_layer_by_name(DRUCKRECHTECK_LAYER_NAME) + if existing_layer is not None: + return existing_layer + + qgs_vector_layer = self._import_qgis_symbol("qgis.core", "QgsVectorLayer") + if qgs_vector_layer is None: + return None + + crs_authid = "EPSG:25832" + if reference_layer is not None: + try: + layer_crs = getattr(reference_layer, "crs", lambda: None)() + if layer_crs is not None: + crs_authid = layer_crs.authid() + except Exception: + pass + + layer = qgs_vector_layer(f"Polygon?crs={crs_authid}", DRUCKRECHTECK_LAYER_NAME, "memory") + if not layer or not getattr(layer, "isValid", lambda: False)(): + return None + + try: + QgsProject.instance().addMapLayer(layer) + except Exception: + return None + return layer + + def _clear_layer_features(self, layer: object) -> None: + get_features = getattr(layer, "getFeatures", None) + data_provider = getattr(layer, "dataProvider", lambda: None)() + if not callable(get_features) or data_provider is None: + return + + feature_ids: list[int] = [] + try: + features = cast(Any, get_features()) + for feature in features: + feature_id = getattr(feature, "id", lambda: None)() + if feature_id is not None: + feature_ids.append(int(feature_id)) + except Exception: + feature_ids = [] + + if feature_ids: + delete_features = getattr(data_provider, "deleteFeatures", None) + if callable(delete_features): + try: + delete_features(feature_ids) + except Exception: + pass + + def _store_druckrechteck(self, layer: object, rectangle: Any) -> bool: + qgs_feature = self._import_qgis_symbol("qgis.core", "QgsFeature") + qgs_geometry = self._import_qgis_symbol("qgis.core", "QgsGeometry") + if qgs_feature is None or qgs_geometry is None: + return False + + if layer is None or rectangle is None: + return False + + data_provider = getattr(layer, "dataProvider", lambda: None)() + if data_provider is None: + return False + + self._clear_layer_features(layer) + + feature = qgs_feature() + try: + feature.setGeometry(qgs_geometry.fromRect(rectangle)) + add_features = getattr(data_provider, "addFeatures", None) + if callable(add_features): + add_features([feature]) + update_extents = getattr(layer, "updateExtents", None) + if callable(update_extents): + update_extents() + trigger_repaint = getattr(layer, "triggerRepaint", None) + if callable(trigger_repaint): + trigger_repaint() + except Exception: + return False + + try: + set_variable("druckrechteck_xmin", str(rectangle.xMinimum()), scope="project") + set_variable("druckrechteck_ymin", str(rectangle.yMinimum()), scope="project") + set_variable("druckrechteck_xmax", str(rectangle.xMaximum()), scope="project") + set_variable("druckrechteck_ymax", str(rectangle.yMaximum()), scope="project") + except Exception: + pass + + map_canvas = getattr(iface, "mapCanvas", lambda: None)() + if map_canvas is not None: + refresh = getattr(map_canvas, "refresh", None) + if callable(refresh): + try: + refresh() + except Exception: + pass + return True + + def activate_druckrechteck_capture(self, reference_layer: object | None = None) -> bool: + layer = self._create_druckrechteck_layer(reference_layer) + if layer is None: + self.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Der temporaere Layer 'Druckrechteck' konnte nicht erzeugt werden.", + ) + return False + + map_canvas = getattr(iface, "mapCanvas", lambda: None)() + if map_canvas is None: + self.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Das Kartenfenster ist nicht verfuegbar.", + ) + return False + + qgs_geometry = self._import_qgis_symbol("qgis.core", "QgsGeometry") + qgs_rectangle = self._import_qgis_symbol("qgis.core", "QgsRectangle") + qgs_wkb_types = self._import_qgis_symbol("qgis.core", "QgsWkbTypes") + qgs_map_tool_emit_point = self._import_qgis_symbol("qgis.gui", "QgsMapToolEmitPoint") + qgs_rubber_band = self._import_qgis_symbol("qgis.gui", "QgsRubberBand") + if None in (qgs_geometry, qgs_rectangle, qgs_wkb_types, qgs_map_tool_emit_point, qgs_rubber_band): + self.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Die Rechteck-Erfassung ist in dieser QGIS-Umgebung nicht verfuegbar.", + ) + return False + + logic = self + self._previous_map_tool = getattr(map_canvas, "mapTool", lambda: None)() + base_tool_class = cast(type, qgs_map_tool_emit_point) + rubber_band_class = cast(Any, qgs_rubber_band) + geometry_class = cast(Any, qgs_geometry) + rectangle_class = cast(Any, qgs_rectangle) + polygon_geometry = cast(Any, getattr(qgs_wkb_types, "PolygonGeometry", None)) + + class RectangleCaptureTool(base_tool_class): + def __init__(self, canvas_widget: Any) -> None: + super().__init__(canvas_widget) + self.canvas = canvas_widget + self.start_point: Any | None = None + self.rubber_band: Any = rubber_band_class(canvas_widget, polygon_geometry) + + def _to_rect(self, start_point: Any, end_point: Any) -> Any: + x_min = min(start_point.x(), end_point.x()) + x_max = max(start_point.x(), end_point.x()) + y_min = min(start_point.y(), end_point.y()) + y_max = max(start_point.y(), end_point.y()) + return rectangle_class(x_min, y_min, x_max, y_max) + + def _show_rect(self, rect: Any) -> None: + try: + self.rubber_band.setToGeometry(geometry_class.fromRect(rect), None) + except Exception: + pass + + def canvasPressEvent(self, event: Any) -> None: + self.start_point = self.toMapCoordinates(event.pos()) + + def canvasMoveEvent(self, event: Any) -> None: + if self.start_point is None: + return + end_point = self.toMapCoordinates(event.pos()) + self._show_rect(self._to_rect(self.start_point, end_point)) + + def canvasReleaseEvent(self, event: Any) -> None: + if self.start_point is None: + return + end_point = self.toMapCoordinates(event.pos()) + rectangle = self._to_rect(self.start_point, end_point) + self._show_rect(rectangle) + width = getattr(rectangle, "width", lambda: 0.0)() + height = getattr(rectangle, "height", lambda: 0.0)() + if width <= 0 or height <= 0: + logic.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Das gezeichnete Rechteck ist ungueltig.", + ) + else: + if logic._store_druckrechteck(layer, rectangle): + logic.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Das Druckrechteck wurde uebernommen.", + ) + try: + self.rubber_band.reset(polygon_geometry) + except Exception: + pass + if logic._previous_map_tool is not None: + try: + self.canvas.setMapTool(logic._previous_map_tool) + except Exception: + pass + logic._rectangle_tool = None + + rectangle_tool = RectangleCaptureTool(map_canvas) + self._rectangle_tool = rectangle_tool + + set_active_layer = getattr(iface, "setActiveLayer", None) + if callable(set_active_layer): + try: + set_active_layer(layer) + except Exception: + pass + + start_editing = getattr(layer, "startEditing", None) + if callable(start_editing): + try: + start_editing() + except Exception: + pass + + set_map_tool = getattr(map_canvas, "setMapTool", None) + if callable(set_map_tool): + try: + set_map_tool(rectangle_tool) + except Exception: + self.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Das Rechteck-Werkzeug konnte nicht aktiviert werden.", + ) + return False + + self.pruefmanager.zeige_hinweis( + "Druckrechteck", + "Bitte ziehen Sie ein Rechteck in der Karte auf.", + ) + return True + + def _resolve_selection_layer(self, fallback_layer: object | None) -> object | None: + active_layer = self._resolve_active_layer() + if active_layer is not None: + selected_count = getattr(active_layer, "selectedFeatureCount", None) + if callable(selected_count): + try: + selected_count_value = selected_count() + if int(cast(Any, selected_count_value)) > 0: + return active_layer + except Exception: + pass + return fallback_layer + + def _get_selected_extent(self, layer: object | None) -> Any | None: + if layer is None: + return None + + get_selected_features = getattr(layer, "selectedFeatures", None) + if not callable(get_selected_features): + return None + + try: + selected_features_any = get_selected_features() + selected_features = list(cast(Any, selected_features_any)) + except Exception: + return None + if not selected_features: + return None + + merged_extent: Any | None = None + for feature in selected_features: + geometry = getattr(feature, "geometry", lambda: None)() + if geometry is None: + continue + bounding_box = getattr(geometry, "boundingBox", lambda: None)() + if bounding_box is None: + continue + if merged_extent is None: + merged_extent = self._clone_extent(bounding_box) + continue + combine_extent = getattr(merged_extent, "combineExtentWith", None) + if callable(combine_extent): + try: + combine_extent(bounding_box) + continue + except Exception: + pass + + x_min = min(float(merged_extent.xMinimum()), float(bounding_box.xMinimum())) + y_min = min(float(merged_extent.yMinimum()), float(bounding_box.yMinimum())) + x_max = max(float(merged_extent.xMaximum()), float(bounding_box.xMaximum())) + y_max = max(float(merged_extent.yMaximum()), float(bounding_box.yMaximum())) + merged_extent = self._build_rectangle(x_min, y_min, x_max, y_max) + return merged_extent + + def _get_druckrechteck_extent(self) -> Any | None: + layer = self._get_existing_layer_by_name(DRUCKRECHTECK_LAYER_NAME) + extent = get_layer_extent(layer) + if extent is not None: + width = getattr(extent, "width", lambda: 0.0)() + height = getattr(extent, "height", lambda: 0.0)() + if width > 0 and height > 0: + return extent + + try: + x_min = float(get_variable("druckrechteck_xmin", scope="project")) + y_min = float(get_variable("druckrechteck_ymin", scope="project")) + x_max = float(get_variable("druckrechteck_xmax", scope="project")) + y_max = float(get_variable("druckrechteck_ymax", scope="project")) + except (TypeError, ValueError): + return None + return self._build_rectangle(x_min, y_min, x_max, y_max) + + def _resolve_extent( + self, + resolved_layer: object | None, + druckbereich_auswahl: str, + plotmassstab: float, + ) -> tuple[Any | None, object | None]: + if druckbereich_auswahl == DRUCKBEREICH_AKTUELLE_AUSWAHL: + selection_layer = self._resolve_selection_layer(resolved_layer) + extent = self._get_selected_extent(selection_layer) + if extent is None: + self.pruefmanager.zeige_hinweis( + "Druckbereich", + "Auf dem aktuellen Layer sind keine gueltigen Objekte ausgewaehlt.", + ) + return None, None + return self._buffer_extent(extent, plotmassstab), None + + if druckbereich_auswahl == DRUCKBEREICH_RECHTECK: + extent = self._get_druckrechteck_extent() + if extent is None: + self.activate_druckrechteck_capture(resolved_layer) + return None, None + return self._buffer_extent(extent, plotmassstab), None + + lp = Layerpruefer(layer=resolved_layer) + ergebnis = lp.pruefe() + if not ergebnis.ok: + self.pruefmanager.zeige_hinweis( + "Verfahrensgebiets-Layer angeben", + "Kein gueltiger Verfahrensgebiets-Layer gefunden. Bitte Layerauswahl im jeweiligen Plugin setzen.", + ) + return None, None + + extent = get_layer_extent(resolved_layer) + if extent is None: + self.pruefmanager.zeige_hinweis("Fehler", "Layer-Ausdehnung konnte nicht ermittelt werden.") + return None, None + return self._buffer_extent(extent, plotmassstab), resolved_layer + def set_kartenname_for_auswahl(self, auswahl: str) -> None: kartenname = KARTENNAME_BY_AUSWAHL.get(auswahl, "") set_variable(KARTENNAME_VAR, kartenname, scope="project") @@ -96,6 +566,15 @@ class PrintLogic: def set_formfaktor(self, endlosrolle: bool) -> None: set_variable(FORMFAKTOR_VAR, "Endlosrolle" if endlosrolle else "Blatt", scope="project") + def set_druckbereich_for_auswahl(self, auswahl: str) -> None: + if auswahl not in { + DRUCKBEREICH_VERFAHRENSGEBIET, + DRUCKBEREICH_AKTUELLE_AUSWAHL, + DRUCKBEREICH_RECHTECK, + }: + auswahl = DRUCKBEREICH_VERFAHRENSGEBIET + set_variable(DRUCKBEREICH_VAR, auswahl, scope="project") + def druckvorlage_anlegen( self, layer: object | None, @@ -103,21 +582,10 @@ class PrintLogic: massstab_auswahl: str, zielgroesse: str, formfaktor: bool, + druckbereich_auswahl: str, ) -> dict: resolved_layer = layer or self._resolve_layer_from_project() - lp = Layerpruefer(layer=resolved_layer) - ergebnis = lp.pruefe() - if not ergebnis.ok: - self.pruefmanager.zeige_hinweis( - "Verfahrensgebiets-Layer angeben", - "Kein gueltiger Verfahrensgebiets-Layer gefunden. Bitte Layerauswahl im jeweiligen Plugin setzen.", - ) - return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} - - layer_id = getattr(resolved_layer, "id", lambda: "")() or "" - set_variable("verfahrensgebietslayer", layer_id, scope="project") - if kartenname_auswahl not in (KARTENNAME_38, KARTENNAME_41): self.pruefmanager.zeige_hinweis("Kartennamen waehlen", "Kartennamen waehlen") return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} @@ -135,11 +603,15 @@ class PrintLogic: self.pruefmanager.zeige_hinweis("Massstab fehlt", "Kein gueltiger Massstab angegeben.") return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} - extent = get_layer_extent(resolved_layer) + extent, layer_for_extent = self._resolve_extent(resolved_layer, druckbereich_auswahl, massstab_zahl) if extent is None: - self.pruefmanager.zeige_hinweis("Fehler", "Layer-Ausdehnung konnte nicht ermittelt werden.") return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} + if layer_for_extent is not None: + layer_id = getattr(layer_for_extent, "id", lambda: "")() or "" + if layer_id: + set_variable("verfahrensgebietslayer", layer_id, scope="project") + kartenbild_w = extent.width() * 1000.0 / massstab_zahl kartenbild_h = extent.height() * 1000.0 / massstab_zahl @@ -153,7 +625,10 @@ class PrintLogic: ziel_w, ziel_h = float(din_dims[0]), float(din_dims[1]) if ziel_w < DIN_GROESSEN["DIN A4"][0] or ziel_h < DIN_GROESSEN["DIN A4"][1]: - self.pruefmanager.zeige_hinweis("Blattgroesse zu klein", "Die Zielgroesse darf nicht kleiner als DIN A4 sein.") + self.pruefmanager.zeige_hinweis( + "Blattgroesse zu klein", + "Die Zielgroesse darf nicht kleiner als DIN A4 sein.", + ) return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} if plotgroesse_w > ziel_w or plotgroesse_h > ziel_h: @@ -163,7 +638,7 @@ class PrintLogic: if plotgroesse_w > ziel_w or plotgroesse_h > ziel_h: self.pruefmanager.zeige_hinweis( "Zielgroesse zu klein", - "Die gewaehlte Zielgroesse reicht fuer den Einzelblatt-Druck nicht aus." + "Die gewaehlte Zielgroesse reicht fuer den Einzelblatt-Druck nicht aus.", ) return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} @@ -192,12 +667,16 @@ class PrintLogic: extent=extent, plotmassstab=massstab_zahl, thema=thema, + fit_extent_to_map=(druckbereich_auswahl != DRUCKBEREICH_VERFAHRENSGEBIET), ) except ValueError as exc: self.pruefmanager.zeige_hinweis("Vorlage anlegen", str(exc)) return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} except Exception as exc: - self.pruefmanager.zeige_hinweis("Vorlage anlegen", f"Die Vorlage konnte nicht angelegt werden: {exc}") + 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} diff --git a/ui/tabs/print_tab.py b/ui/tabs/print_tab.py index c07fcae..8884cd6 100644 --- a/ui/tabs/print_tab.py +++ b/ui/tabs/print_tab.py @@ -26,6 +26,12 @@ from sn_basis.modules.print_logic import ( THEMA_WIE_KARTENFENSTER, DIN_GROESSEN, DIN_STANDARD, + DRUCKBEREICH_VAR, + DRUCKPUFFER_VAR, + DRUCKPUFFER_STANDARD_CM, + DRUCKBEREICH_VERFAHRENSGEBIET, + DRUCKBEREICH_AKTUELLE_AUSWAHL, + DRUCKBEREICH_RECHTECK, ) @@ -37,6 +43,11 @@ MASSSTAB_VAR = "tab_b_massstab" THEMA_VAR = "tab_b_thema" ZIELGROESSE_UI_VAR = "tab_b_zielgroesse" FORMFAKTOR_UI_VAR = "tab_b_formfaktor" +DRUCKBEREICH_OPTIONEN = [ + DRUCKBEREICH_VERFAHRENSGEBIET, + DRUCKBEREICH_AKTUELLE_AUSWAHL, + DRUCKBEREICH_RECHTECK, +] class PrintTab(QWidget): @@ -55,7 +66,9 @@ class PrintTab(QWidget): self._theme_signal_connected = False self._connected_theme_collection: object = None self._zielgroesse_combo: Optional[Any] = None + self._druckbereich_combo: Optional[Any] = None self._endlosrolle_cb: Optional[Any] = None + self._druckpuffer_button: Optional[Any] = None self._btn_vorlage_erstellen: Optional[Any] = None self._build_ui() @@ -79,6 +92,8 @@ class PrintTab(QWidget): self.logic.set_view_for_auswahl(self._thema_combo.currentText()) if self._zielgroesse_combo: self.logic.set_zielgroesse_for_auswahl(self._zielgroesse_combo.currentText()) + if self._druckbereich_combo: + self.logic.set_druckbereich_for_auswahl(self._druckbereich_combo.currentText()) if self._endlosrolle_cb: self.logic.set_formfaktor(self._endlosrolle_cb.isChecked()) @@ -141,6 +156,41 @@ class PrintTab(QWidget): zielgroesse_row.addWidget(endlosrolle_cb) main_layout.addLayout(zielgroesse_row) + druckbereich_row = QHBoxLayout() + druckbereich_row.setSpacing(6) + + druckbereich_column = QVBoxLayout() + druckbereich_column.setSpacing(4) + druckbereich_column.setContentsMargins(0, 0, 0, 0) + + druckbereich_label = QLabel("Druckbereich") + druckbereich_label.setStyleSheet("font-weight: bold; margin-top: 6px;") + druckbereich_column.addWidget(druckbereich_label) + + druckbereich_combo = QComboBox(self) + druckbereich_combo.addItems(DRUCKBEREICH_OPTIONEN) + druckbereich_combo.currentTextChanged.connect(self._on_druckbereich_changed) + self._druckbereich_combo = druckbereich_combo + druckbereich_column.addWidget(druckbereich_combo) + druckbereich_row.addLayout(druckbereich_column, 1) + + druckpuffer_column = QVBoxLayout() + druckpuffer_column.setSpacing(4) + druckpuffer_column.setContentsMargins(0, 0, 0, 0) + + druckpuffer_label = QLabel("Kartenpuffer") + druckpuffer_label.setStyleSheet("font-weight: bold; margin-top: 6px;") + druckpuffer_column.addWidget(druckpuffer_label) + + druckpuffer_button = QPushButton(self._format_druckpuffer_label(DRUCKPUFFER_STANDARD_CM), self) + druckpuffer_button.setToolTip("Puffer") + druckpuffer_button.clicked.connect(self._on_druckpuffer_clicked) + self._druckpuffer_button = druckpuffer_button + druckpuffer_column.addWidget(druckpuffer_button) + druckbereich_row.addLayout(druckpuffer_column) + + main_layout.addLayout(druckbereich_row) + btn_vorlage_erstellen = QPushButton("Vorlage erstellen", self) btn_vorlage_erstellen.clicked.connect(self._on_vorlage_erstellen) self._btn_vorlage_erstellen = btn_vorlage_erstellen @@ -154,6 +204,8 @@ class PrintTab(QWidget): return if not self._zielgroesse_combo or not self._endlosrolle_cb: return + if not self._druckbereich_combo or not self._druckpuffer_button: + return saved_kartenname = get_variable(KARTENNAME_VAR, scope="project") if saved_kartenname in (KARTENNAME_38, KARTENNAME_41): @@ -187,6 +239,19 @@ class PrintTab(QWidget): saved_formfaktor = get_variable(FORMFAKTOR_UI_VAR, scope="project") self._endlosrolle_cb.setChecked(saved_formfaktor == "Endlosrolle") + saved_druckbereich = get_variable(DRUCKBEREICH_VAR, scope="project") + if saved_druckbereich in DRUCKBEREICH_OPTIONEN: + self._druckbereich_combo.setCurrentText(saved_druckbereich) + else: + self._druckbereich_combo.setCurrentText(DRUCKBEREICH_VERFAHRENSGEBIET) + + saved_druckpuffer = get_variable(DRUCKPUFFER_VAR, scope="project") + try: + druckpuffer_cm = int(str(saved_druckpuffer).strip()) + except (TypeError, ValueError): + druckpuffer_cm = DRUCKPUFFER_STANDARD_CM + self._druckpuffer_button.setText(self._format_druckpuffer_label(max(0, druckpuffer_cm))) + def _on_kartenname_changed(self, value: str) -> None: if value in (KARTENNAME_38, KARTENNAME_41): set_variable(KARTENNAME_VAR, value, scope="project") @@ -217,6 +282,51 @@ class PrintTab(QWidget): if self.logic: self.logic.set_formfaktor(checked) + def _on_druckbereich_changed(self, value: str) -> None: + if value not in DRUCKBEREICH_OPTIONEN: + value = DRUCKBEREICH_VERFAHRENSGEBIET + set_variable(DRUCKBEREICH_VAR, value, scope="project") + if self.logic: + self.logic.set_druckbereich_for_auswahl(value) + if value == DRUCKBEREICH_RECHTECK: + self.logic.activate_druckrechteck_capture() + + def _on_druckpuffer_clicked(self) -> None: + if not self.pruefmanager: + return + + current_value = get_variable(DRUCKPUFFER_VAR, scope="project") or str(DRUCKPUFFER_STANDARD_CM) + text_value, bestaetigt = self.pruefmanager.frage_text( + "Puffer um den Druckbereich", + "Puffer um den Druckbereich", + default_text=str(current_value), + ) + if not bestaetigt: + return + + try: + puffer_value = int(str(text_value).strip()) + except (TypeError, ValueError): + self.pruefmanager.zeige_hinweis( + "Puffer um den Druckbereich", + "Der Puffer muss als ganze Zahl eingegeben werden.", + ) + return + + if puffer_value < 0: + self.pruefmanager.zeige_hinweis( + "Puffer um den Druckbereich", + "Der Puffer darf nicht negativ sein.", + ) + return + + set_variable(DRUCKPUFFER_VAR, str(puffer_value), scope="project") + if self._druckpuffer_button: + self._druckpuffer_button.setText(self._format_druckpuffer_label(puffer_value)) + + def _format_druckpuffer_label(self, value_cm: int) -> str: + return f"{value_cm} cm" + def _on_vorlage_erstellen(self) -> None: if not self.logic or not self.pruefmanager: return @@ -227,6 +337,7 @@ class PrintTab(QWidget): 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, + druckbereich_auswahl=self._druckbereich_combo.currentText() if self._druckbereich_combo else DRUCKBEREICH_VERFAHRENSGEBIET, ) def _connect_project_signals(self) -> None: