Files
Plugin_SN_Basis/functions/qt_wrapper.py

583 lines
19 KiB
Python
Raw Permalink Normal View History

"""
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
2026-03-12 16:14:02 +01:00
QProgressDialog: Type[Any] = object
QEventLoop: Type[Any] = object
2026-03-12 16:14:02 +01:00
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
2026-03-20 12:01:16 +01:00
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,
2026-03-12 16:14:02 +01:00
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,
2026-03-20 12:01:16 +01:00
QCheckBox as _QCheckBox,
QHBoxLayout as _QHBoxLayout,
)
from qgis.PyQt.QtGui import QFont as _QFont
from qgis.PyQt.QtCore import (
QEventLoop as _QEventLoop,
2026-03-12 16:14:02 +01:00
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
2026-03-12 16:14:02 +01:00
QProgressDialog = _QProgressDialog
QProgressDialog = _QProgressDialog
QEventLoop = _QEventLoop
2026-03-12 16:14:02 +01:00
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
2026-03-20 12:01:16 +01:00
QCheckBox = _QCheckBox
QVariant = _QVariant
2026-03-20 12:01:16 +01:00
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,
2026-03-20 12:01:16 +01:00
QCheckBox as _QCheckBox,
QHBoxLayout as _QHBoxLayout,
)
from PyQt5.QtGui import QFont as _QFont
from PyQt5.QtCore import (
QEventLoop as _QEventLoop,
2026-03-12 16:14:02 +01:00
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
2026-03-12 16:14:02 +01:00
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
2026-03-20 12:01:16 +01:00
QCheckBox = _QCheckBox
QVariant = _QVariant
2026-03-20 12:01:16 +01:00
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
2026-03-12 16:14:02 +01:00
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
2026-03-20 12:01:16 +01:00
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()