qt_wrapper, dialog;wrapper, Pruef_ergebnis und Pruefmanager überarbeitet, so dass die Übergaben jetzt stimmen. Nutzerabfragen werden tatsächlich ausgelöst- Nutzerabfrage Datei überschreiebn... ist noch Blödsinn

This commit is contained in:
2026-03-04 15:32:49 +01:00
parent f8be65f6f6
commit 3b56725e4f
7 changed files with 756 additions and 1053 deletions

View File

@@ -1,90 +1,95 @@
"""
sn_basis/functions/qt_wrapper.py zentrale Qt-Abstraktion (PyQt5 / PyQt6 / Mock)
sn_basis/functions/qt_wrapper.py zentrale Qt-Abstraktion (PyQt6 primär / PyQt5 Fallback / Mock)
"""
from typing import Optional, Type, Any
# ---------------------------------------------------------
# Qt-Symbole (werden dynamisch gesetzt)
# ---------------------------------------------------------
QDockWidget: Type[Any]
QMessageBox: Type[Any]
QFileDialog: Type[Any]
QEventLoop: Type[Any]
QUrl: Type[Any]
QNetworkRequest: Type[Any]
QNetworkReply: Type[Any]
QCoreApplication: Type[Any]
QWidget: Type[Any]
QGridLayout: Type[Any]
QLabel: Type[Any]
QLineEdit: Type[Any]
QGroupBox: Type[Any]
QVBoxLayout: Type[Any]
QPushButton: Type[Any]
QAction: Type[Any]
QMenu: Type[Any]
QToolBar: Type[Any]
QActionGroup: Type[Any]
QTabWidget: type
QToolButton: Type[Any]
QSizePolicy: Type[Any]
Qt: Type[Any]
QComboBox: Type[Any]
from typing import Optional, Type, Any, Callable
# Globale Qt-Symbole (werden dynamisch gesetzt)
QT_VERSION = 0 # 0 = Mock, 5 = PyQt5, 6 = PyQt6
YES: Optional[Any] = None
NO: Optional[Any] = None
CANCEL: Optional[Any] = None
ICON_QUESTION: Optional[Any] = None
QT_VERSION = 0 # 0 = Mock, 5 = PyQt5, 6 = PyQt6
# Qt-Klassen (werden dynamisch gesetzt)
QDockWidget: Type[Any] = object
QMessageBox: Type[Any] = object
QFileDialog: Type[Any] = object
QEventLoop: Type[Any] = object
QUrl: Type[Any] = object
QNetworkRequest: Type[Any] = object
QNetworkReply: Type[Any] = object
QCoreApplication: Type[Any] = object
QWidget: Type[Any] = object
QGridLayout: Type[Any] = object
QLabel: Type[Any] = object
QLineEdit: Type[Any] = object
QGroupBox: Type[Any] = object
QVBoxLayout: Type[Any] = object
QPushButton: Type[Any] = object
QAction: Type[Any] = object
QMenu: Type[Any] = object
QToolBar: Type[Any] = object
QActionGroup: Type[Any] = object
QTabWidget: Type[Any] = object
QToolButton: Type[Any] = object
QSizePolicy: Type[Any] = object
Qt: Type[Any] = object
QComboBox: Type[Any] = object
def exec_dialog(dialog: Any) -> Any:
raise NotImplementedError
"""Führt Dialog modal aus (Qt6: exec(), Qt5: exec_(), Mock: YES)"""
raise NotImplementedError("Qt nicht initialisiert")
def debug_qt_status() -> None:
"""Debug: Zeigt Qt-Status für Troubleshooting."""
print(f"🔍 QT_VERSION: {QT_VERSION}")
print(f"🔍 QMessageBox Typ: {getattr(QMessageBox, '__name__', type(QMessageBox).__name__)}")
print(f"🔍 YES Wert: {YES} (Typ: {type(YES) if YES is not None else 'None'})")
if QT_VERSION == 0:
print("❌ MOCK-MODUS AKTIV! Keine Dialoge möglich!")
elif QT_VERSION == 5:
print("✅ PyQt5 geladen (Fallback) Dialoge sollten funktionieren!")
elif QT_VERSION == 6:
print("✅ PyQt6 geladen (primär) Dialoge sollten funktionieren!")
else:
print("❓ Unbekannte Qt-Version!")
# ---------------------------------------------------------
# Versuch: PyQt6
# ---------------------------------------------------------
# --------------------------- PYQT6 PRIMÄR ---------------------------
try:
from qgis.PyQt.QtWidgets import ( # type: ignore
QMessageBox as _QMessageBox,# type: ignore
QFileDialog as _QFileDialog,# type: ignore
QWidget as _QWidget,# type: ignore
QGridLayout as _QGridLayout,# type: ignore
QLabel as _QLabel,# type: ignore
QLineEdit as _QLineEdit,# type: ignore
QGroupBox as _QGroupBox,# type: ignore
QVBoxLayout as _QVBoxLayout,# type: ignore
QPushButton as _QPushButton,# type: ignore
QAction as _QAction,
QMenu as _QMenu,# type: ignore
QToolBar as _QToolBar,# type: ignore
QActionGroup as _QActionGroup,# type: ignore
QDockWidget as _QDockWidget,# type: ignore
QTabWidget as _QTabWidget,# type: ignore
QToolButton as _QToolButton,#type:ignore
QSizePolicy as _QSizePolicy,#type:ignore
QComboBox as _QComboBox,
)
from qgis.PyQt.QtCore import ( # type: ignore
QEventLoop as _QEventLoop,# type: ignore
QUrl as _QUrl,# type: ignore
QCoreApplication as _QCoreApplication,# type: ignore
Qt as _Qt#type:ignore
from qgis.PyQt.QtWidgets import (
QMessageBox as _QMessageBox,
QFileDialog as _QFileDialog,
QWidget as _QWidget,
QGridLayout as _QGridLayout,
QLabel as _QLabel,
QLineEdit as _QLineEdit,
QGroupBox as _QGroupBox,
QVBoxLayout as _QVBoxLayout,
QPushButton as _QPushButton,
QAction as _QAction,
QMenu as _QMenu,
QToolBar as _QToolBar,
QActionGroup as _QActionGroup,
QDockWidget as _QDockWidget,
QTabWidget as _QTabWidget,
QToolButton as _QToolButton,
QSizePolicy as _QSizePolicy,
QComboBox as _QComboBox,
)
from qgis.PyQt.QtNetwork import ( # type: ignore
QNetworkRequest as _QNetworkRequest,# type: ignore
QNetworkReply as _QNetworkReply,# type: ignore
from qgis.PyQt.QtCore import (
QEventLoop as _QEventLoop,
QUrl as _QUrl,
QCoreApplication as _QCoreApplication,
Qt as _Qt,
)
from qgis.PyQt.QtNetwork import (
QNetworkRequest as _QNetworkRequest,
QNetworkReply as _QNetworkReply,
)
# ✅ ALLE GLOBALS ZUWEISEN
QT_VERSION = 6
QMessageBox = _QMessageBox
QFileDialog = _QFileDialog
@@ -93,7 +98,7 @@ try:
QNetworkRequest = _QNetworkRequest
QNetworkReply = _QNetworkReply
QCoreApplication = _QCoreApplication
Qt=_Qt
Qt = _Qt
QDockWidget = _QDockWidget
QWidget = _QWidget
QGridLayout = _QGridLayout
@@ -107,51 +112,37 @@ try:
QToolBar = _QToolBar
QActionGroup = _QActionGroup
QTabWidget = _QTabWidget
QToolButton=_QToolButton
QSizePolicy=_QSizePolicy
QComboBox=_QComboBox
QToolButton = _QToolButton
QSizePolicy = _QSizePolicy
QComboBox = _QComboBox
# ✅ QT6 ENUMS
YES = QMessageBox.StandardButton.Yes
NO = QMessageBox.StandardButton.No
CANCEL = QMessageBox.StandardButton.Cancel
ICON_QUESTION = QMessageBox.Icon.Question
# ---------------------------------------------------------
# Qt6 Enum-Aliase (vereinheitlicht)
# ---------------------------------------------------------
# Qt6 Enum-Aliase
ToolButtonTextBesideIcon = Qt.ToolButtonStyle.ToolButtonTextBesideIcon
ArrowDown = Qt.ArrowType.DownArrow
ArrowRight = Qt.ArrowType.RightArrow
# QSizePolicy Enum-Aliase (Qt6)
SizePolicyPreferred = QSizePolicy.Policy.Preferred
SizePolicyMaximum = QSizePolicy.Policy.Maximum
# ---------------------------------------------------------
# QDockWidget Feature-Aliase (Qt6)
# ---------------------------------------------------------
DockWidgetMovable = QDockWidget.DockWidgetFeature.DockWidgetMovable
DockWidgetFloatable = QDockWidget.DockWidgetFeature.DockWidgetFloatable
DockWidgetClosable = QDockWidget.DockWidgetFeature.DockWidgetClosable
# ---------------------------------------------------------
# Dock-Area-Aliase (Qt6)
# ---------------------------------------------------------
DockAreaLeft = Qt.DockWidgetArea.LeftDockWidgetArea
DockAreaRight = Qt.DockWidgetArea.RightDockWidgetArea
def exec_dialog(dialog: Any) -> Any:
return dialog.exec()
print(f"✅ qt_wrapper: PyQt6 geladen (QT_VERSION={QT_VERSION})")
# ---------------------------------------------------------
# Versuch: PyQt5
# ---------------------------------------------------------
except Exception:
# --------------------------- PYQT5 FALLBACK ---------------------------
except (ImportError, AttributeError):
try:
from PyQt5.QtWidgets import (# type: ignore
from PyQt5.QtWidgets import (
QMessageBox as _QMessageBox,
QFileDialog as _QFileDialog,
QWidget as _QWidget,
@@ -171,18 +162,19 @@ except Exception:
QSizePolicy as _QSizePolicy,
QComboBox as _QComboBox,
)
from PyQt5.QtCore import (# type: ignore
from PyQt5.QtCore import (
QEventLoop as _QEventLoop,
QUrl as _QUrl,
QCoreApplication as _QCoreApplication,
Qt as _Qt,
)
from PyQt5.QtNetwork import (# type: ignore
from PyQt5.QtNetwork import (
QNetworkRequest as _QNetworkRequest,
QNetworkReply as _QNetworkReply,
)
# ✅ ALLE GLOBALS ZUWEISEN
QT_VERSION = 5
QMessageBox = _QMessageBox
QFileDialog = _QFileDialog
QEventLoop = _QEventLoop
@@ -190,10 +182,8 @@ except Exception:
QNetworkRequest = _QNetworkRequest
QNetworkReply = _QNetworkReply
QCoreApplication = _QCoreApplication
Qt=_Qt
Qt = _Qt
QDockWidget = _QDockWidget
QWidget = _QWidget
QGridLayout = _QGridLayout
QLabel = _QLabel
@@ -206,55 +196,41 @@ except Exception:
QToolBar = _QToolBar
QActionGroup = _QActionGroup
QTabWidget = _QTabWidget
QToolButton=_QToolButton
QSizePolicy=_QSizePolicy
ComboBox=_QComboBox
QToolButton = _QToolButton
QSizePolicy = _QSizePolicy
QComboBox = _QComboBox
# ✅ PYQT5 ENUMS
YES = QMessageBox.Yes
NO = QMessageBox.No
CANCEL = QMessageBox.Cancel
ICON_QUESTION = QMessageBox.Question
QT_VERSION = 5
# then try next backend
# ---------------------------------------------------------
# Qt5 Enum-Aliase (vereinheitlicht)
# ---------------------------------------------------------
# PyQt5 Enum-Aliase
ToolButtonTextBesideIcon = Qt.ToolButtonTextBesideIcon
ArrowDown = Qt.DownArrow
ArrowRight = Qt.RightArrow
# QSizePolicy Enum-Aliase (Qt5)
SizePolicyPreferred = QSizePolicy.Preferred
SizePolicyMaximum = QSizePolicy.Maximum
# ---------------------------------------------------------
# QDockWidget Feature-Aliase (Qt5)
# ---------------------------------------------------------
DockWidgetMovable = QDockWidget.DockWidgetMovable
DockWidgetFloatable = QDockWidget.DockWidgetFloatable
DockWidgetClosable = QDockWidget.DockWidgetClosable
# ---------------------------------------------------------
# Dock-Area-Aliase (Qt5)
# ---------------------------------------------------------
DockAreaLeft = Qt.LeftDockWidgetArea
DockAreaRight = Qt.RightDockWidgetArea
def exec_dialog(dialog: Any) -> Any:
return dialog.exec_()
print(f"✅ qt_wrapper: PyQt5 Fallback geladen (QT_VERSION={QT_VERSION})")
# ---------------------------------------------------------
# Mock-Modus
# ---------------------------------------------------------
# --------------------------- MOCK-MODUS ---------------------------
except Exception:
QT_VERSION = 0
print("⚠️ qt_wrapper: Mock-Modus aktiviert (QT_VERSION=0)")
# Fake Enum für Bit-Operationen
class FakeEnum(int):
def __or__(self, other: int) -> "FakeEnum":
def __or__(self, other: Any) -> "FakeEnum":
return FakeEnum(int(self) | int(other))
YES = FakeEnum(1)
@@ -262,103 +238,72 @@ except Exception:
CANCEL = FakeEnum(4)
ICON_QUESTION = FakeEnum(8)
# Im Mock-Block von qt_wrapper.py:
class _MockQMessageBox:
Yes = YES
No = NO
Cancel = CANCEL
Question = ICON_QUESTION
@classmethod
def question(cls, parent, title, message, buttons, default_button):
"""Mock: Gibt immer default_button zurück"""
print(f"🔍 Mock QMessageBox.question: '{title}'{default_button}")
return default_button
QMessageBox = _MockQMessageBox
class _MockQFileDialog:
@staticmethod
def getOpenFileName(*args, **kwargs):
return ("", "")
def getOpenFileName(*args, **kwargs): return ("", "")
@staticmethod
def getSaveFileName(*args, **kwargs):
return ("", "")
def getSaveFileName(*args, **kwargs): return ("", "")
QFileDialog = _MockQFileDialog
class _MockQEventLoop:
def exec(self) -> int:
return 0
def quit(self) -> None:
pass
def exec(self) -> int: return 0
def quit(self) -> None: pass
QEventLoop = _MockQEventLoop
class _MockQUrl(str):
def isValid(self) -> bool:
return True
def isValid(self) -> bool: return True
QUrl = _MockQUrl
class _MockQNetworkRequest:
def __init__(self, url: Any):
self.url = url
def __init__(self, url: Any): self.url = url
QNetworkRequest = _MockQNetworkRequest
class _MockQNetworkReply:
def error(self) -> int:
return 0
def errorString(self) -> str:
return ""
def readAll(self) -> bytes:
return b""
def deleteLater(self) -> None:
pass
def error(self) -> int: return 0
def errorString(self) -> str: return ""
def readAll(self) -> bytes: return b""
def deleteLater(self) -> None: pass
QNetworkReply = _MockQNetworkReply
class _MockWidget:
def __init__(self, *args, **kwargs):
pass
class _MockWidget: pass
class _MockLayout:
def __init__(self, *args, **kwargs):
self._widgets = []
def addWidget(self, widget):
self._widgets.append(widget)
def addLayout(self, layout):
pass
def addStretch(self, *args, **kwargs):
pass
def setSpacing(self, *args, **kwargs):
pass
def setContentsMargins(self, *args, **kwargs):
pass
class _MockLabel:
def __init__(self, text: str = ""):
self._text = text
def __init__(self, *args, **kwargs): self._widgets = []
def addWidget(self, widget): self._widgets.append(widget)
def addLayout(self, layout): pass
def addStretch(self, *args, **kwargs): pass
def setSpacing(self, *args, **kwargs): pass
def setContentsMargins(self, *args, **kwargs): pass
class _MockLabel:
def __init__(self, text: str = ""): self._text = text
class _MockLineEdit:
def __init__(self, *args, **kwargs):
self._text = ""
def __init__(self, *args, **kwargs): self._text = ""
def text(self) -> str: return self._text
def setText(self, value: str) -> None: self._text = value
def text(self) -> str:
return self._text
def setText(self, value: str) -> None:
self._text = value
class _MockButton:
def __init__(self, *args, **kwargs):
self.clicked = lambda *a, **k: None
class _MockButton:
def __init__(self, *args, **kwargs): self.clicked = lambda *a, **k: None
QWidget = _MockWidget
QGridLayout = _MockLayout
@@ -367,101 +312,61 @@ except Exception:
QGroupBox = _MockWidget
QVBoxLayout = _MockLayout
QPushButton = _MockButton
class _MockQCoreApplication:
pass
QCoreApplication = _MockQCoreApplication
QCoreApplication = object()
class _MockQt:
# ToolButtonStyle
ToolButtonTextBesideIcon = 0
# ArrowType
ArrowDown = 1
ArrowRight = 2
LeftDockWidgetArea = 1
RightDockWidgetArea = 2
Qt=_MockQt
Qt = _MockQt()
ToolButtonTextBesideIcon = Qt.ToolButtonTextBesideIcon
ArrowDown = Qt.ArrowDown
ArrowRight = Qt.ArrowRight
DockAreaLeft = Qt.LeftDockWidgetArea
DockAreaRight = Qt.RightDockWidgetArea
class _MockQDockWidget(_MockWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._object_name = ""
def setObjectName(self, name: str) -> None: self._object_name = name
def objectName(self) -> str: return self._object_name
def show(self) -> None: pass
def deleteLater(self) -> None: pass
def setObjectName(self, name: str) -> None:
self._object_name = name
def objectName(self) -> str:
return self._object_name
def show(self) -> None:
pass
def deleteLater(self) -> None:
pass
QDockWidget = _MockQDockWidget
class _MockAction:
def __init__(self, *args, **kwargs):
self._checked = False
self.triggered = lambda *a, **k: None
def setToolTip(self, text: str) -> None:
pass
def setCheckable(self, value: bool) -> None:
pass
def setChecked(self, value: bool) -> None:
self._checked = value
def setToolTip(self, text: str) -> None: pass
def setCheckable(self, value: bool) -> None: pass
def setChecked(self, value: bool) -> None: self._checked = value
class _MockMenu:
def __init__(self, *args, **kwargs):
self._actions = []
def addAction(self, action):
self._actions.append(action)
def removeAction(self, action):
if action in self._actions:
self._actions.remove(action)
def clear(self):
self._actions.clear()
def menuAction(self):
return self
def __init__(self, *args, **kwargs): self._actions = []
def addAction(self, action): self._actions.append(action)
def removeAction(self, action):
if action in self._actions: self._actions.remove(action)
def clear(self): self._actions.clear()
def menuAction(self): return self
class _MockToolBar:
def __init__(self, *args, **kwargs):
self._actions = []
def setObjectName(self, name: str) -> None:
pass
def addAction(self, action):
self._actions.append(action)
def removeAction(self, action):
if action in self._actions:
self._actions.remove(action)
def clear(self):
self._actions.clear()
def __init__(self, *args, **kwargs): self._actions = []
def setObjectName(self, name: str) -> None: pass
def addAction(self, action): self._actions.append(action)
def removeAction(self, action):
if action in self._actions: self._actions.remove(action)
def clear(self): self._actions.clear()
class _MockActionGroup:
def __init__(self, *args, **kwargs):
self._actions = []
def __init__(self, *args, **kwargs): self._actions = []
def setExclusive(self, value: bool) -> None: pass
def addAction(self, action): self._actions.append(action)
def setExclusive(self, value: bool) -> None:
pass
def addAction(self, action):
self._actions.append(action)
QAction = _MockAction
QMenu = _MockMenu
QToolBar = _MockToolBar
@@ -469,112 +374,58 @@ except Exception:
class _MockToolButton(_MockWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._checked = False
self.toggled = lambda *a, **k: None
def setText(self, text: str) -> None: pass
def setCheckable(self, value: bool) -> None: pass
def setChecked(self, value: bool) -> None: self._checked = value
def setToolButtonStyle(self, *args, **kwargs): pass
def setArrowType(self, *args, **kwargs): pass
def setStyleSheet(self, *args, **kwargs): pass
def setText(self, text: str) -> None:
pass
def setCheckable(self, value: bool) -> None:
pass
def setChecked(self, value: bool) -> None:
self._checked = value
def setToolButtonStyle(self, *args, **kwargs):
pass
def setArrowType(self, *args, **kwargs):
pass
def setStyleSheet(self, *args, **kwargs):
pass
QToolButton=_MockToolButton
QToolButton = _MockToolButton
class _MockQSizePolicy:
# horizontale Policies
Fixed = 0
Minimum = 1
Maximum = 2
Preferred = 3
Expanding = 4
MinimumExpanding = 5
Ignored = 6
Maximum = 2
# vertikale Policies (Qt nutzt dieselben Werte)
def __init__(self, horizontal=None, vertical=None):
self.horizontal = horizontal
self.vertical = vertical
QSizePolicy=_MockQSizePolicy
QSizePolicy = _MockQSizePolicy
SizePolicyPreferred = QSizePolicy.Preferred
SizePolicyMaximum = QSizePolicy.Maximum
DockWidgetMovable = 1
DockWidgetFloatable = 2
DockWidgetClosable = 4
DockAreaLeft = 1
DockAreaRight = 2
def exec_dialog(dialog: Any) -> Any:
return YES
class _MockTabWidget:
def __init__(self, *args, **kwargs):
self._tabs = []
def __init__(self, *args, **kwargs): self._tabs = []
def addTab(self, widget, title: str): self._tabs.append((widget, title))
def addTab(self, widget, title: str):
self._tabs.append((widget, title))
QTabWidget = _MockTabWidget
# -------------------------
# Mock ComboBox Implementation
# -------------------------
class _MockSignal:
def __init__(self):
self._slots = []
def connect(self, cb):
self._slots.append(cb)
def emit(self, value):
for s in list(self._slots):
try:
s(value)
except Exception:
pass
class _MockComboBox:
def __init__(self, parent=None):
self._items = []
self._index = -1
self.currentTextChanged = _MockSignal()
def addItem(self, text: str) -> None:
self._items.append(text)
def addItems(self, items):
for it in items:
self.addItem(it)
def findText(self, text: str) -> int:
try:
return self._items.index(text)
except ValueError:
return -1
self.currentTextChanged = type('Signal', (), {'connect': lambda s, cb: None, 'emit': lambda s, v: None})()
def addItem(self, text: str) -> None: self._items.append(text)
def addItems(self, items): [self.addItem(it) for it in items]
def findText(self, text: str) -> int:
return self._items.index(text) if text in self._items else -1
def setCurrentIndex(self, idx: int) -> None:
if 0 <= idx < len(self._items):
self._index = idx
self.currentTextChanged.emit(self.currentText())
def setCurrentText(self, text: str) -> None:
idx = self.findText(text)
if idx >= 0:
self.setCurrentIndex(idx)
if idx >= 0: self.setCurrentIndex(idx)
def currentText(self) -> str:
if 0 <= self._index < len(self._items):
return self._items[self._index]
return ""
return self._items[self._index] if 0 <= self._index < len(self._items) else ""
ComboBox = _MockComboBox
QComboBox = _MockComboBox
def exec_dialog(dialog: Any) -> Any:
return YES
# --------------------------- TEST ---------------------------
if __name__ == "__main__":
debug_qt_status()