diff --git a/ui/tab_b_logic.py b/ui/tab_b_logic.py index 495fb52..2e12e2c 100644 --- a/ui/tab_b_logic.py +++ b/ui/tab_b_logic.py @@ -2,8 +2,11 @@ sn_plan41/ui/tab_b_logic.py – Fachlogik für Tab B (Druck) """ from __future__ import annotations -from sn_basis.functions.variable_wrapper import set_variable +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.modules.Pruefmanager import Pruefmanager +from sn_basis.modules.layerpruefer import Layerpruefer KARTENNAME_VAR = "sn_kartenname" @@ -81,4 +84,128 @@ class TabBLogic: def set_formfaktor(self, endlosrolle: bool) -> None: """Setzt ``sn_formfaktor`` auf ``Endlosrolle`` oder ``Blatt``.""" set_variable(FORMFAKTOR_VAR, "Endlosrolle" if endlosrolle else "Blatt", scope="project") - \ No newline at end of file + + # ───────────────────────────────────────────────────────────────────────── + # Pipeline: Druckvorlage_anlegen + # ───────────────────────────────────────────────────────────────────────── + + def druckvorlage_anlegen( + self, + kartenname_auswahl: str, + massstab_auswahl: str, + zielgroesse: str, + formfaktor: bool, + ) -> dict: + """Pipeline 'Druckvorlage_anlegen'. + + Prüft Parameter, berechnet Plotgröße und stellt bei Bedarf Atlas-Rückfrage. + + Returns + ------- + dict + ``ok`` (bool): Ob die Pipeline erfolgreich durchlaufen werden soll. + ``switch_to_tab_a`` (bool): Ob Tab A aktiviert werden soll. + ``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: + self.pruefmanager.zeige_hinweis( + "Verfahrensgebiets-Layer angeben", + "Verfahrensgebiets-Layer angeben", + ) + return {"ok": False, "switch_to_tab_a": True, "atlas_seiten": 0} + + set_variable("sn_verfahrensgebietslayer", layer_id, scope="project") + + # ─── 2. Kartenname prüfen ───────────────────────────────────────── + if kartenname_auswahl not in (KARTENNAME_38, KARTENNAME_41): + self.pruefmanager.zeige_hinweis( + "Kartennamen wählen", + "Kartennamen wählen", + ) + return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} + + # ─── 3. Maßstab ermitteln ───────────────────────────────────────── + if massstab_auswahl == MASSSTAB_WIE_KARTENFENSTER: + massstab_str = get_variable(PLOTMASSSTAB_VAR, scope="project") or "" + try: + massstab_zahl = float(massstab_str) + except (ValueError, TypeError): + massstab_zahl = 0.0 + else: + massstab_zahl = float(PLOTMASSSTAB_BY_AUSWAHL.get(massstab_auswahl, 0)) + + if massstab_zahl <= 0: + self.pruefmanager.zeige_hinweis( + "Maßstab fehlt", + "Kein gültiger Maßstab angegeben.", + ) + return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} + + # ─── 4. Kartenbild berechnen ────────────────────────────────────── + # Der Layer wird als metrisch projiziert (Einheit: m) vorausgesetzt, + # wie es für deutsche Planungslagen (z.B. EPSG:25832) üblich ist. + extent = get_layer_extent(layer) + 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} + + # Naturgröße (m) → Papiergröße (mm): mm = m * 1000 / massstab + kartenbild_w = extent.width() * 1000.0 / massstab_zahl + kartenbild_h = extent.height() * 1000.0 / massstab_zahl + + # ─── 5. Plotgröße = Kartenbild + Randabstand (x+210 mm, y+20 mm) ── + plotgroesse_w = kartenbild_w + 210.0 + plotgroesse_h = kartenbild_h + 20.0 + + # ─── 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]) + else: + ziel_w, ziel_h = float(din_dims[0]), float(din_dims[1]) + + # ─── 7. Passt auf ein Blatt? ────────────────────────────────────── + if plotgroesse_w <= ziel_w and plotgroesse_h <= ziel_h: + return {"ok": True, "switch_to_tab_a": False, "atlas_seiten": 1} + + # ─── 8. Atlas: Anzahl Seiten berechnen ──────────────────────────── + # Nutzbarer Kartenbereich pro Atlasseite (abzüglich gleichem Randabstand) + seite_karte_w = ziel_w - 210.0 + seite_karte_h = ziel_h - 20.0 + + if seite_karte_w <= 0 or seite_karte_h <= 0: + self.pruefmanager.zeige_hinweis( + "Blattgröße zu klein", + "Die gewählte Zielgröße ist kleiner als der Mindest-Randabstand. " + "Bitte eine größere Blattgröße wählen.", + ) + return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": 0} + + pages_x = math.ceil(kartenbild_w / seite_karte_w) + pages_y = math.ceil(kartenbild_h / seite_karte_h) + anzahl_seiten = pages_x * pages_y + + ja = self.pruefmanager.frage_ja_nein( + "Ausdruck als Atlas anlegen?", + f"Für die ausgewählten Parameter sind {anzahl_seiten} Einzelseiten erforderlich.\n" + "Ausdruck als Atlas anlegen?", + default=True, + ) + if not ja: + return {"ok": False, "switch_to_tab_a": False, "atlas_seiten": anzahl_seiten} + + return {"ok": True, "switch_to_tab_a": False, "atlas_seiten": anzahl_seiten} \ No newline at end of file diff --git a/ui/tab_b_ui.py b/ui/tab_b_ui.py index 4260cd3..99799b8 100644 --- a/ui/tab_b_ui.py +++ b/ui/tab_b_ui.py @@ -12,6 +12,7 @@ from sn_basis.functions.qt_wrapper import ( QComboBox, QCheckBox, QHBoxLayout, + QPushButton, ) from sn_basis.functions.qgiscore_wrapper import QgsProject from sn_basis.functions.qgisui_wrapper import iface @@ -77,8 +78,8 @@ class TabB(QWidget): self._connected_theme_collection: object = None # Referenz für sauberes Trennen self._zielgroesse_combo: Optional[QComboBox] = None self._endlosrolle_cb: Optional[QCheckBox] = None + self._btn_vorlage_erstellen: Optional[QPushButton] = None - self._build_ui() self._restore_state() self._connect_theme_collection_signals() @@ -158,6 +159,10 @@ class TabB(QWidget): zielgroesse_row.addWidget(self._endlosrolle_cb) main_layout.addLayout(zielgroesse_row) + self._btn_vorlage_erstellen = QPushButton("Vorlage erstellen", self) + self._btn_vorlage_erstellen.clicked.connect(self._on_vorlage_erstellen) + main_layout.addWidget(self._btn_vorlage_erstellen) + main_layout.addStretch(1) self.setLayout(main_layout) @@ -236,6 +241,34 @@ class TabB(QWidget): if self.logic: self.logic.set_formfaktor(checked) + def _on_vorlage_erstellen(self) -> None: + """Startet die Pipeline Druckvorlage_anlegen.""" + if not self.logic or not self.pruefmanager: + return + + result = self.logic.druckvorlage_anlegen( + 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, + ) + + if result.get("switch_to_tab_a"): + self._aktiviere_tab_a() + + def _aktiviere_tab_a(self) -> None: + """Wechselt zum Tab A im übergeordneten TabWidget.""" + try: + widget = self.parent() + while widget is not None: + if hasattr(widget, "setCurrentIndex") and hasattr(widget, "count"): + widget.setCurrentIndex(0) + return + parent_fn = getattr(widget, "parent", None) + widget = parent_fn() if callable(parent_fn) else None + except Exception: + pass + def _connect_project_signals(self) -> None: """Verbindet QgsProject-Signale für Projektwechsel/-neuladen.""" project = QgsProject.instance()