From 80dc006094fab4e4a950b2cf0fbb74ad130d5dbd Mon Sep 17 00:00:00 2001 From: zoshua Date: Thu, 22 Feb 2024 07:43:38 -0700 Subject: [PATCH] Added PySide6 Support --- Qt.py | 137 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/Qt.py b/Qt.py index b68f9a73..08b5c036 100644 --- a/Qt.py +++ b/Qt.py @@ -3,14 +3,16 @@ DOCUMENTATION Qt.py was born in the film and visual effects industry to address the growing need for the development of software capable of running - with more than one flavour of the Qt bindings for Python - PySide, - PySide2, PyQt4 and PyQt5. + with more than one flavour of the Qt bindings for Python. + + Supported Binding: PySide, PySide2, PySide6, PyQt4, PyQt5 1. Build for one, run with all 2. Explicit is better than implicit 3. Support co-existence Default resolution order: + - PySide6 - PySide2 - PyQt5 - PySide @@ -80,10 +82,12 @@ _common_members = { "QtCore": [ + "Property", "QAbstractAnimation", "QAbstractEventDispatcher", "QAbstractItemModel", "QAbstractListModel", + "QAbstractProxyModel", "QAbstractState", "QAbstractTableModel", "QAbstractTransition", @@ -114,6 +118,8 @@ "QGenericArgument", "QGenericReturnArgument", "QHistoryState", + "QItemSelection", + "QItemSelectionModel", "QItemSelectionRange", "QIODevice", "QLibraryInfo", @@ -155,8 +161,10 @@ "QSize", "QSizeF", "QSocketNotifier", + "QSortFilterProxyModel", "QState", "QStateMachine", + "QStringListModel", "QSysInfo", "QSystemSemaphore", "QT_TRANSLATE_NOOP", @@ -211,7 +219,9 @@ "qVersion", "qWarning", "qrand", - "qsrand" + "qsrand", + "Signal", + "Slot" ], "QtGui": [ "QAbstractTextDocumentLayout", @@ -730,12 +740,14 @@ def messageOutputHandler(*args): passObject = messageOutputHandler if handler else handler if Qt.IsPySide or Qt.IsPyQt4: return Qt._QtCore.qInstallMsgHandler(passObject) - elif Qt.IsPySide2 or Qt.IsPyQt5: + elif Qt.IsPySide2 or Qt.IsPyQt5 or Qt.IsPySide6: return Qt._QtCore.qInstallMessageHandler(passObject) def _getcpppointer(object): - if hasattr(Qt, "_shiboken2"): + if hasattr(Qt, "_shiboken6"): + return getattr(Qt, "_shiboken6").getCppPointer(object)[0] + elif hasattr(Qt, "_shiboken2"): return getattr(Qt, "_shiboken2").getCppPointer(object)[0] elif hasattr(Qt, "_shiboken"): return getattr(Qt, "_shiboken").getCppPointer(object)[0] @@ -773,6 +785,8 @@ def _wrapinstance(ptr, base=None): func = getattr(Qt, "_sip").wrapinstance elif Qt.IsPySide2: func = getattr(Qt, "_shiboken2").wrapInstance + elif Qt.IsPySide6: + func = getattr(Qt, "_shiboken6").wrapInstance elif Qt.IsPySide: func = getattr(Qt, "_shiboken").wrapInstance else: @@ -812,7 +826,10 @@ def _isvalid(object): object (QObject): QObject to check the validity of. """ - if hasattr(Qt, "_shiboken2"): + if hasattr(Qt, "_shiboken6"): + return getattr(Qt, "_shiboken6").isValid(object) + + elif hasattr(Qt, "_shiboken2"): return getattr(Qt, "_shiboken2").isValid(object) elif hasattr(Qt, "_shiboken"): @@ -1014,17 +1031,29 @@ def createWidget(self, class_name, parent=None, name=""): """ _misplaced_members = { + "PySide6": { + "QtGui.QShortcut": "QtWidgets.QShortcut", + "QtGui.QStringListModel": "QtCore.QStringListModel", + "QtGui.QAction": "QtWidgets.QAction", + "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi], + "shiboken6.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance], + "shiboken6.getCppPointer": ["QtCompat.getCppPointer", _getcpppointer], + "shiboken6.isValid": ["QtCompat.isValid", _isvalid], + "QtWidgets.qApp": "QtWidgets.QApplication.instance()", + "QtCore.QCoreApplication.translate": [ + "QtCompat.translate", _translate + ], + "QtWidgets.QApplication.translate": [ + "QtCompat.translate", _translate + ], + "QtCore.qInstallMessageHandler": [ + "QtCompat.qInstallMessageHandler", _qInstallMessageHandler + ], + "QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4", + "QtMultimedia.QSound": "QtMultimedia.QSound", + }, "PySide2": { - "QtCore.QStringListModel": "QtCore.QStringListModel", "QtGui.QStringListModel": "QtCore.QStringListModel", - "QtCore.Property": "QtCore.Property", - "QtCore.Signal": "QtCore.Signal", - "QtCore.Slot": "QtCore.Slot", - "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtCore.QItemSelection": "QtCore.QItemSelection", - "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi], "shiboken2.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance], "shiboken2.getCppPointer": ["QtCompat.getCppPointer", _getcpppointer], @@ -1046,12 +1075,6 @@ def createWidget(self, class_name, parent=None, name=""): "QtCore.pyqtProperty": "QtCore.Property", "QtCore.pyqtSignal": "QtCore.Signal", "QtCore.pyqtSlot": "QtCore.Slot", - "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtCore.QStringListModel": "QtCore.QStringListModel", - "QtCore.QItemSelection": "QtCore.QItemSelection", - "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", "uic.loadUi": ["QtCompat.loadUi", _loadUi], "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance], "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer], @@ -1075,9 +1098,6 @@ def createWidget(self, class_name, parent=None, name=""): "QtGui.QStringListModel": "QtCore.QStringListModel", "QtGui.QItemSelection": "QtCore.QItemSelection", "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.Property": "QtCore.Property", - "QtCore.Signal": "QtCore.Signal", - "QtCore.Slot": "QtCore.Slot", "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange", "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog", "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog", @@ -1122,7 +1142,6 @@ def createWidget(self, class_name, parent=None, name=""): "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget", "QtGui.QPrinter": "QtPrintSupport.QPrinter", "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo", - # "QtCore.pyqtSignature": "QtCore.Slot", "uic.loadUi": ["QtCompat.loadUi", _loadUi], "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance], "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer], @@ -1157,6 +1176,26 @@ def createWidget(self, class_name, parent=None, name=""): } """ _compatibility_members = { + "PySide6": { + "QWidget": { + "grab": "QtWidgets.QWidget.grab", + }, + "QHeaderView": { + "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable", + "setSectionsClickable": + "QtWidgets.QHeaderView.setSectionsClickable", + "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode", + "setSectionResizeMode": + "QtWidgets.QHeaderView.setSectionResizeMode", + "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable", + "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable", + }, + "QFileDialog": { + "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", + "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", + "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", + }, + }, "PySide2": { "QWidget": { "grab": "QtWidgets.QWidget.grab", @@ -1435,6 +1474,50 @@ def _build_compatibility_members(binding, decorators=None): setattr(Qt.QtCompat, classname, compat_class) +def _pyside6(): + """Initialise PySide6 + + These functions serve to test the existence of a binding + along with set it up in such a way that it aligns with + the final step; adding members from the original binding + to Qt.py + + """ + + import PySide6 as module + extras = ["QtUiTools"] + try: + import shiboken6 + extras.append("shiboken6") + except ImportError as e: + print("ImportError: %s" % e) + + _setup(module, extras) + Qt.__binding_version__ = module.__version__ + + if hasattr(Qt, "_shiboken6"): + Qt.QtCompat.wrapInstance = _wrapinstance + Qt.QtCompat.getCppPointer = _getcpppointer + Qt.QtCompat.delete = shiboken6.delete + + if hasattr(Qt, "_QtUiTools"): + Qt.QtCompat.loadUi = _loadUi + + if hasattr(Qt, "_QtCore"): + Qt.__qt_version__ = Qt._QtCore.qVersion() + Qt.QtCompat.dataChanged = ( + lambda self, topleft, bottomright, roles=None: + self.dataChanged.emit(topleft, bottomright, roles or []) + ) + + if hasattr(Qt, "_QtWidgets"): + Qt.QtCompat.setSectionResizeMode = \ + Qt._QtWidgets.QHeaderView.setSectionResizeMode + + _reassign_misplaced_members("PySide6") + _build_compatibility_members("PySide6") + + def _pyside2(): """Initialise PySide2 @@ -1821,7 +1904,7 @@ def __call__(self, *a, **kw): def _install(): # Default order (customize order and content via QT_PREFERRED_BINDING) - default_order = ("PySide2", "PyQt5", "PySide", "PyQt4") + default_order = ("PySide6", "PySide2", "PyQt5", "PySide", "PyQt4") preferred_order = None if QT_PREFERRED_BINDING_JSON: # A per-vendor preferred binding customization was defined @@ -1854,6 +1937,7 @@ def _install(): order = preferred_order or default_order available = { + "PySide6": _pyside6, "PySide2": _pyside2, "PyQt5": _pyqt5, "PySide": _pyside, @@ -1938,6 +2022,7 @@ def _install(): _install() # Setup Binding Enum states +Qt.IsPySide6 = Qt.__binding__ == "PySide6" Qt.IsPySide2 = Qt.__binding__ == 'PySide2' Qt.IsPyQt5 = Qt.__binding__ == 'PyQt5' Qt.IsPySide = Qt.__binding__ == 'PySide'