2025-12-19 14:29:52 +01:00
|
|
|
|
"""
|
2026-03-04 15:32:49 +01:00
|
|
|
|
sn_basis/functions/dialog_wrapper.py – Benutzer-Dialoge (Qt5/6/Mock-kompatibel)
|
2025-12-19 14:29:52 +01:00
|
|
|
|
"""
|
|
|
|
|
|
from typing import Any
|
2026-03-06 10:20:40 +01:00
|
|
|
|
from typing import Literal, Optional
|
2025-12-19 14:29:52 +01:00
|
|
|
|
from sn_basis.functions.qt_wrapper import (
|
2026-03-06 10:20:40 +01:00
|
|
|
|
QMessageBox, YES, NO, CANCEL, QT_VERSION, exec_dialog, ICON_QUESTION,
|
2026-03-20 14:01:57 +01:00
|
|
|
|
QProgressDialog, QCoreApplication, Qt, QInputDialog, QLineEdit,
|
2025-12-19 14:29:52 +01:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def ask_yes_no(
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
2026-03-04 15:32:49 +01:00
|
|
|
|
default: bool = True,
|
2025-12-19 14:29:52 +01:00
|
|
|
|
parent: Any = None,
|
|
|
|
|
|
) -> bool:
|
|
|
|
|
|
"""
|
2026-03-04 15:32:49 +01:00
|
|
|
|
Stellt Ja/Nein-Frage. Funktioniert in PyQt5/6 UND Mock-Modus.
|
2025-12-19 14:29:52 +01:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
2026-03-04 15:32:49 +01:00
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"🔍 Mock-Modus: ask_yes_no('{title}') → {default}")
|
|
|
|
|
|
return default
|
|
|
|
|
|
|
|
|
|
|
|
# ✅ KORREKT: Verwende YES/NO-Aliase aus qt_wrapper!
|
|
|
|
|
|
buttons = YES | NO
|
|
|
|
|
|
default_button = YES if default else NO
|
|
|
|
|
|
|
2025-12-19 14:29:52 +01:00
|
|
|
|
result = QMessageBox.question(
|
2026-03-04 15:32:49 +01:00
|
|
|
|
parent, title, message, buttons, default_button
|
2025-12-19 14:29:52 +01:00
|
|
|
|
)
|
2026-03-04 15:32:49 +01:00
|
|
|
|
|
|
|
|
|
|
# ✅ int(result) == int(YES) funktioniert Qt5/6/Mock
|
|
|
|
|
|
print(f"DEBUG ask_yes_no: result={result}, YES={YES}, match={int(result) == int(YES)}")
|
|
|
|
|
|
return int(result) == int(YES)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"⚠️ ask_yes_no Fehler: {e}")
|
2025-12-19 14:29:52 +01:00
|
|
|
|
return default
|
2026-03-06 10:20:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
2026-03-20 12:42:21 +01:00
|
|
|
|
def show_info_dialog(title: str, message: str, parent: Any = None) -> None:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Zeigt einen modalen Info-Dialog mit OK-Button.
|
|
|
|
|
|
Blockiert bis der Nutzer bestätigt.
|
2026-09-18 11:51:57 +02:00
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
2026-03-20 12:42:21 +01:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"Mock-Modus: show_info_dialog('{title}')")
|
|
|
|
|
|
return
|
|
|
|
|
|
QMessageBox.information(parent, title, message)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"⚠️ show_info_dialog Fehler: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-20 14:01:57 +01:00
|
|
|
|
def ask_text(
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
label: str,
|
|
|
|
|
|
default_text: str = "",
|
|
|
|
|
|
parent: Any = None,
|
|
|
|
|
|
) -> tuple[str, bool]:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""Zeigt einen modalen Texteingabe-Dialog und gibt Text + OK-Status zurück.
|
|
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
|
|
|
|
|
"""
|
2026-03-20 14:01:57 +01:00
|
|
|
|
try:
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"Mock-Modus: ask_text('{title}') -> '{default_text}'")
|
|
|
|
|
|
return default_text, True
|
|
|
|
|
|
# PyQt6: QLineEdit.EchoMode.Normal / PyQt5: QLineEdit.Normal
|
|
|
|
|
|
echo_mode = (
|
|
|
|
|
|
getattr(QLineEdit, "Normal", None)
|
|
|
|
|
|
or getattr(getattr(QLineEdit, "EchoMode", None), "Normal", None)
|
|
|
|
|
|
or 0
|
|
|
|
|
|
)
|
|
|
|
|
|
text, accepted = QInputDialog.getText(
|
|
|
|
|
|
parent,
|
|
|
|
|
|
title,
|
|
|
|
|
|
label,
|
|
|
|
|
|
echo_mode,
|
|
|
|
|
|
default_text,
|
|
|
|
|
|
)
|
|
|
|
|
|
return str(text or ""), bool(accepted)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"⚠️ ask_text Fehler: {e}")
|
|
|
|
|
|
return default_text, False
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-06 10:20:40 +01:00
|
|
|
|
OverwriteDecision = Optional[Literal["overwrite", "append", "cancel"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ask_overwrite_append_cancel_custom(
|
|
|
|
|
|
parent,
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
|
|
|
|
|
) -> Literal["overwrite", "append", "cancel"]:
|
|
|
|
|
|
"""Zeigt Dialog mit benutzerdefinierten Buttons: Überschreiben/Anhängen/Abbrechen.
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
parent :
|
|
|
|
|
|
Eltern-Widget oder None.
|
|
|
|
|
|
title : str
|
|
|
|
|
|
Dialog-Titel.
|
|
|
|
|
|
message : str
|
|
|
|
|
|
Hauptmeldung mit Erklärung.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
Literal["overwrite", "append", "cancel"]
|
|
|
|
|
|
Genaue Entscheidung des Nutzers.
|
|
|
|
|
|
"""
|
|
|
|
|
|
msg = QMessageBox(parent)
|
|
|
|
|
|
msg.setIcon(ICON_QUESTION)
|
|
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
|
|
msg.setText(message)
|
|
|
|
|
|
|
|
|
|
|
|
# Eigene Buttons mit exakten Texten
|
|
|
|
|
|
overwrite_btn = msg.addButton("Überschreiben", QMessageBox.ButtonRole.AcceptRole)
|
|
|
|
|
|
append_btn = msg.addButton("Anhängen", QMessageBox.ButtonRole.ActionRole)
|
|
|
|
|
|
cancel_btn = msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
|
|
|
|
|
|
|
|
|
|
|
|
exec_dialog(msg)
|
|
|
|
|
|
|
|
|
|
|
|
clicked = msg.clickedButton()
|
|
|
|
|
|
if clicked == overwrite_btn:
|
|
|
|
|
|
return "overwrite"
|
|
|
|
|
|
elif clicked == append_btn:
|
|
|
|
|
|
return "append"
|
|
|
|
|
|
else: # cancel_btn
|
|
|
|
|
|
return "cancel"
|
2026-03-12 16:14:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
2026-10-05 21:27:35 +02:00
|
|
|
|
def ask_auswahl(
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
|
|
|
|
|
optionen: list[tuple[str, str]],
|
|
|
|
|
|
default: str,
|
|
|
|
|
|
abbrechen: Optional[str] = None,
|
|
|
|
|
|
parent: Any = None,
|
|
|
|
|
|
) -> str:
|
|
|
|
|
|
"""Stellt eine Frage mit frei benannten Knöpfen und gibt den Schlüssel der Wahl zurück.
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
optionen :
|
|
|
|
|
|
``(schluessel, beschriftung)`` in Anzeigereihenfolge.
|
|
|
|
|
|
default :
|
|
|
|
|
|
Vorbelegter Knopf; im Mock-Modus die Antwort.
|
|
|
|
|
|
abbrechen :
|
|
|
|
|
|
Schlüssel, der beim Schließen des Fensters (Esc, X) und bei einem Fehler gilt;
|
|
|
|
|
|
ohne Angabe ``default``.
|
|
|
|
|
|
"""
|
|
|
|
|
|
rueckfall = abbrechen if abbrechen is not None else default
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
return default
|
|
|
|
|
|
try:
|
|
|
|
|
|
from sn_basis.functions import qt_wrapper
|
|
|
|
|
|
|
|
|
|
|
|
msg = QMessageBox(parent)
|
|
|
|
|
|
msg.setIcon(ICON_QUESTION)
|
|
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
|
|
msg.setText(message)
|
|
|
|
|
|
knoepfe = {}
|
|
|
|
|
|
for schluessel, beschriftung in optionen:
|
|
|
|
|
|
rolle = qt_wrapper.RejectRole if schluessel == abbrechen else qt_wrapper.ActionRole
|
|
|
|
|
|
knoepfe[schluessel] = msg.addButton(beschriftung, rolle)
|
|
|
|
|
|
if default in knoepfe:
|
|
|
|
|
|
msg.setDefaultButton(knoepfe[default])
|
|
|
|
|
|
if abbrechen in knoepfe:
|
|
|
|
|
|
msg.setEscapeButton(knoepfe[abbrechen])
|
|
|
|
|
|
exec_dialog(msg)
|
|
|
|
|
|
geklickt = msg.clickedButton()
|
|
|
|
|
|
for schluessel, knopf in knoepfe.items():
|
|
|
|
|
|
if geklickt == knopf:
|
|
|
|
|
|
return schluessel
|
|
|
|
|
|
return rueckfall
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"⚠️ ask_auswahl Fehler: {e}")
|
|
|
|
|
|
return rueckfall
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-21 21:50:33 +02:00
|
|
|
|
def ask_ergaenzen_entfernen_ersetzen_abbrechen(
|
|
|
|
|
|
parent: Any,
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
|
|
|
|
|
) -> Literal["ergaenzen", "entfernen", "ersetzen", "abbrechen"]:
|
|
|
|
|
|
"""Zeigt Dialog mit vier Optionen für Verfahrensgebiet-Editoperationen.
|
|
|
|
|
|
|
|
|
|
|
|
Optionen:
|
|
|
|
|
|
- **Ergänzen**: ausgewählte Flurstücke zum Verfahrensgebiet hinzufügen
|
|
|
|
|
|
- **Entfernen**: ausgewählte Flurstücke aus dem Verfahrensgebiet entfernen
|
|
|
|
|
|
- **Ersetzen**: Verfahrensgebiet vollständig durch Auswahl ersetzen
|
|
|
|
|
|
- **Abbrechen**: Vorgang beenden ohne Änderung
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
parent :
|
|
|
|
|
|
Eltern-Widget oder None.
|
|
|
|
|
|
title : str
|
|
|
|
|
|
Dialog-Titel.
|
|
|
|
|
|
message : str
|
|
|
|
|
|
Hauptmeldung mit Erklärung.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
Literal["ergaenzen", "entfernen", "ersetzen", "abbrechen"]
|
|
|
|
|
|
Genaue Entscheidung des Nutzers.
|
2026-09-18 11:51:57 +02:00
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
2026-04-21 21:50:33 +02:00
|
|
|
|
"""
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"Mock-Modus: ask_ergaenzen_entfernen_ersetzen_abbrechen('{title}') → abbrechen")
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
msg = QMessageBox(parent)
|
|
|
|
|
|
msg.setIcon(ICON_QUESTION)
|
|
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
|
|
msg.setText(message)
|
|
|
|
|
|
|
|
|
|
|
|
ergaenzen_btn = msg.addButton("Ergänzen", QMessageBox.ButtonRole.ActionRole)
|
|
|
|
|
|
entfernen_btn = msg.addButton("Entfernen", QMessageBox.ButtonRole.ActionRole)
|
|
|
|
|
|
ersetzen_btn = msg.addButton("Ersetzen", QMessageBox.ButtonRole.AcceptRole)
|
|
|
|
|
|
msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
|
|
|
|
|
|
|
|
|
|
|
|
exec_dialog(msg)
|
|
|
|
|
|
|
|
|
|
|
|
clicked = msg.clickedButton()
|
|
|
|
|
|
if clicked == ergaenzen_btn:
|
|
|
|
|
|
return "ergaenzen"
|
|
|
|
|
|
elif clicked == entfernen_btn:
|
|
|
|
|
|
return "entfernen"
|
|
|
|
|
|
elif clicked == ersetzen_btn:
|
|
|
|
|
|
return "ersetzen"
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-05 19:47:25 +02:00
|
|
|
|
def ask_detailpruefung_oder_vg_laden(
|
|
|
|
|
|
parent: Any,
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
|
|
|
|
|
) -> Literal["detailpruefung", "als_vg_laden", "abbrechen"]:
|
|
|
|
|
|
"""Zeigt Dialog mit drei Optionen nach erkannter Flächenabweichung.
|
|
|
|
|
|
|
|
|
|
|
|
Optionen:
|
|
|
|
|
|
- **Detailprüfung starten**: Knickpunktbasierte Prüfung des BROR-Umrings
|
|
|
|
|
|
- **Bodenordnungsobjekt als VG laden**: BROR direkt als Verfahrensgebiet übernehmen
|
|
|
|
|
|
- **Abbrechen**: Vorgang beenden ohne weitere Aktion
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
parent :
|
|
|
|
|
|
Eltern-Widget oder None.
|
|
|
|
|
|
title : str
|
|
|
|
|
|
Dialog-Titel.
|
2026-06-19 11:47:36 +02:00
|
|
|
|
message : str
|
2026-05-05 19:47:25 +02:00
|
|
|
|
Hauptmeldung mit Flächen-Informationen.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
Literal["detailpruefung", "als_vg_laden", "abbrechen"]
|
|
|
|
|
|
Genaue Entscheidung des Nutzers.
|
2026-09-18 11:51:57 +02:00
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
2026-05-05 19:47:25 +02:00
|
|
|
|
"""
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"Mock-Modus: ask_detailpruefung_oder_vg_laden('{title}') → abbrechen")
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
msg = QMessageBox(parent)
|
|
|
|
|
|
msg.setIcon(ICON_QUESTION)
|
|
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
|
|
msg.setText(message)
|
|
|
|
|
|
|
|
|
|
|
|
detail_btn = msg.addButton("Detailprüfung starten", QMessageBox.ButtonRole.AcceptRole)
|
2026-05-22 14:26:58 +02:00
|
|
|
|
vg_btn = msg.addButton("Bodenordnungsobjekt als Verfahrensgebiet laden", QMessageBox.ButtonRole.ActionRole)
|
2026-05-05 19:47:25 +02:00
|
|
|
|
msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
|
|
|
|
|
|
|
|
|
|
|
|
exec_dialog(msg)
|
|
|
|
|
|
|
|
|
|
|
|
clicked = msg.clickedButton()
|
|
|
|
|
|
if clicked == detail_btn:
|
|
|
|
|
|
return "detailpruefung"
|
|
|
|
|
|
elif clicked == vg_btn:
|
|
|
|
|
|
return "als_vg_laden"
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-07-15 15:27:24 +02:00
|
|
|
|
def ask_detailpruefung_oder_abbrechen(
|
|
|
|
|
|
parent: Any,
|
|
|
|
|
|
title: str,
|
|
|
|
|
|
message: str,
|
|
|
|
|
|
) -> Literal["detailpruefung", "abbrechen"]:
|
|
|
|
|
|
"""Zeigt Dialog mit zwei Optionen nach erkannter Flächenabweichung (Shape-Workflow).
|
|
|
|
|
|
|
|
|
|
|
|
Optionen:
|
|
|
|
|
|
- **Detailprüfung starten**: Knickpunktbasierte Prüfung des Shape-VG-Umrings
|
|
|
|
|
|
- **Abbrechen**: Vorgang beenden, Shape-Verfahrensgebiet unverändert behalten
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
parent :
|
|
|
|
|
|
Eltern-Widget oder None.
|
|
|
|
|
|
title : str
|
|
|
|
|
|
Dialog-Titel.
|
|
|
|
|
|
message : str
|
|
|
|
|
|
Hauptmeldung mit Flächen-Informationen.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
Literal["detailpruefung", "abbrechen"]
|
|
|
|
|
|
Genaue Entscheidung des Nutzers.
|
2026-09-18 11:51:57 +02:00
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
2026-07-15 15:27:24 +02:00
|
|
|
|
"""
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
print(f"Mock-Modus: ask_detailpruefung_oder_abbrechen('{title}') → abbrechen")
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
msg = QMessageBox(parent)
|
|
|
|
|
|
msg.setIcon(ICON_QUESTION)
|
|
|
|
|
|
msg.setWindowTitle(title)
|
|
|
|
|
|
msg.setText(message)
|
|
|
|
|
|
|
|
|
|
|
|
detail_btn = msg.addButton("Detailprüfung starten", QMessageBox.ButtonRole.AcceptRole)
|
|
|
|
|
|
msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
|
|
|
|
|
|
|
|
|
|
|
|
exec_dialog(msg)
|
|
|
|
|
|
|
|
|
|
|
|
clicked = msg.clickedButton()
|
|
|
|
|
|
if clicked == detail_btn:
|
|
|
|
|
|
return "detailpruefung"
|
|
|
|
|
|
else:
|
|
|
|
|
|
return "abbrechen"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-12 16:14:02 +01:00
|
|
|
|
class ProgressDialog:
|
|
|
|
|
|
def __init__(self, total: int, title: str = "Fortschritt", label: str = "Verarbeite..."):
|
|
|
|
|
|
self.total = max(total, 1)
|
|
|
|
|
|
self._canceled = False
|
|
|
|
|
|
|
|
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
self.value = 0
|
|
|
|
|
|
self.label = label
|
|
|
|
|
|
self.title = title
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self._dlg = QProgressDialog(label, "Abbrechen", 0, self.total)
|
|
|
|
|
|
self._dlg.setWindowTitle(title)
|
|
|
|
|
|
|
|
|
|
|
|
# Qt5 vs Qt6: WindowModality-Enum unterschiedlich verfügbar
|
|
|
|
|
|
modality = None
|
|
|
|
|
|
if hasattr(Qt, "WindowModality"):
|
|
|
|
|
|
try:
|
|
|
|
|
|
modality = Qt.WindowModality.WindowModal
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
modality = None
|
|
|
|
|
|
if modality is None and hasattr(Qt, "WindowModal"):
|
|
|
|
|
|
modality = Qt.WindowModal
|
|
|
|
|
|
if modality is not None:
|
|
|
|
|
|
try:
|
|
|
|
|
|
self._dlg.setWindowModality(modality)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
self._dlg.setMinimumDuration(0)
|
|
|
|
|
|
self._dlg.setAutoClose(False)
|
|
|
|
|
|
self._dlg.setAutoReset(False)
|
|
|
|
|
|
self._dlg.setValue(0)
|
|
|
|
|
|
|
|
|
|
|
|
def on_cancel():
|
|
|
|
|
|
if self._dlg and self._dlg.value() >= self.total:
|
|
|
|
|
|
# OK-Button am Ende
|
|
|
|
|
|
self._dlg.close()
|
|
|
|
|
|
return
|
|
|
|
|
|
self._canceled = True
|
|
|
|
|
|
self._dlg.close()
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
self._dlg.canceled.connect(on_cancel)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def set_total(self, total: int) -> None:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
self.total = max(total, 1)
|
|
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
self._dlg.setMaximum(self.total)
|
|
|
|
|
|
|
|
|
|
|
|
def set_value(self, value: int) -> None:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
self.value = value
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
self._dlg.setValue(min(value, self.total))
|
|
|
|
|
|
if value >= self.total:
|
|
|
|
|
|
self._dlg.setLabelText("Fertig. Klicken Sie auf OK, um das Fenster zu schließen.")
|
|
|
|
|
|
self._dlg.setCancelButtonText("OK")
|
|
|
|
|
|
QCoreApplication.processEvents()
|
|
|
|
|
|
|
|
|
|
|
|
def set_label(self, text: str) -> None:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
self.label = text
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
self._dlg.setLabelText(text)
|
|
|
|
|
|
QCoreApplication.processEvents()
|
|
|
|
|
|
|
|
|
|
|
|
def is_canceled(self) -> bool:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
return self._canceled
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
return self._canceled or self._dlg.wasCanceled()
|
|
|
|
|
|
|
|
|
|
|
|
return self._canceled
|
|
|
|
|
|
|
|
|
|
|
|
def close(self) -> None:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
self._dlg.close()
|
|
|
|
|
|
|
2026-05-22 08:56:26 +02:00
|
|
|
|
def raise_to_front(self) -> None:
|
|
|
|
|
|
"""Bringt den Fortschrittsdialog in den Vordergrund.
|
|
|
|
|
|
|
|
|
|
|
|
Wird aufgerufen, nachdem ein modaler Subdialog (z. B. Auswahlbox)
|
|
|
|
|
|
geschlossen wurde und der Fortschrittsdialog wieder sichtbar sein soll.
|
2026-09-18 11:51:57 +02:00
|
|
|
|
|
|
|
|
|
|
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
2026-05-22 08:56:26 +02:00
|
|
|
|
"""
|
|
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
try:
|
|
|
|
|
|
self._dlg.raise_()
|
|
|
|
|
|
self._dlg.activateWindow()
|
|
|
|
|
|
QCoreApplication.processEvents()
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-03-12 16:14:02 +01:00
|
|
|
|
|
|
|
|
|
|
def create_progress_dialog(total: int, title: str = "Fortschritt", label: str = "Verarbeite...") -> ProgressDialog:
|
2026-09-18 11:51:57 +02:00
|
|
|
|
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
|
2026-03-12 16:14:02 +01:00
|
|
|
|
return ProgressDialog(total, title, label)
|
|
|
|
|
|
|
2026-10-02 11:57:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ask_mehrfachauswahl(
|
|
|
|
|
|
titel: str,
|
|
|
|
|
|
hinweis: str,
|
|
|
|
|
|
eintraege: list[dict],
|
|
|
|
|
|
vorauswahl: set[str] | list[str],
|
|
|
|
|
|
parent: Any = None,
|
|
|
|
|
|
) -> Optional[list[str]]:
|
|
|
|
|
|
"""Zeigt eine Liste mit Häkchen, in der Einträge an- und abgewählt werden.
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
|
----------
|
|
|
|
|
|
eintraege :
|
|
|
|
|
|
Dicts mit ``schluessel`` und ``text``, optional ``gruppe`` (Zwischenüberschrift; Einträge
|
|
|
|
|
|
einer Gruppe müssen aufeinander folgen) und ``icon`` (z. B. Symbolvorschau).
|
|
|
|
|
|
vorauswahl :
|
|
|
|
|
|
Schlüssel der anfangs angehakten Einträge.
|
|
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
|
-------
|
|
|
|
|
|
list[str] | None
|
|
|
|
|
|
Schlüssel der angehakten Einträge in Listenreihenfolge, ``None`` bei Abbruch.
|
|
|
|
|
|
Im Mock-Modus die Vorauswahl.
|
|
|
|
|
|
"""
|
|
|
|
|
|
vorauswahl = set(vorauswahl)
|
|
|
|
|
|
if QT_VERSION == 0: # Mock-Modus
|
|
|
|
|
|
return [e["schluessel"] for e in eintraege if e["schluessel"] in vorauswahl]
|
|
|
|
|
|
|
|
|
|
|
|
from sn_basis.functions.qt_wrapper import (
|
|
|
|
|
|
QDialog, QDialogButtonBox, QFont, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
|
|
|
|
|
|
QVBoxLayout, DialogAccepted, ItemIsEnabled, ItemIsUserCheckable, CheckStateChecked,
|
|
|
|
|
|
CheckStateUnchecked, UserRole,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
dialog = QDialog(parent)
|
|
|
|
|
|
dialog.setWindowTitle(titel)
|
|
|
|
|
|
layout = QVBoxLayout(dialog)
|
|
|
|
|
|
|
|
|
|
|
|
beschriftung = QLabel(hinweis)
|
|
|
|
|
|
beschriftung.setWordWrap(True)
|
|
|
|
|
|
layout.addWidget(beschriftung)
|
|
|
|
|
|
|
|
|
|
|
|
suche = QLineEdit()
|
|
|
|
|
|
suche.setPlaceholderText("Suche …")
|
|
|
|
|
|
layout.addWidget(suche)
|
|
|
|
|
|
|
|
|
|
|
|
liste = QListWidget()
|
|
|
|
|
|
kopf_schrift = QFont()
|
|
|
|
|
|
kopf_schrift.setBold(True)
|
|
|
|
|
|
zeilen: list[tuple[Any, dict]] = []
|
|
|
|
|
|
koepfe: dict[str, Any] = {}
|
|
|
|
|
|
letzte_gruppe = None
|
|
|
|
|
|
for eintrag in eintraege:
|
|
|
|
|
|
gruppe = eintrag.get("gruppe")
|
|
|
|
|
|
if gruppe and gruppe != letzte_gruppe:
|
|
|
|
|
|
kopf = QListWidgetItem(gruppe)
|
|
|
|
|
|
kopf.setFlags(ItemIsEnabled)
|
|
|
|
|
|
kopf.setFont(kopf_schrift)
|
|
|
|
|
|
liste.addItem(kopf)
|
|
|
|
|
|
koepfe[gruppe] = kopf
|
|
|
|
|
|
letzte_gruppe = gruppe
|
|
|
|
|
|
zeile = QListWidgetItem(eintrag["text"])
|
|
|
|
|
|
if eintrag.get("icon") is not None:
|
|
|
|
|
|
zeile.setIcon(eintrag["icon"])
|
|
|
|
|
|
zeile.setFlags(ItemIsUserCheckable | ItemIsEnabled)
|
|
|
|
|
|
zeile.setCheckState(CheckStateChecked if eintrag["schluessel"] in vorauswahl else CheckStateUnchecked)
|
|
|
|
|
|
zeile.setData(UserRole, eintrag["schluessel"])
|
|
|
|
|
|
liste.addItem(zeile)
|
|
|
|
|
|
zeilen.append((zeile, eintrag))
|
|
|
|
|
|
layout.addWidget(liste, 1)
|
|
|
|
|
|
|
|
|
|
|
|
def _filtern(text: str) -> None:
|
|
|
|
|
|
suchtext = text.strip().lower()
|
|
|
|
|
|
sichtbare_gruppen = set()
|
|
|
|
|
|
for zeile, eintrag in zeilen:
|
|
|
|
|
|
treffer = not suchtext or suchtext in eintrag["text"].lower() \
|
|
|
|
|
|
or suchtext in (eintrag.get("gruppe") or "").lower()
|
|
|
|
|
|
zeile.setHidden(not treffer)
|
|
|
|
|
|
if treffer:
|
|
|
|
|
|
sichtbare_gruppen.add(eintrag.get("gruppe"))
|
|
|
|
|
|
for gruppe, kopf in koepfe.items():
|
|
|
|
|
|
kopf.setHidden(gruppe not in sichtbare_gruppen)
|
|
|
|
|
|
|
|
|
|
|
|
def _alle_setzen(zustand: Any) -> None:
|
|
|
|
|
|
for zeile, _ in zeilen:
|
|
|
|
|
|
if not zeile.isHidden():
|
|
|
|
|
|
zeile.setCheckState(zustand)
|
|
|
|
|
|
|
|
|
|
|
|
suche.textChanged.connect(_filtern)
|
|
|
|
|
|
|
|
|
|
|
|
knopfzeile = QHBoxLayout()
|
|
|
|
|
|
alle = QPushButton("Alle auswählen")
|
|
|
|
|
|
alle.clicked.connect(lambda *_: _alle_setzen(CheckStateChecked))
|
|
|
|
|
|
keine = QPushButton("Keine auswählen")
|
|
|
|
|
|
keine.clicked.connect(lambda *_: _alle_setzen(CheckStateUnchecked))
|
|
|
|
|
|
knopfzeile.addWidget(alle)
|
|
|
|
|
|
knopfzeile.addWidget(keine)
|
|
|
|
|
|
knopfzeile.addStretch(1)
|
|
|
|
|
|
layout.addLayout(knopfzeile)
|
|
|
|
|
|
|
|
|
|
|
|
knoepfe = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
|
|
|
|
|
knoepfe.accepted.connect(dialog.accept)
|
|
|
|
|
|
knoepfe.rejected.connect(dialog.reject)
|
|
|
|
|
|
layout.addWidget(knoepfe)
|
|
|
|
|
|
dialog.resize(460, 560)
|
|
|
|
|
|
|
|
|
|
|
|
if exec_dialog(dialog) != DialogAccepted:
|
|
|
|
|
|
return None
|
|
|
|
|
|
return [eintrag["schluessel"] for zeile, eintrag in zeilen if zeile.checkState() == CheckStateChecked]
|