Beim Erstellen einer §38-/§41-Vorlage wertet BasemapLegende die Kacheln des sichtbaren basemap.de-Layers im Verfahrensgebiet (wahlweise Druckbereich) kachelweise über selectByGeometry aus, prüft die Renderer-Styles mit derselben Zoomlogik wie QGIS und bildet daraus Legendeneinträge. Ein Mehrfachauswahl-Dialog (neu: ask_mehrfachauswahl) lässt Kategorien abwählen; die Auswahl wird im Projekt gemerkt. Die gewählten Einträge stehen mit der Signatur des Projektlayers (zoomabhängige Werte festgeschrieben) in einem eigenen Legendenelement über der Quellenangabe. Ein eigenes Element ist nötig, weil die nach Karteninhalt filternde Hauptlegende die Hilfslayer ausblenden würde. Wrapper um die nötigen QGIS-/Qt-Symbole mit Mock ergänzt (eigener Block in qgiscore_wrapper; QgsRectangle hatte bisher keinen Mock). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
833 lines
30 KiB
Python
833 lines
30 KiB
Python
"""
|
||
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
|
||
QSettings: Type[Any] = object
|
||
QDialog: Type[Any] = object
|
||
QDialogButtonBox: Type[Any] = object
|
||
QListWidget: Type[Any] = object
|
||
QListWidgetItem: Type[Any] = object
|
||
QAbstractItemView: Type[Any] = object
|
||
QTableWidget: Type[Any] = object
|
||
QTableWidgetItem: Type[Any] = object
|
||
QTableWidgetSelectionRange: Type[Any] = object
|
||
QIcon: Type[Any] = object
|
||
QSize: Type[Any] = object
|
||
|
||
|
||
def exec_dialog(dialog: Any) -> Any:
|
||
"""Führt Dialog modal aus (Qt6: exec(), Qt5: exec_(), Mock: YES)
|
||
|
||
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
||
"""
|
||
raise NotImplementedError("Qt nicht initialisiert")
|
||
|
||
def debug_qt_status() -> None:
|
||
"""Debug: Zeigt Qt-Status für Troubleshooting.
|
||
|
||
von Test abgesehen, weil vernachlässigbar; HD 18.09.2026
|
||
"""
|
||
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,
|
||
QDialog as _QDialog,
|
||
QDialogButtonBox as _QDialogButtonBox,
|
||
QListWidget as _QListWidget,
|
||
QListWidgetItem as _QListWidgetItem,
|
||
QAbstractItemView as _QAbstractItemView,
|
||
QTableWidget as _QTableWidget,
|
||
QTableWidgetItem as _QTableWidgetItem,
|
||
QTableWidgetSelectionRange as _QTableWidgetSelectionRange,
|
||
)
|
||
from qgis.PyQt.QtGui import QFont as _QFont, QIcon as _QIcon
|
||
from qgis.PyQt.QtCore import (
|
||
QEventLoop as _QEventLoop,
|
||
QTimer as _QTimer,
|
||
QUrl as _QUrl,
|
||
QCoreApplication as _QCoreApplication,
|
||
Qt as _Qt,
|
||
QVariant as _QVariant,
|
||
QSettings as _QSettings,
|
||
QSize as _QSize,
|
||
)
|
||
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
|
||
QSettings = _QSettings
|
||
QDialog = _QDialog
|
||
QDialogButtonBox = _QDialogButtonBox
|
||
QListWidget = _QListWidget
|
||
QListWidgetItem = _QListWidgetItem
|
||
QAbstractItemView = _QAbstractItemView
|
||
QTableWidget = _QTableWidget
|
||
QTableWidgetItem = _QTableWidgetItem
|
||
QTableWidgetSelectionRange = _QTableWidgetSelectionRange
|
||
QIcon = _QIcon
|
||
QSize = _QSize
|
||
# ✅ 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
|
||
DialogAccepted = QDialog.DialogCode.Accepted
|
||
ExtendedSelection = QAbstractItemView.SelectionMode.ExtendedSelection
|
||
SelectRows = QAbstractItemView.SelectionBehavior.SelectRows
|
||
NoEditTriggers = QAbstractItemView.EditTrigger.NoEditTriggers
|
||
AscendingOrder = Qt.SortOrder.AscendingOrder
|
||
ItemIsUserCheckable = Qt.ItemFlag.ItemIsUserCheckable
|
||
ItemIsEnabled = Qt.ItemFlag.ItemIsEnabled
|
||
CheckStateChecked = Qt.CheckState.Checked
|
||
CheckStateUnchecked = Qt.CheckState.Unchecked
|
||
UserRole = Qt.ItemDataRole.UserRole
|
||
|
||
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,
|
||
QDialog as _QDialog,
|
||
QDialogButtonBox as _QDialogButtonBox,
|
||
QListWidget as _QListWidget,
|
||
QListWidgetItem as _QListWidgetItem,
|
||
QAbstractItemView as _QAbstractItemView,
|
||
QTableWidget as _QTableWidget,
|
||
QTableWidgetItem as _QTableWidgetItem,
|
||
QTableWidgetSelectionRange as _QTableWidgetSelectionRange,
|
||
)
|
||
from PyQt5.QtGui import QFont as _QFont, QIcon as _QIcon
|
||
from PyQt5.QtCore import (
|
||
QEventLoop as _QEventLoop,
|
||
QTimer as _QTimer,
|
||
QUrl as _QUrl,
|
||
QCoreApplication as _QCoreApplication,
|
||
Qt as _Qt,
|
||
QVariant as _QVariant,
|
||
QSettings as _QSettings,
|
||
QSize as _QSize,
|
||
)
|
||
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
|
||
QSettings = _QSettings
|
||
QDialog = _QDialog
|
||
QDialogButtonBox = _QDialogButtonBox
|
||
QListWidget = _QListWidget
|
||
QListWidgetItem = _QListWidgetItem
|
||
QAbstractItemView = _QAbstractItemView
|
||
QTableWidget = _QTableWidget
|
||
QTableWidgetItem = _QTableWidgetItem
|
||
QTableWidgetSelectionRange = _QTableWidgetSelectionRange
|
||
QIcon = _QIcon
|
||
QSize = _QSize
|
||
|
||
# ✅ 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
|
||
DialogAccepted = QDialog.Accepted
|
||
ExtendedSelection = QAbstractItemView.ExtendedSelection
|
||
SelectRows = QAbstractItemView.SelectRows
|
||
NoEditTriggers = QAbstractItemView.NoEditTriggers
|
||
AscendingOrder = Qt.AscendingOrder
|
||
ItemIsUserCheckable = Qt.ItemIsUserCheckable
|
||
ItemIsEnabled = Qt.ItemIsEnabled
|
||
CheckStateChecked = Qt.Checked
|
||
CheckStateUnchecked = Qt.Unchecked
|
||
UserRole = Qt.UserRole
|
||
|
||
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
|
||
|
||
@staticmethod
|
||
def getItem(parent, title, label, items, current=0, editable=False):
|
||
return (items[current] if items else ""), 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
|
||
def closeEvent(self, event) -> 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
|
||
def deleteLater(self) -> None: pass
|
||
|
||
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 deleteLater(self) -> None: pass
|
||
|
||
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 = [] # Liste von (text, userData)
|
||
self._index = -1
|
||
self.currentTextChanged = type('Signal', (), {'connect': lambda s, cb: None, 'emit': lambda s, v: None})()
|
||
def addItem(self, text: str, userData=None) -> None:
|
||
self._items.append((text, userData))
|
||
if self._index < 0:
|
||
self._index = 0
|
||
def addItems(self, items): [self.addItem(it) for it in items]
|
||
def findText(self, text: str) -> int:
|
||
for i, (t, _) in enumerate(self._items):
|
||
if t == text:
|
||
return i
|
||
return -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][0] if 0 <= self._index < len(self._items) else ""
|
||
def currentData(self):
|
||
return self._items[self._index][1] if 0 <= self._index < len(self._items) else None
|
||
def count(self) -> int:
|
||
return len(self._items)
|
||
|
||
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 _MockQSettings:
|
||
def __init__(self, *args, **kwargs):
|
||
self._data: dict = {}
|
||
self._group = ""
|
||
|
||
def beginGroup(self, group: str) -> None:
|
||
self._group = group
|
||
|
||
def endGroup(self) -> None:
|
||
self._group = ""
|
||
|
||
def setValue(self, key: str, value: Any) -> None:
|
||
full = "%s/%s" % (self._group, key) if self._group else key
|
||
self._data[full] = value
|
||
|
||
def value(self, key: str, default: Any = "") -> Any:
|
||
full = "%s/%s" % (self._group, key) if self._group else key
|
||
return self._data.get(full, default)
|
||
|
||
def remove(self, key: str) -> None:
|
||
full = "%s/%s" % (self._group, key) if self._group else key
|
||
self._data.pop(full, None)
|
||
|
||
QSettings = _MockQSettings
|
||
|
||
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
|
||
|
||
DialogAccepted = 1
|
||
ExtendedSelection = 3
|
||
SelectRows = 1
|
||
NoEditTriggers = 0
|
||
AscendingOrder = 0
|
||
ItemIsUserCheckable = 16
|
||
ItemIsEnabled = 32
|
||
CheckStateChecked = 2
|
||
CheckStateUnchecked = 0
|
||
UserRole = 256
|
||
|
||
class _MockQSize:
|
||
def __init__(self, width: int = 0, height: int = 0):
|
||
self._width, self._height = width, height
|
||
def width(self) -> int: return self._width
|
||
def height(self) -> int: return self._height
|
||
|
||
QSize = _MockQSize
|
||
|
||
class _MockQDialog(_MockWidget):
|
||
DialogCode = type("DialogCode", (), {"Accepted": DialogAccepted, "Rejected": 0})
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self._layout = None
|
||
def setWindowTitle(self, title: str) -> None: pass
|
||
def setWindowIcon(self, icon) -> None: pass
|
||
def exec(self) -> int: return DialogAccepted
|
||
def exec_(self) -> int: return DialogAccepted
|
||
def accept(self) -> None: pass
|
||
def reject(self) -> None: pass
|
||
def resize(self, *args, **kwargs) -> None: pass
|
||
def adjustSize(self) -> None: pass
|
||
def sizeHint(self):
|
||
return type("Size", (), {"width": lambda self=None: 200, "height": lambda self=None: 100})()
|
||
|
||
QDialog = _MockQDialog
|
||
|
||
class _MockQDialogButtonBox(_MockWidget):
|
||
StandardButton = type("StandardButton", (), {"Ok": 1, "Cancel": 2})
|
||
Ok = 1
|
||
Cancel = 2
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.accepted = type('Signal', (), {'connect': lambda s, cb: None})()
|
||
self.rejected = type('Signal', (), {'connect': lambda s, cb: None})()
|
||
|
||
QDialogButtonBox = _MockQDialogButtonBox
|
||
|
||
class _MockQListWidgetItem:
|
||
def __init__(self, text: str = ""):
|
||
self._text = text
|
||
self._selected = False
|
||
def setSelected(self, value: bool) -> None: self._selected = value
|
||
def isSelected(self) -> bool: return self._selected
|
||
|
||
QListWidgetItem = _MockQListWidgetItem
|
||
|
||
class _MockQListWidget(_MockWidget):
|
||
def __init__(self, *args, **kwargs):
|
||
self._items = []
|
||
def addItem(self, item) -> None: self._items.append(item)
|
||
def item(self, index: int): return self._items[index] if 0 <= index < len(self._items) else None
|
||
def count(self) -> int: return len(self._items)
|
||
def row(self, item) -> int: return self._items.index(item) if item in self._items else -1
|
||
def selectedItems(self): return [i for i in self._items if i.isSelected()]
|
||
def setSelectionMode(self, *args, **kwargs) -> None: pass
|
||
def sizeHintForRow(self, index: int) -> int: return 20
|
||
def setFixedHeight(self, height: int) -> None: pass
|
||
def frameWidth(self) -> int: return 1
|
||
|
||
QListWidget = _MockQListWidget
|
||
|
||
class _MockQAbstractItemView:
|
||
SelectionMode = type("SelectionMode", (), {"ExtendedSelection": ExtendedSelection})
|
||
|
||
QAbstractItemView = _MockQAbstractItemView
|
||
|
||
class _MockQTableWidgetItem:
|
||
def __init__(self, text: str = ""):
|
||
self._text = text
|
||
def text(self) -> str: return self._text
|
||
def setText(self, text: str) -> None: self._text = text
|
||
|
||
QTableWidgetItem = _MockQTableWidgetItem
|
||
|
||
class _MockQTableWidget(_MockWidget):
|
||
def __init__(self, rows: int = 0, cols: int = 0, *args, **kwargs):
|
||
self._items = {}
|
||
self._row_count = rows
|
||
self._col_count = cols
|
||
self._hidden_rows = set()
|
||
def setItem(self, row: int, col: int, item) -> None: self._items[(row, col)] = item
|
||
def item(self, row: int, col: int): return self._items.get((row, col))
|
||
def rowCount(self) -> int: return self._row_count
|
||
def setRowCount(self, rows: int) -> None: self._row_count = rows
|
||
def setHorizontalHeaderLabels(self, labels) -> None: pass
|
||
def setSelectionMode(self, *args, **kwargs) -> None: pass
|
||
def setSelectionBehavior(self, *args, **kwargs) -> None: pass
|
||
def setEditTriggers(self, *args, **kwargs) -> None: pass
|
||
def setSortingEnabled(self, *args, **kwargs) -> None: pass
|
||
def setRowHidden(self, row: int, hidden: bool) -> None:
|
||
if hidden: self._hidden_rows.add(row)
|
||
else: self._hidden_rows.discard(row)
|
||
def isRowHidden(self, row: int) -> bool: return row in self._hidden_rows
|
||
def selectRow(self, row: int) -> None: pass
|
||
def selectedItems(self): return []
|
||
def setRangeSelected(self, *args, **kwargs) -> None: pass
|
||
def sortByColumn(self, *args, **kwargs) -> None: pass
|
||
def resizeColumnToContents(self, col: int) -> None: pass
|
||
def setColumnWidth(self, col: int, width: int) -> None: pass
|
||
def columnWidth(self, col: int) -> int: return 100
|
||
def columnCount(self) -> int: return self._col_count
|
||
def verticalHeader(self): return _MockWidget()
|
||
def horizontalHeader(self): return _MockWidget()
|
||
def frameWidth(self) -> int: return 1
|
||
|
||
QTableWidget = _MockQTableWidget
|
||
|
||
class _MockQTableWidgetSelectionRange:
|
||
def __init__(self, *args, **kwargs): pass
|
||
|
||
QTableWidgetSelectionRange = _MockQTableWidgetSelectionRange
|
||
|
||
class _MockQIcon:
|
||
def __init__(self, *args, **kwargs): pass
|
||
|
||
QIcon = _MockQIcon
|
||
|
||
def exec_dialog(dialog: Any) -> Any:
|
||
return YES
|
||
# --------------------------- TEST ---------------------------
|
||
if __name__ == "__main__":
|
||
debug_qt_status()
|