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-12 16:14:02 +01:00
|
|
|
|
QProgressDialog, QCoreApplication, Qt,
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
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:
|
|
|
|
|
|
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:
|
|
|
|
|
|
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:
|
|
|
|
|
|
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:
|
|
|
|
|
|
if QT_VERSION == 0:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self._dlg is not None:
|
|
|
|
|
|
self._dlg.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_progress_dialog(total: int, title: str = "Fortschritt", label: str = "Verarbeite...") -> ProgressDialog:
|
|
|
|
|
|
return ProgressDialog(total, title, label)
|
|
|
|
|
|
|