""" sn_basis/functions/qt_wrapper.py – zentrale Qt-Abstraktion (PyQt6 primär / PyQt5 Fallback / Mock) """ 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 QVariant: Type[Any] = object # Qt-Klassen (werden dynamisch gesetzt) QDockWidget: Type[Any] = object QMessageBox: Type[Any] = object QFileDialog: Type[Any] = object QProgressDialog: Type[Any] = object QEventLoop: Type[Any] = object QTimer: 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 QInputDialog: 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 QCheckBox: Type[Any] = object QHBoxLayout: Type[Any] = object QFont: Type[Any] = object def exec_dialog(dialog: Any) -> Any: """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!") # --------------------------- PYQT6 PRIMÄR --------------------------- try: from qgis.PyQt.QtWidgets import ( QMessageBox as _QMessageBox, QFileDialog as _QFileDialog, QProgressDialog as _QProgressDialog, QWidget as _QWidget, QGridLayout as _QGridLayout, QLabel as _QLabel, QLineEdit as _QLineEdit, QInputDialog as _QInputDialog, 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, QCheckBox as _QCheckBox, QHBoxLayout as _QHBoxLayout, ) from qgis.PyQt.QtGui import QFont as _QFont from qgis.PyQt.QtCore import ( QEventLoop as _QEventLoop, QTimer as _QTimer, QUrl as _QUrl, QCoreApplication as _QCoreApplication, Qt as _Qt, QVariant as _QVariant ) from qgis.PyQt.QtNetwork import ( QNetworkRequest as _QNetworkRequest, QNetworkReply as _QNetworkReply, ) # ✅ ALLE GLOBALS ZUWEISEN QT_VERSION = 6 QMessageBox = _QMessageBox QFileDialog = _QFileDialog QProgressDialog = _QProgressDialog QProgressDialog = _QProgressDialog QEventLoop = _QEventLoop QTimer = _QTimer QUrl = _QUrl QNetworkRequest = _QNetworkRequest QNetworkReply = _QNetworkReply QCoreApplication = _QCoreApplication Qt = _Qt QDockWidget = _QDockWidget QWidget = _QWidget QGridLayout = _QGridLayout QLabel = _QLabel QLineEdit = _QLineEdit QInputDialog = _QInputDialog QGroupBox = _QGroupBox QVBoxLayout = _QVBoxLayout QPushButton = _QPushButton QAction = _QAction QMenu = _QMenu QToolBar = _QToolBar QActionGroup = _QActionGroup QTabWidget = _QTabWidget QToolButton = _QToolButton QSizePolicy = _QSizePolicy QComboBox = _QComboBox QCheckBox = _QCheckBox QVariant = _QVariant QHBoxLayout = _QHBoxLayout QFont = _QFont # ✅ QT6 ENUMS YES = QMessageBox.StandardButton.Yes NO = QMessageBox.StandardButton.No CANCEL = QMessageBox.StandardButton.Cancel ICON_QUESTION = QMessageBox.Icon.Question AcceptRole = QMessageBox.ButtonRole.AcceptRole ActionRole = QMessageBox.ButtonRole.ActionRole RejectRole = QMessageBox.ButtonRole.RejectRole # Qt6 Enum-Aliase ToolButtonTextBesideIcon = Qt.ToolButtonStyle.ToolButtonTextBesideIcon ArrowDown = Qt.ArrowType.DownArrow ArrowRight = Qt.ArrowType.RightArrow SizePolicyPreferred = QSizePolicy.Policy.Preferred SizePolicyMaximum = QSizePolicy.Policy.Maximum DockWidgetMovable = QDockWidget.DockWidgetFeature.DockWidgetMovable DockWidgetFloatable = QDockWidget.DockWidgetFeature.DockWidgetFloatable DockWidgetClosable = QDockWidget.DockWidgetFeature.DockWidgetClosable 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})") # --------------------------- PYQT5 FALLBACK --------------------------- except (ImportError, AttributeError): try: from PyQt5.QtWidgets import ( QMessageBox as _QMessageBox, QFileDialog as _QFileDialog, QWidget as _QWidget, QGridLayout as _QGridLayout, QLabel as _QLabel, QLineEdit as _QLineEdit, QInputDialog as _QInputDialog, 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, QCheckBox as _QCheckBox, QHBoxLayout as _QHBoxLayout, ) from PyQt5.QtGui import QFont as _QFont from PyQt5.QtCore import ( QEventLoop as _QEventLoop, QTimer as _QTimer, QUrl as _QUrl, QCoreApplication as _QCoreApplication, Qt as _Qt, QVariant as _QVariant ) from PyQt5.QtNetwork import ( QNetworkRequest as _QNetworkRequest, QNetworkReply as _QNetworkReply, ) # ✅ ALLE GLOBALS ZUWEISEN QT_VERSION = 5 QMessageBox = _QMessageBox QFileDialog = _QFileDialog QEventLoop = _QEventLoop QTimer = _QTimer QUrl = _QUrl QNetworkRequest = _QNetworkRequest QNetworkReply = _QNetworkReply QCoreApplication = _QCoreApplication Qt = _Qt QDockWidget = _QDockWidget QWidget = _QWidget QGridLayout = _QGridLayout QLabel = _QLabel QLineEdit = _QLineEdit QInputDialog = _QInputDialog QGroupBox = _QGroupBox QVBoxLayout = _QVBoxLayout QPushButton = _QPushButton QAction = _QAction QMenu = _QMenu QToolBar = _QToolBar QActionGroup = _QActionGroup QTabWidget = _QTabWidget QToolButton = _QToolButton QSizePolicy = _QSizePolicy QComboBox = _QComboBox QCheckBox = _QCheckBox QVariant = _QVariant QHBoxLayout= _QHBoxLayout QFont = _QFont # ✅ PYQT5 ENUMS YES = QMessageBox.Yes NO = QMessageBox.No CANCEL = QMessageBox.Cancel ICON_QUESTION = QMessageBox.Question AcceptRole = QMessageBox.AcceptRole ActionRole = QMessageBox.ActionRole RejectRole = QMessageBox.RejectRole # PyQt5 Enum-Aliase ToolButtonTextBesideIcon = Qt.ToolButtonTextBesideIcon ArrowDown = Qt.DownArrow ArrowRight = Qt.RightArrow SizePolicyPreferred = QSizePolicy.Preferred SizePolicyMaximum = QSizePolicy.Maximum DockWidgetMovable = QDockWidget.DockWidgetMovable DockWidgetFloatable = QDockWidget.DockWidgetFloatable DockWidgetClosable = QDockWidget.DockWidgetClosable 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 --------------------------- 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: Any) -> "FakeEnum": return FakeEnum(int(self) | int(other)) YES = FakeEnum(1) NO = FakeEnum(2) 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 AcceptRole = 0 ActionRole = 3 RejectRole = 1 @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 ("", "") @staticmethod def getSaveFileName(*args, **kwargs): return ("", "") QFileDialog = _MockQFileDialog class _MockQInputDialog: @staticmethod def getText(parent, title, label, mode=None, text=""): return text, True QInputDialog = _MockQInputDialog class _MockQEventLoop: def exec(self) -> int: return 0 def quit(self) -> None: pass QEventLoop = _MockQEventLoop class _MockQTimer: def __init__(self, *args, **kwargs): self.timeout = type('Signal', (), { 'connect': lambda s, cb: None, })() def setSingleShot(self, v: bool) -> None: pass def start(self, ms: int) -> None: pass def stop(self) -> None: pass QTimer = _MockQTimer class _MockQUrl(str): def isValid(self) -> bool: return True QUrl = _MockQUrl class _MockQNetworkRequest: 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 QNetworkReply = _MockQNetworkReply 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 class _MockLineEdit: Normal = 0 def __init__(self, *args, **kwargs): self._text = "" def text(self) -> str: return self._text def setText(self, value: str) -> None: self._text = value class _MockFont: def __init__(self, family: str = "", pointSize: int = 10): self.family = family self.pointSize = pointSize class _MockButton: def __init__(self, *args, **kwargs): self.clicked = lambda *a, **k: None QWidget = _MockWidget QGridLayout = _MockLayout QLabel = _MockLabel QLineEdit = _MockLineEdit QGroupBox = _MockWidget QVBoxLayout = _MockLayout QPushButton = _MockButton QFont = _MockFont QCoreApplication = object() class _MockQt: ToolButtonTextBesideIcon = 0 ArrowDown = 1 ArrowRight = 2 LeftDockWidgetArea = 1 RightDockWidgetArea = 2 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): 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 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 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 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() class _MockActionGroup: def __init__(self, *args, **kwargs): self._actions = [] def setExclusive(self, value: bool) -> None: pass def addAction(self, action): self._actions.append(action) QAction = _MockAction QMenu = _MockMenu QToolBar = _MockToolBar QActionGroup = _MockActionGroup class _MockToolButton(_MockWidget): def __init__(self, *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 QToolButton = _MockToolButton class _MockQSizePolicy: Preferred = 3 Maximum = 2 QSizePolicy = _MockQSizePolicy SizePolicyPreferred = QSizePolicy.Preferred SizePolicyMaximum = QSizePolicy.Maximum DockWidgetMovable = 1 DockWidgetFloatable = 2 DockWidgetClosable = 4 class _MockTabWidget: def __init__(self, *args, **kwargs): self._tabs = [] def addTab(self, widget, title: str): self._tabs.append((widget, title)) QTabWidget = _MockTabWidget class _MockComboBox: def __init__(self, parent=None): self._items = [] self._index = -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) def currentText(self) -> str: return self._items[self._index] if 0 <= self._index < len(self._items) else "" QComboBox = _MockComboBox # --------------------------- # Mock für QVariant # --------------------------- class _MockQVariant: """ Minimaler Ersatz für QtCore.QVariant. Ziel: - Werte transparent durchreichen - Typ-Konstanten bereitstellen - Keine Qt-Abhängigkeiten """ # Typ-Konstanten (symbolisch, Werte egal) Invalid = 0 Int = 1 Double = 2 String = 3 Bool = 4 Date = 5 DateTime = 6 def __init__(self, value: Any = None): self._value = value def value(self) -> Any: return self._value def __repr__(self) -> str: return f"QVariant({self._value!r})" # Optional: automatische Entpackung def __int__(self): return int(self._value) def __float__(self): return float(self._value) def __str__(self): return str(self._value) QVariant = _MockQVariant class _MockQHBoxLayout: 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 QHBoxLayout = _MockQHBoxLayout class _MockQCheckBox: def __init__(self, text: str = "", *args, **kwargs): self._text = text self._checked = False def setText(self, text: str) -> None: self._text = text def isChecked(self) -> bool: return self._checked def setChecked(self, checked: bool) -> None: self._checked = checked QCheckBox = _MockQCheckBox def exec_dialog(dialog: Any) -> Any: return YES # --------------------------- TEST --------------------------- if __name__ == "__main__": debug_qt_status()