From e1d2edb204b593e509854ca7a34a747e47f1fbbe Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Tue, 2 Nov 2021 11:13:52 -0400 Subject: [PATCH] refactoring qtcompat (#34) * add other modules * add qtsvg * more changes for qt6 support * add qaction * more enum namespacing * more ns fixes * updating qtcompat * more minimal * wip * update typing * fix one more namespace * update types * update exports * add stubs * fix * fix exec --- examples/basic.py | 4 +- examples/basic_float.py | 2 +- examples/demo_widget.py | 22 +- examples/float.py | 6 +- examples/generic.py | 2 +- examples/labeled.py | 6 +- mypy.ini | 7 + setup.cfg | 5 + src/superqt/qtcompat/Qt3DAnimation.pyi | 4 + src/superqt/qtcompat/Qt3DCore.pyi | 4 + src/superqt/qtcompat/Qt3DExtras.pyi | 4 + src/superqt/qtcompat/Qt3DInput.pyi | 4 + src/superqt/qtcompat/Qt3DLogic.pyi | 4 + src/superqt/qtcompat/Qt3DRender.pyi | 4 + src/superqt/qtcompat/QtCharts.pyi | 4 + src/superqt/qtcompat/QtConcurrent.pyi | 4 + src/superqt/qtcompat/QtCore.py | 70 +---- src/superqt/qtcompat/QtCore.pyi | 10 + src/superqt/qtcompat/QtDataVisualization.pyi | 4 + src/superqt/qtcompat/QtGui.py | 41 +-- src/superqt/qtcompat/QtGui.pyi | 4 + src/superqt/qtcompat/QtHelp.pyi | 4 + src/superqt/qtcompat/QtLocation.pyi | 4 + src/superqt/qtcompat/QtMacExtras.pyi | 4 + src/superqt/qtcompat/QtMultimedia.pyi | 4 + src/superqt/qtcompat/QtMultimediaWidgets.pyi | 4 + src/superqt/qtcompat/QtNetwork.pyi | 4 + src/superqt/qtcompat/QtOpenGL.pyi | 4 + src/superqt/qtcompat/QtOpenGLFunctions.pyi | 4 + src/superqt/qtcompat/QtPositioning.pyi | 4 + src/superqt/qtcompat/QtPrintSupport.pyi | 4 + src/superqt/qtcompat/QtQml.pyi | 4 + src/superqt/qtcompat/QtQuick.pyi | 4 + src/superqt/qtcompat/QtQuickControls2.pyi | 4 + src/superqt/qtcompat/QtQuickWidgets.pyi | 4 + src/superqt/qtcompat/QtRemoteObjects.pyi | 4 + src/superqt/qtcompat/QtScript.pyi | 4 + src/superqt/qtcompat/QtScriptTools.pyi | 4 + src/superqt/qtcompat/QtScxml.pyi | 4 + src/superqt/qtcompat/QtSensors.pyi | 4 + src/superqt/qtcompat/QtSerialPort.pyi | 4 + src/superqt/qtcompat/QtSql.pyi | 4 + src/superqt/qtcompat/QtSvg.pyi | 4 + src/superqt/qtcompat/QtTest.pyi | 4 + src/superqt/qtcompat/QtTextToSpeech.pyi | 4 + src/superqt/qtcompat/QtUiTools.pyi | 4 + src/superqt/qtcompat/QtWebChannel.pyi | 4 + src/superqt/qtcompat/QtWebEngine.pyi | 4 + src/superqt/qtcompat/QtWebEngineCore.pyi | 4 + src/superqt/qtcompat/QtWebEngineWidgets.pyi | 4 + src/superqt/qtcompat/QtWebSockets.pyi | 4 + src/superqt/qtcompat/QtWidgets.py | 48 +--- src/superqt/qtcompat/QtWidgets.pyi | 12 + src/superqt/qtcompat/QtXml.pyi | 4 + src/superqt/qtcompat/QtXmlPatterns.pyi | 4 + src/superqt/qtcompat/__init__.py | 254 +++++++----------- src/superqt/sliders/_generic_range_slider.py | 12 +- src/superqt/sliders/_generic_slider.py | 57 ++-- src/superqt/sliders/_labeled.py | 42 +-- src/superqt/sliders/_range_style.py | 32 +-- src/superqt/spinbox/_intspin.py | 12 +- tests/test_eliding_label.py | 2 +- tests/test_large_int_spinbox.py | 2 +- tests/test_sliders/_testutil.py | 34 ++- tests/test_sliders/test_generic_slider.py | 30 ++- tests/test_sliders/test_range_slider.py | 26 +- .../test_sliders/test_single_value_sliders.py | 31 ++- tests/test_sliders/test_slider.py | 28 +- 68 files changed, 533 insertions(+), 432 deletions(-) create mode 100644 mypy.ini create mode 100644 src/superqt/qtcompat/Qt3DAnimation.pyi create mode 100644 src/superqt/qtcompat/Qt3DCore.pyi create mode 100644 src/superqt/qtcompat/Qt3DExtras.pyi create mode 100644 src/superqt/qtcompat/Qt3DInput.pyi create mode 100644 src/superqt/qtcompat/Qt3DLogic.pyi create mode 100644 src/superqt/qtcompat/Qt3DRender.pyi create mode 100644 src/superqt/qtcompat/QtCharts.pyi create mode 100644 src/superqt/qtcompat/QtConcurrent.pyi create mode 100644 src/superqt/qtcompat/QtCore.pyi create mode 100644 src/superqt/qtcompat/QtDataVisualization.pyi create mode 100644 src/superqt/qtcompat/QtGui.pyi create mode 100644 src/superqt/qtcompat/QtHelp.pyi create mode 100644 src/superqt/qtcompat/QtLocation.pyi create mode 100644 src/superqt/qtcompat/QtMacExtras.pyi create mode 100644 src/superqt/qtcompat/QtMultimedia.pyi create mode 100644 src/superqt/qtcompat/QtMultimediaWidgets.pyi create mode 100644 src/superqt/qtcompat/QtNetwork.pyi create mode 100644 src/superqt/qtcompat/QtOpenGL.pyi create mode 100644 src/superqt/qtcompat/QtOpenGLFunctions.pyi create mode 100644 src/superqt/qtcompat/QtPositioning.pyi create mode 100644 src/superqt/qtcompat/QtPrintSupport.pyi create mode 100644 src/superqt/qtcompat/QtQml.pyi create mode 100644 src/superqt/qtcompat/QtQuick.pyi create mode 100644 src/superqt/qtcompat/QtQuickControls2.pyi create mode 100644 src/superqt/qtcompat/QtQuickWidgets.pyi create mode 100644 src/superqt/qtcompat/QtRemoteObjects.pyi create mode 100644 src/superqt/qtcompat/QtScript.pyi create mode 100644 src/superqt/qtcompat/QtScriptTools.pyi create mode 100644 src/superqt/qtcompat/QtScxml.pyi create mode 100644 src/superqt/qtcompat/QtSensors.pyi create mode 100644 src/superqt/qtcompat/QtSerialPort.pyi create mode 100644 src/superqt/qtcompat/QtSql.pyi create mode 100644 src/superqt/qtcompat/QtSvg.pyi create mode 100644 src/superqt/qtcompat/QtTest.pyi create mode 100644 src/superqt/qtcompat/QtTextToSpeech.pyi create mode 100644 src/superqt/qtcompat/QtUiTools.pyi create mode 100644 src/superqt/qtcompat/QtWebChannel.pyi create mode 100644 src/superqt/qtcompat/QtWebEngine.pyi create mode 100644 src/superqt/qtcompat/QtWebEngineCore.pyi create mode 100644 src/superqt/qtcompat/QtWebEngineWidgets.pyi create mode 100644 src/superqt/qtcompat/QtWebSockets.pyi create mode 100644 src/superqt/qtcompat/QtWidgets.pyi create mode 100644 src/superqt/qtcompat/QtXml.pyi create mode 100644 src/superqt/qtcompat/QtXmlPatterns.pyi diff --git a/examples/basic.py b/examples/basic.py index 13b40d6..8d91fd5 100644 --- a/examples/basic.py +++ b/examples/basic.py @@ -4,8 +4,8 @@ from superqt.qtcompat.QtWidgets import QApplication app = QApplication([]) -slider = QRangeSlider(Qt.Horizontal) -slider = QRangeSlider(Qt.Horizontal) +slider = QRangeSlider(Qt.Orientation.Horizontal) +slider = QRangeSlider(Qt.Orientation.Horizontal) slider.setValue((20, 80)) slider.show() diff --git a/examples/basic_float.py b/examples/basic_float.py index 18c506d..a4fed2b 100644 --- a/examples/basic_float.py +++ b/examples/basic_float.py @@ -4,7 +4,7 @@ from superqt.qtcompat.QtWidgets import QApplication app = QApplication([]) -slider = QDoubleSlider(Qt.Horizontal) +slider = QDoubleSlider(Qt.Orientation.Horizontal) slider.setRange(0, 1) slider.setValue(0.5) slider.show() diff --git a/examples/demo_widget.py b/examples/demo_widget.py index fbd8335..94ff211 100644 --- a/examples/demo_widget.py +++ b/examples/demo_widget.py @@ -33,35 +33,37 @@ QRangeSlider { } """ +Horizontal = QtCore.Qt.Orientation.Horizontal + class DemoWidget(QtW.QWidget): def __init__(self) -> None: super().__init__() - reg_hslider = QtW.QSlider(QtCore.Qt.Horizontal) + reg_hslider = QtW.QSlider(Horizontal) reg_hslider.setValue(50) - range_hslider = QRangeSlider(QtCore.Qt.Horizontal) + range_hslider = QRangeSlider(Horizontal) range_hslider.setValue((20, 80)) - multi_range_hslider = QRangeSlider(QtCore.Qt.Horizontal) + multi_range_hslider = QRangeSlider(Horizontal) multi_range_hslider.setValue((11, 33, 66, 88)) - multi_range_hslider.setTickPosition(QtW.QSlider.TicksAbove) + multi_range_hslider.setTickPosition(QtW.QSlider.TickPosition.TicksAbove) - styled_reg_hslider = QtW.QSlider(QtCore.Qt.Horizontal) + styled_reg_hslider = QtW.QSlider(Horizontal) styled_reg_hslider.setValue(50) styled_reg_hslider.setStyleSheet(QSS) - styled_range_hslider = QRangeSlider(QtCore.Qt.Horizontal) + styled_range_hslider = QRangeSlider(Horizontal) styled_range_hslider.setValue((20, 80)) styled_range_hslider.setStyleSheet(QSS) - reg_vslider = QtW.QSlider(QtCore.Qt.Vertical) + reg_vslider = QtW.QSlider(QtCore.Qt.Orientation.Vertical) reg_vslider.setValue(50) - range_vslider = QRangeSlider(QtCore.Qt.Vertical) + range_vslider = QRangeSlider(QtCore.Qt.Orientation.Vertical) range_vslider.setValue((22, 77)) - tick_vslider = QtW.QSlider(QtCore.Qt.Vertical) + tick_vslider = QtW.QSlider(QtCore.Qt.Orientation.Vertical) tick_vslider.setValue(55) tick_vslider.setTickPosition(QtW.QSlider.TicksRight) - range_tick_vslider = QRangeSlider(QtCore.Qt.Vertical) + range_tick_vslider = QRangeSlider(QtCore.Qt.Orientation.Vertical) range_tick_vslider.setValue((22, 77)) range_tick_vslider.setTickPosition(QtW.QSlider.TicksLeft) diff --git a/examples/float.py b/examples/float.py index 5f9f888..c255b8b 100644 --- a/examples/float.py +++ b/examples/float.py @@ -6,9 +6,9 @@ app = QApplication([]) w = QWidget() -sld1 = QDoubleSlider(Qt.Horizontal) -sld2 = QDoubleRangeSlider(Qt.Horizontal) -rs = QRangeSlider(Qt.Horizontal) +sld1 = QDoubleSlider(Qt.Orientation.Horizontal) +sld2 = QDoubleRangeSlider(Qt.Orientation.Horizontal) +rs = QRangeSlider(Qt.Orientation.Horizontal) sld1.valueChanged.connect(lambda e: print("doubslider valuechanged", e)) diff --git a/examples/generic.py b/examples/generic.py index 656bd2e..6d31bc1 100644 --- a/examples/generic.py +++ b/examples/generic.py @@ -4,7 +4,7 @@ from superqt.qtcompat.QtWidgets import QApplication app = QApplication([]) -sld = QDoubleSlider(Qt.Horizontal) +sld = QDoubleSlider(Qt.Orientation.Horizontal) sld.setRange(0, 1) sld.setValue(0.5) sld.show() diff --git a/examples/labeled.py b/examples/labeled.py index aed02f1..a7915c4 100644 --- a/examples/labeled.py +++ b/examples/labeled.py @@ -9,7 +9,7 @@ from superqt.qtcompat.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, Q app = QApplication([]) -ORIENTATION = Qt.Horizontal +ORIENTATION = Qt.Orientation.Horizontal w = QWidget() qls = QLabeledSlider(ORIENTATION) @@ -35,7 +35,9 @@ qldrs.setSingleStep(0.01) qldrs.setValue((0.2, 0.7)) -w.setLayout(QVBoxLayout() if ORIENTATION == Qt.Horizontal else QHBoxLayout()) +w.setLayout( + QVBoxLayout() if ORIENTATION == Qt.Orientation.Horizontal else QHBoxLayout() +) w.layout().addWidget(qls) w.layout().addWidget(qlds) w.layout().addWidget(qlrs) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..9f50123 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,7 @@ +[mypy] +strict = True + +[mypy-superqt.qtcompat.*] +ignore_missing_imports = True +warn_unused_ignores = False +allow_redefinition = True diff --git a/setup.cfg b/setup.cfg index 43517f7..b176172 100644 --- a/setup.cfg +++ b/setup.cfg @@ -81,6 +81,11 @@ superqt = py.typed exclude = _version.py,.eggs,examples docstring-convention = numpy ignore = E203,W503,E501,C901,F403,F405,D100 +per-file-ignores = + src/superqt/qtcompat/QtCore.py:F401 + src/superqt/qtcompat/QtGui.py:F401 + src/superqt/qtcompat/QtWidgets.py:F401 + src/superqt/qtcompat/__init__.py:F401,F811 [pydocstyle] convention = numpy diff --git a/src/superqt/qtcompat/Qt3DAnimation.pyi b/src/superqt/qtcompat/Qt3DAnimation.pyi new file mode 100644 index 0000000..f357aa1 --- /dev/null +++ b/src/superqt/qtcompat/Qt3DAnimation.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DAnimation import * +from PyQt6.Qt3DAnimation import * +from PySide2.Qt3DAnimation import * +from PySide6.Qt3DAnimation import * diff --git a/src/superqt/qtcompat/Qt3DCore.pyi b/src/superqt/qtcompat/Qt3DCore.pyi new file mode 100644 index 0000000..9d8f8e5 --- /dev/null +++ b/src/superqt/qtcompat/Qt3DCore.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DCore import * +from PyQt6.Qt3DCore import * +from PySide2.Qt3DCore import * +from PySide6.Qt3DCore import * diff --git a/src/superqt/qtcompat/Qt3DExtras.pyi b/src/superqt/qtcompat/Qt3DExtras.pyi new file mode 100644 index 0000000..35d7b9e --- /dev/null +++ b/src/superqt/qtcompat/Qt3DExtras.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DExtras import * +from PyQt6.Qt3DExtras import * +from PySide2.Qt3DExtras import * +from PySide6.Qt3DExtras import * diff --git a/src/superqt/qtcompat/Qt3DInput.pyi b/src/superqt/qtcompat/Qt3DInput.pyi new file mode 100644 index 0000000..1569eed --- /dev/null +++ b/src/superqt/qtcompat/Qt3DInput.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DInput import * +from PyQt6.Qt3DInput import * +from PySide2.Qt3DInput import * +from PySide6.Qt3DInput import * diff --git a/src/superqt/qtcompat/Qt3DLogic.pyi b/src/superqt/qtcompat/Qt3DLogic.pyi new file mode 100644 index 0000000..580facd --- /dev/null +++ b/src/superqt/qtcompat/Qt3DLogic.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DLogic import * +from PyQt6.Qt3DLogic import * +from PySide2.Qt3DLogic import * +from PySide6.Qt3DLogic import * diff --git a/src/superqt/qtcompat/Qt3DRender.pyi b/src/superqt/qtcompat/Qt3DRender.pyi new file mode 100644 index 0000000..541e3ce --- /dev/null +++ b/src/superqt/qtcompat/Qt3DRender.pyi @@ -0,0 +1,4 @@ +from PyQt5.Qt3DRender import * +from PyQt6.Qt3DRender import * +from PySide2.Qt3DRender import * +from PySide6.Qt3DRender import * diff --git a/src/superqt/qtcompat/QtCharts.pyi b/src/superqt/qtcompat/QtCharts.pyi new file mode 100644 index 0000000..e095480 --- /dev/null +++ b/src/superqt/qtcompat/QtCharts.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtCharts import * +from PyQt6.QtCharts import * +from PySide2.QtCharts import * +from PySide6.QtCharts import * diff --git a/src/superqt/qtcompat/QtConcurrent.pyi b/src/superqt/qtcompat/QtConcurrent.pyi new file mode 100644 index 0000000..7e665d0 --- /dev/null +++ b/src/superqt/qtcompat/QtConcurrent.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtConcurrent import * +from PyQt6.QtConcurrent import * +from PySide2.QtConcurrent import * +from PySide6.QtConcurrent import * diff --git a/src/superqt/qtcompat/QtCore.py b/src/superqt/qtcompat/QtCore.py index 338dff0..d184eb8 100644 --- a/src/superqt/qtcompat/QtCore.py +++ b/src/superqt/qtcompat/QtCore.py @@ -1,62 +1,12 @@ -# -# Copyright © 2014-2015 Colin Duquesnoy -# Copyright © 2009- The Spyder Development Team -# -# Licensed under the terms of the MIT License -# (see LICENSE.txt for details) +# type: ignore +from . import API_NAME, _get_qtmodule -""" -Modified from qtpy.QtCore. -Provides QtCore classes and functions. -""" +_QtCore = _get_qtmodule(__name__) +globals().update(_QtCore.__dict__) -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError - -if PYQT5: - from PyQt5.QtCore import QT_VERSION_STR as __version__ - from PyQt5.QtCore import * - - try: - from PyQt5.QtCore import pyqtBoundSignal as SignalInstance # noqa - except ImportError: # 5.11 - SignalInstance = None - from PyQt5.QtCore import pyqtProperty as Property # noqa - from PyQt5.QtCore import pyqtSignal as Signal # noqa - from PyQt5.QtCore import pyqtSlot as Slot # noqa - - # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR -elif PYQT6: - from PyQt6.QtCore import QT_VERSION_STR as __version__ - from PyQt6.QtCore import * - from PyQt6.QtCore import pyqtBoundSignal as SignalInstance # noqa - from PyQt6.QtCore import pyqtProperty as Property # noqa - from PyQt6.QtCore import pyqtSignal as Signal # noqa - from PyQt6.QtCore import pyqtSlot as Slot # noqa - - # backwards compat with PyQt5 - # namespace moves: - for cls in (QEvent, Qt): - for attr in dir(cls): - if not attr[0].isupper(): - continue - ns = getattr(cls, attr) - for name, val in vars(ns).items(): - if not name.startswith("_"): - setattr(cls, name, val) - - # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR -elif PYSIDE2: - import PySide2.QtCore - from PySide2.QtCore import * # noqa - - __version__ = PySide2.QtCore.__version__ -elif PYSIDE6: - import PySide6.QtCore - from PySide6.QtCore import * # noqa - - __version__ = PySide6.QtCore.__version__ - -else: - raise PythonQtError("No Qt bindings could be found") +if "PyQt" in API_NAME: + Property = _QtCore.pyqtProperty + Signal = _QtCore.pyqtSignal + SignalInstance = getattr(_QtCore, "pyqtBoundSignal", None) + Slot = _QtCore.pyqtSlot + __version__ = _QtCore.QT_VERSION_STR diff --git a/src/superqt/qtcompat/QtCore.pyi b/src/superqt/qtcompat/QtCore.pyi new file mode 100644 index 0000000..f525ba8 --- /dev/null +++ b/src/superqt/qtcompat/QtCore.pyi @@ -0,0 +1,10 @@ +from PyQt5.QtCore import * +from PyQt6.QtCore import * +from PySide2.QtCore import * +from PySide6.QtCore import * + +Property = pyqtProperty +Signal = pyqtSignal +SignalInstance = pyqtBoundSignal +Slot = pyqtSlot +__version__: str diff --git a/src/superqt/qtcompat/QtDataVisualization.pyi b/src/superqt/qtcompat/QtDataVisualization.pyi new file mode 100644 index 0000000..45cc01d --- /dev/null +++ b/src/superqt/qtcompat/QtDataVisualization.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtDataVisualization import * +from PyQt6.QtDataVisualization import * +from PySide2.QtDataVisualization import * +from PySide6.QtDataVisualization import * diff --git a/src/superqt/qtcompat/QtGui.py b/src/superqt/qtcompat/QtGui.py index fda74f5..de815df 100644 --- a/src/superqt/qtcompat/QtGui.py +++ b/src/superqt/qtcompat/QtGui.py @@ -1,42 +1,13 @@ -# -# Copyright © 2014-2015 Colin Duquesnoy -# Copyright © 2009- The Spyder Development Team -# -# Licensed under the terms of the MIT License -# (see LICENSE.txt for details) +# type: ignore +from . import API_NAME, _get_qtmodule -""" -Modified from qtpy.QtGui -Provides QtGui classes and functions. -""" +_QtGui = _get_qtmodule(__name__) +globals().update(_QtGui.__dict__) -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError - -if PYQT5: - from PyQt5.QtGui import * -elif PYSIDE2: - from PySide2.QtGui import * -elif PYQT6: - from PyQt6.QtGui import * - - # backwards compat with PyQt5 - # namespace moves: - for cls in (QPalette,): - for attr in dir(cls): - if not attr[0].isupper(): - continue - ns = getattr(cls, attr) - for name, val in vars(ns).items(): - if not name.startswith("_"): - setattr(cls, name, val) +if "6" in API_NAME: def pos(self, *a): _pos = self.position(*a) return _pos.toPoint() - QMouseEvent.pos = pos - -elif PYSIDE6: - from PySide6.QtGui import * # noqa -else: - raise PythonQtError("No Qt bindings could be found") + _QtGui.QMouseEvent.pos = pos diff --git a/src/superqt/qtcompat/QtGui.pyi b/src/superqt/qtcompat/QtGui.pyi new file mode 100644 index 0000000..1df2e9a --- /dev/null +++ b/src/superqt/qtcompat/QtGui.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtGui import * +from PyQt6.QtGui import * +from PySide2.QtGui import * +from PySide6.QtGui import * diff --git a/src/superqt/qtcompat/QtHelp.pyi b/src/superqt/qtcompat/QtHelp.pyi new file mode 100644 index 0000000..99c8dad --- /dev/null +++ b/src/superqt/qtcompat/QtHelp.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtHelp import * +from PyQt6.QtHelp import * +from PySide2.QtHelp import * +from PySide6.QtHelp import * diff --git a/src/superqt/qtcompat/QtLocation.pyi b/src/superqt/qtcompat/QtLocation.pyi new file mode 100644 index 0000000..eade933 --- /dev/null +++ b/src/superqt/qtcompat/QtLocation.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtLocation import * +from PyQt6.QtLocation import * +from PySide2.QtLocation import * +from PySide6.QtLocation import * diff --git a/src/superqt/qtcompat/QtMacExtras.pyi b/src/superqt/qtcompat/QtMacExtras.pyi new file mode 100644 index 0000000..0d81cea --- /dev/null +++ b/src/superqt/qtcompat/QtMacExtras.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtMacExtras import * +from PyQt6.QtMacExtras import * +from PySide2.QtMacExtras import * +from PySide6.QtMacExtras import * diff --git a/src/superqt/qtcompat/QtMultimedia.pyi b/src/superqt/qtcompat/QtMultimedia.pyi new file mode 100644 index 0000000..dc85fa5 --- /dev/null +++ b/src/superqt/qtcompat/QtMultimedia.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtMultimedia import * +from PyQt6.QtMultimedia import * +from PySide2.QtMultimedia import * +from PySide6.QtMultimedia import * diff --git a/src/superqt/qtcompat/QtMultimediaWidgets.pyi b/src/superqt/qtcompat/QtMultimediaWidgets.pyi new file mode 100644 index 0000000..85b52e3 --- /dev/null +++ b/src/superqt/qtcompat/QtMultimediaWidgets.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtMultimediaWidgets import * +from PyQt6.QtMultimediaWidgets import * +from PySide2.QtMultimediaWidgets import * +from PySide6.QtMultimediaWidgets import * diff --git a/src/superqt/qtcompat/QtNetwork.pyi b/src/superqt/qtcompat/QtNetwork.pyi new file mode 100644 index 0000000..fc0b017 --- /dev/null +++ b/src/superqt/qtcompat/QtNetwork.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtNetwork import * +from PyQt6.QtNetwork import * +from PySide2.QtNetwork import * +from PySide6.QtNetwork import * diff --git a/src/superqt/qtcompat/QtOpenGL.pyi b/src/superqt/qtcompat/QtOpenGL.pyi new file mode 100644 index 0000000..cbbc22c --- /dev/null +++ b/src/superqt/qtcompat/QtOpenGL.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtOpenGL import * +from PyQt6.QtOpenGL import * +from PySide2.QtOpenGL import * +from PySide6.QtOpenGL import * diff --git a/src/superqt/qtcompat/QtOpenGLFunctions.pyi b/src/superqt/qtcompat/QtOpenGLFunctions.pyi new file mode 100644 index 0000000..4f3014b --- /dev/null +++ b/src/superqt/qtcompat/QtOpenGLFunctions.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtOpenGLFunctions import * +from PyQt6.QtOpenGLFunctions import * +from PySide2.QtOpenGLFunctions import * +from PySide6.QtOpenGLFunctions import * diff --git a/src/superqt/qtcompat/QtPositioning.pyi b/src/superqt/qtcompat/QtPositioning.pyi new file mode 100644 index 0000000..a15cdee --- /dev/null +++ b/src/superqt/qtcompat/QtPositioning.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtPositioning import * +from PyQt6.QtPositioning import * +from PySide2.QtPositioning import * +from PySide6.QtPositioning import * diff --git a/src/superqt/qtcompat/QtPrintSupport.pyi b/src/superqt/qtcompat/QtPrintSupport.pyi new file mode 100644 index 0000000..6ef4723 --- /dev/null +++ b/src/superqt/qtcompat/QtPrintSupport.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtPrintSupport import * +from PyQt6.QtPrintSupport import * +from PySide2.QtPrintSupport import * +from PySide6.QtPrintSupport import * diff --git a/src/superqt/qtcompat/QtQml.pyi b/src/superqt/qtcompat/QtQml.pyi new file mode 100644 index 0000000..3af6fe2 --- /dev/null +++ b/src/superqt/qtcompat/QtQml.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtQml import * +from PyQt6.QtQml import * +from PySide2.QtQml import * +from PySide6.QtQml import * diff --git a/src/superqt/qtcompat/QtQuick.pyi b/src/superqt/qtcompat/QtQuick.pyi new file mode 100644 index 0000000..a03b7e9 --- /dev/null +++ b/src/superqt/qtcompat/QtQuick.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtQuick import * +from PyQt6.QtQuick import * +from PySide2.QtQuick import * +from PySide6.QtQuick import * diff --git a/src/superqt/qtcompat/QtQuickControls2.pyi b/src/superqt/qtcompat/QtQuickControls2.pyi new file mode 100644 index 0000000..8b29962 --- /dev/null +++ b/src/superqt/qtcompat/QtQuickControls2.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtQuickControls2 import * +from PyQt6.QtQuickControls2 import * +from PySide2.QtQuickControls2 import * +from PySide6.QtQuickControls2 import * diff --git a/src/superqt/qtcompat/QtQuickWidgets.pyi b/src/superqt/qtcompat/QtQuickWidgets.pyi new file mode 100644 index 0000000..b3d4878 --- /dev/null +++ b/src/superqt/qtcompat/QtQuickWidgets.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtQuickWidgets import * +from PyQt6.QtQuickWidgets import * +from PySide2.QtQuickWidgets import * +from PySide6.QtQuickWidgets import * diff --git a/src/superqt/qtcompat/QtRemoteObjects.pyi b/src/superqt/qtcompat/QtRemoteObjects.pyi new file mode 100644 index 0000000..bda5536 --- /dev/null +++ b/src/superqt/qtcompat/QtRemoteObjects.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtRemoteObjects import * +from PyQt6.QtRemoteObjects import * +from PySide2.QtRemoteObjects import * +from PySide6.QtRemoteObjects import * diff --git a/src/superqt/qtcompat/QtScript.pyi b/src/superqt/qtcompat/QtScript.pyi new file mode 100644 index 0000000..1a2b3f3 --- /dev/null +++ b/src/superqt/qtcompat/QtScript.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtScript import * +from PyQt6.QtScript import * +from PySide2.QtScript import * +from PySide6.QtScript import * diff --git a/src/superqt/qtcompat/QtScriptTools.pyi b/src/superqt/qtcompat/QtScriptTools.pyi new file mode 100644 index 0000000..4471e2d --- /dev/null +++ b/src/superqt/qtcompat/QtScriptTools.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtScriptTools import * +from PyQt6.QtScriptTools import * +from PySide2.QtScriptTools import * +from PySide6.QtScriptTools import * diff --git a/src/superqt/qtcompat/QtScxml.pyi b/src/superqt/qtcompat/QtScxml.pyi new file mode 100644 index 0000000..6831e1d --- /dev/null +++ b/src/superqt/qtcompat/QtScxml.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtScxml import * +from PyQt6.QtScxml import * +from PySide2.QtScxml import * +from PySide6.QtScxml import * diff --git a/src/superqt/qtcompat/QtSensors.pyi b/src/superqt/qtcompat/QtSensors.pyi new file mode 100644 index 0000000..00147ba --- /dev/null +++ b/src/superqt/qtcompat/QtSensors.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtSensors import * +from PyQt6.QtSensors import * +from PySide2.QtSensors import * +from PySide6.QtSensors import * diff --git a/src/superqt/qtcompat/QtSerialPort.pyi b/src/superqt/qtcompat/QtSerialPort.pyi new file mode 100644 index 0000000..f018f40 --- /dev/null +++ b/src/superqt/qtcompat/QtSerialPort.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtSerialPort import * +from PyQt6.QtSerialPort import * +from PySide2.QtSerialPort import * +from PySide6.QtSerialPort import * diff --git a/src/superqt/qtcompat/QtSql.pyi b/src/superqt/qtcompat/QtSql.pyi new file mode 100644 index 0000000..d4dad99 --- /dev/null +++ b/src/superqt/qtcompat/QtSql.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtSql import * +from PyQt6.QtSql import * +from PySide2.QtSql import * +from PySide6.QtSql import * diff --git a/src/superqt/qtcompat/QtSvg.pyi b/src/superqt/qtcompat/QtSvg.pyi new file mode 100644 index 0000000..d7fe576 --- /dev/null +++ b/src/superqt/qtcompat/QtSvg.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtSvg import * +from PyQt6.QtSvg import * +from PySide2.QtSvg import * +from PySide6.QtSvg import * diff --git a/src/superqt/qtcompat/QtTest.pyi b/src/superqt/qtcompat/QtTest.pyi new file mode 100644 index 0000000..4bb798e --- /dev/null +++ b/src/superqt/qtcompat/QtTest.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtTest import * +from PyQt6.QtTest import * +from PySide2.QtTest import * +from PySide6.QtTest import * diff --git a/src/superqt/qtcompat/QtTextToSpeech.pyi b/src/superqt/qtcompat/QtTextToSpeech.pyi new file mode 100644 index 0000000..4feebeb --- /dev/null +++ b/src/superqt/qtcompat/QtTextToSpeech.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtTextToSpeech import * +from PyQt6.QtTextToSpeech import * +from PySide2.QtTextToSpeech import * +from PySide6.QtTextToSpeech import * diff --git a/src/superqt/qtcompat/QtUiTools.pyi b/src/superqt/qtcompat/QtUiTools.pyi new file mode 100644 index 0000000..e55769c --- /dev/null +++ b/src/superqt/qtcompat/QtUiTools.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtUiTools import * +from PyQt6.QtUiTools import * +from PySide2.QtUiTools import * +from PySide6.QtUiTools import * diff --git a/src/superqt/qtcompat/QtWebChannel.pyi b/src/superqt/qtcompat/QtWebChannel.pyi new file mode 100644 index 0000000..96f24e2 --- /dev/null +++ b/src/superqt/qtcompat/QtWebChannel.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtWebChannel import * +from PyQt6.QtWebChannel import * +from PySide2.QtWebChannel import * +from PySide6.QtWebChannel import * diff --git a/src/superqt/qtcompat/QtWebEngine.pyi b/src/superqt/qtcompat/QtWebEngine.pyi new file mode 100644 index 0000000..49a66ab --- /dev/null +++ b/src/superqt/qtcompat/QtWebEngine.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtWebEngine import * +from PyQt6.QtWebEngine import * +from PySide2.QtWebEngine import * +from PySide6.QtWebEngine import * diff --git a/src/superqt/qtcompat/QtWebEngineCore.pyi b/src/superqt/qtcompat/QtWebEngineCore.pyi new file mode 100644 index 0000000..e942ee4 --- /dev/null +++ b/src/superqt/qtcompat/QtWebEngineCore.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtWebEngineCore import * +from PyQt6.QtWebEngineCore import * +from PySide2.QtWebEngineCore import * +from PySide6.QtWebEngineCore import * diff --git a/src/superqt/qtcompat/QtWebEngineWidgets.pyi b/src/superqt/qtcompat/QtWebEngineWidgets.pyi new file mode 100644 index 0000000..195a903 --- /dev/null +++ b/src/superqt/qtcompat/QtWebEngineWidgets.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtWebEngineWidgets import * +from PyQt6.QtWebEngineWidgets import * +from PySide2.QtWebEngineWidgets import * +from PySide6.QtWebEngineWidgets import * diff --git a/src/superqt/qtcompat/QtWebSockets.pyi b/src/superqt/qtcompat/QtWebSockets.pyi new file mode 100644 index 0000000..2d0ba40 --- /dev/null +++ b/src/superqt/qtcompat/QtWebSockets.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtWebSockets import * +from PyQt6.QtWebSockets import * +from PySide2.QtWebSockets import * +from PySide6.QtWebSockets import * diff --git a/src/superqt/qtcompat/QtWidgets.py b/src/superqt/qtcompat/QtWidgets.py index 1fa868e..73fe0b5 100644 --- a/src/superqt/qtcompat/QtWidgets.py +++ b/src/superqt/qtcompat/QtWidgets.py @@ -1,42 +1,20 @@ -# -# Copyright © 2014-2015 Colin Duquesnoy -# Copyright © 2009- The Spyder Developmet Team -# -# Licensed under the terms of the MIT License -# (see LICENSE.txt for details) +# type: ignore +from . import API_NAME, _get_qtmodule -""" -Modified from qtpy.QtWidgets -Provides widget classes and functions. -""" +_QtWidgets = _get_qtmodule(__name__) +globals().update(_QtWidgets.__dict__) -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT5: - from PyQt5.QtWidgets import * -elif PYSIDE2: - from PySide2.QtWidgets import * -elif PYQT6: - from PyQt6.QtWidgets import * - - # backwards compat with PyQt5 - # namespace moves: - for cls in (QStyle, QSlider, QSizePolicy, QSpinBox): - for attr in dir(cls): - if not attr[0].isupper(): - continue - ns = getattr(cls, attr) - for name, val in vars(ns).items(): - if not name.startswith("_"): - setattr(cls, name, val) +QApplication = _QtWidgets.QApplication +if not hasattr(QApplication, "exec"): def exec_(self): - self.exec() + _QtWidgets.QApplication.exec(self) - QApplication.exec_ = exec_ + QApplication.exec = exec_ -elif PYSIDE6: - from PySide6.QtWidgets import * # noqa - -else: - raise PythonQtError("No Qt bindings could be found") +# backwargs compat with qt5 +if "6" in API_NAME: + _QtGui = _get_qtmodule("QtGui") + QAction = _QtGui.QAction + QShortcut = _QtGui.QShortcut diff --git a/src/superqt/qtcompat/QtWidgets.pyi b/src/superqt/qtcompat/QtWidgets.pyi new file mode 100644 index 0000000..b59539d --- /dev/null +++ b/src/superqt/qtcompat/QtWidgets.pyi @@ -0,0 +1,12 @@ +from PyQt5.QtWidgets import * +from PyQt6.QtWidgets import * +from PySide2.QtWidgets import * +from PySide6.QtWidgets import * + +QApplication.exec_ = QApplication.exec + +from PyQt6 import QtGui +from PySide6 import QtGui + +QAction = QtGui.QAction +QShortcut = QtGui.QShortcut diff --git a/src/superqt/qtcompat/QtXml.pyi b/src/superqt/qtcompat/QtXml.pyi new file mode 100644 index 0000000..50aa8e2 --- /dev/null +++ b/src/superqt/qtcompat/QtXml.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtXml import * +from PyQt6.QtXml import * +from PySide2.QtXml import * +from PySide6.QtXml import * diff --git a/src/superqt/qtcompat/QtXmlPatterns.pyi b/src/superqt/qtcompat/QtXmlPatterns.pyi new file mode 100644 index 0000000..1ef8044 --- /dev/null +++ b/src/superqt/qtcompat/QtXmlPatterns.pyi @@ -0,0 +1,4 @@ +from PyQt5.QtXmlPatterns import * +from PyQt6.QtXmlPatterns import * +from PySide2.QtXmlPatterns import * +from PySide6.QtXmlPatterns import * diff --git a/src/superqt/qtcompat/__init__.py b/src/superqt/qtcompat/__init__.py index 9269372..44f134c 100644 --- a/src/superqt/qtcompat/__init__.py +++ b/src/superqt/qtcompat/__init__.py @@ -1,166 +1,114 @@ -# -# Copyright © 2009- The Spyder Development Team -# Copyright © 2014-2015 Colin Duquesnoy -# -# Licensed under the terms of the MIT License -# (see LICENSE.txt for details) - -""" -This file is borrowed from qtpy and modified to support PySide6/PyQt6 (drops PyQt4) -""" +from __future__ import annotations import os -import platform import sys import warnings -from distutils.version import LooseVersion +from importlib import abc, import_module, util +from typing import TYPE_CHECKING, Optional, Sequence, Union + +if TYPE_CHECKING: + from importlib.machinery import ModuleSpec + from types import ModuleType -class PythonQtError(RuntimeError): +class QtMissingError(ImportError): """Error raise if no bindings could be selected.""" -class PythonQtWarning(Warning): - """Warning if some features are not implemented in a binding.""" - - -# Qt API environment variable name -QT_API = "QT_API" - -# Names of the expected PyQt5 api -PYQT5_API = ["pyqt5"] - -# Names of the expected PyQt6 api -PYQT6_API = ["pyqt6"] - -# Names of the expected PySide2 api -PYSIDE2_API = ["pyside2"] - -# Names of the expected PySide6 api -PYSIDE6_API = ["pyside6"] - -# Detecting if a binding was specified by the user -binding_specified = QT_API in os.environ - -# Setting a default value for QT_API -os.environ.setdefault(QT_API, "pyqt5") - -API = os.environ[QT_API].lower() -initial_api = API -assert API in (PYQT5_API + PYQT6_API + PYSIDE2_API + PYSIDE6_API) - -PYQT5 = True -PYSIDE2 = PYQT6 = PYSIDE6 = False - -# When `FORCE_QT_API` is set, we disregard -# any previously imported python bindings. -if os.environ.get("FORCE_QT_API") is not None: - if "PyQt5" in sys.modules: - API = initial_api if initial_api in PYQT5_API else "pyqt5" - elif "PySide2" in sys.modules: - API = initial_api if initial_api in PYSIDE2_API else "pyside2" - elif "PyQt6" in sys.modules: - API = initial_api if initial_api in PYQT6_API else "pyqt6" - elif "PySide6" in sys.modules: - API = initial_api if initial_api in PYSIDE6_API else "pyside6" - - -if API in PYQT5_API: - try: - from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # noqa - from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # noqa - - PYSIDE_VERSION = None # noqa - - if sys.platform == "darwin": - macos_version = LooseVersion(platform.mac_ver()[0]) - if macos_version < LooseVersion("10.10"): - if LooseVersion(QT_VERSION) >= LooseVersion("5.9"): - raise PythonQtError( - "Qt 5.9 or higher only works in " - "macOS 10.10 or higher. Your " - "program will fail in this " - "system." - ) - elif macos_version < LooseVersion("10.11"): - if LooseVersion(QT_VERSION) >= LooseVersion("5.11"): - raise PythonQtError( - "Qt 5.11 or higher only works in " - "macOS 10.11 or higher. Your " - "program will fail in this " - "system." - ) - - del macos_version - except ImportError: - API = os.environ["QT_API"] = "pyside2" - -if API in PYSIDE2_API: - try: - from PySide2 import __version__ as PYSIDE_VERSION # noqa - from PySide2.QtCore import __version__ as QT_VERSION # noqa - - PYQT_VERSION = None # noqa - PYQT5 = False - PYSIDE2 = True - - if sys.platform == "darwin": - macos_version = LooseVersion(platform.mac_ver()[0]) - if macos_version < LooseVersion("10.11"): - if LooseVersion(QT_VERSION) >= LooseVersion("5.11"): - raise PythonQtError( - "Qt 5.11 or higher only works in " - "macOS 10.11 or higher. Your " - "program will fail in this " - "system." - ) - - del macos_version - except ImportError: - API = os.environ["QT_API"] = "pyqt6" - -if API in PYQT6_API: - try: - from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # noqa - from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # noqa - - PYSIDE_VERSION = None # noqa - PYQT5 = False - PYQT6 = True - - except ImportError: - API = os.environ["QT_API"] = "pyside6" - -if API in PYSIDE6_API: - try: - from PySide6 import __version__ as PYSIDE_VERSION # noqa - from PySide6.QtCore import __version__ as QT_VERSION # noqa - - PYQT_VERSION = None # noqa - PYQT5 = False - PYSIDE6 = True - - except ImportError: - API = None - -if API is None: - raise PythonQtError( - "No Qt bindings could be found.\nYou must install one of the following packages " - "to use superqt: PyQt5, PyQt6, PySide2, or PySide6" - ) - -# If a correct API name is passed to QT_API and it could not be found, -# switches to another and informs through the warning -if API != initial_api and binding_specified: - warnings.warn( - 'Selected binding "{}" could not be found, ' - 'using "{}"'.format(initial_api, API), - RuntimeWarning, - ) - -API_NAME = { +VALID_APIS = { "pyqt5": "PyQt5", "pyqt6": "PyQt6", "pyside2": "PySide2", "pyside6": "PySide6", -}[API] +} + +# Detecting if a binding was specified by the user +_requested_api = os.getenv("QT_API", "").lower() +_forced_api = os.getenv("FORCE_QT_API") + +# warn if an invalid API has been requested +if _requested_api and _requested_api not in VALID_APIS: + warnings.warn( + f"invalid QT_API specified: {_requested_api}. " + f"Valid values include {set(VALID_APIS)}" + ) + _forced_api = None + _requested_api = "" + +# TODO: FORCE_QT_API requires also using QT_API ... does that make sense? + +# now we'll try to import QtCore +_QtCore: Optional[ModuleType] = None + +# If `FORCE_QT_API` is not set, we first look for previously imported bindings +if not _forced_api: + for api_name, module_name in VALID_APIS.items(): + if module_name in sys.modules: + _QtCore = import_module(f"{module_name}.QtCore") + break + +if _QtCore is None: + # try the requested API first, and if _forced_api is True, + # raise an ImportError if it doesn't work. + # Otherwise go through the list of Valid APIs until something imports + requested = VALID_APIS.get(_requested_api) + for module_name in sorted(VALID_APIS.values(), key=lambda x: x != requested): + try: + _QtCore = import_module(f"{module_name}.QtCore") + break + except ImportError: + if _forced_api: + ImportError( + "FORCE_QT_API set and unable to import requested QT_API: {e}" + ) + +# didn't find one... not going to work +if _QtCore is None: + raise QtMissingError(f"No QtCore could be found. Tried: {VALID_APIS.values()}") + +# load variables based on what we found. +if not _QtCore.__package__: + raise RuntimeError("QtCore does not declare __package__?") + +API_NAME = _QtCore.__package__ +PYSIDE2 = API_NAME == "PySide2" +PYSIDE6 = API_NAME == "PySide6" +PYQT5 = API_NAME == "PyQt5" +PYQT6 = API_NAME == "PyQt6" +QT_VERSION = getattr(_QtCore, "QT_VERSION_STR", "") or getattr(_QtCore, "__version__") + +# lastly, emit a warning if we ended up with an API other than the one requested +if _requested_api and API_NAME != VALID_APIS[_requested_api]: + warnings.warn( + f"Selected binding {_requested_api!r} could not be found, using {API_NAME!r}" + ) + + +# Setup the meta path finder that lets us import anything using `superqt.qtcompat.Mod` +class SuperQtImporter(abc.MetaPathFinder): + def find_spec( + self, + fullname: str, + path: Optional[Sequence[Union[bytes, str]]], + target: Optional[ModuleType] = None, + ) -> Optional[ModuleSpec]: + """Find a spec for the specified module. + + If fullname is superqt.X or superqt.qtcompat.Xx ... + it will look for API_NAME.X instead... + + See https://docs.python.org/3/reference/import.html#the-meta-path + """ + if fullname.startswith(__name__): + spec = fullname.replace(__name__, API_NAME) + return util.find_spec(spec) + return None + + +def _get_qtmodule(mod_name: str) -> ModuleType: + """Convenience to get a submodule from the current QT_API""" + _mod_name = mod_name.rsplit(".", maxsplit=1)[-1] + return import_module(f"{API_NAME}.{_mod_name}") + + +sys.meta_path.append(SuperQtImporter()) diff --git a/src/superqt/sliders/_generic_range_slider.py b/src/superqt/sliders/_generic_range_slider.py index 42e1e1b..b69f34d 100644 --- a/src/superqt/sliders/_generic_range_slider.py +++ b/src/superqt/sliders/_generic_range_slider.py @@ -146,7 +146,7 @@ class _GenericRangeSlider(_GenericSlider[Tuple], Generic[_T]): return super().setStyleSheet(styleSheet + override) def event(self, ev: QEvent) -> bool: - if ev.type() == QEvent.StyleChange: + if ev.type() == QEvent.Type.StyleChange: update_styles_from_stylesheet(self) return super().event(ev) @@ -225,7 +225,7 @@ class _GenericRangeSlider(_GenericSlider[Tuple], Generic[_T]): thickness = self._style.thickness(opt) offset = self._style.offset(opt) - if opt.orientation == Qt.Horizontal: + if opt.orientation == Qt.Orientation.Horizontal: r_bar.setTop(r_bar.center().y() - thickness / 2 + offset) r_bar.setHeight(thickness) r_bar.setLeft(hdl_low.center().x()) @@ -261,9 +261,9 @@ class _GenericRangeSlider(_GenericSlider[Tuple], Generic[_T]): opt.sliderPosition = pos # make pressed handles appear sunken if idx == pidx: - opt.state |= QStyle.State_Sunken + opt.state |= QStyle.StateFlag.State_Sunken else: - opt.state = opt.state & ~QStyle.State_Sunken + opt.state = opt.state & ~QStyle.StateFlag.State_Sunken opt.activeSubControls = SC_HANDLE if idx == hidx else SC_NONE painter.drawComplexControl(CC_SLIDER, opt) @@ -314,11 +314,11 @@ class _GenericRangeSlider(_GenericSlider[Tuple], Generic[_T]): return (SC_HANDLE, len(self._position) - 1) def _execute_scroll(self, steps_to_scroll, modifiers): - if modifiers & Qt.AltModifier: + if modifiers & Qt.KeyboardModifier.AltModifier: self._spreadAllPositions(shrink=steps_to_scroll < 0) else: self._offsetAllPositions(steps_to_scroll) - self.triggerAction(QSlider.SliderMove) + self.triggerAction(QSlider.SliderAction.SliderMove) def _has_scroll_space_left(self, offset): return (offset > 0 and max(self._value) < self._maximum) or ( diff --git a/src/superqt/sliders/_generic_slider.py b/src/superqt/sliders/_generic_slider.py index fdbd9aa..094bd8c 100644 --- a/src/superqt/sliders/_generic_slider.py +++ b/src/superqt/sliders/_generic_slider.py @@ -74,7 +74,7 @@ class _GenericSlider(QSlider, Generic[_T]): self._control_fraction = 0.04 super().__init__(*args, **kwargs) - self.setAttribute(Qt.WA_Hover) + self.setAttribute(Qt.WidgetAttribute.WA_Hover) # ############### QtOverrides ####################### @@ -134,7 +134,7 @@ class _GenericSlider(QSlider, Generic[_T]): oldMax, self._maximum = self._maximum, float(max(min, max_)) if oldMin != self._minimum or oldMax != self._maximum: - self.sliderChange(self.SliderRangeChange) + self.sliderChange(self.SliderChange.SliderRangeChange) self.rangeChanged.emit(self._minimum, self._maximum) self.setValue(self._value) # re-bound @@ -159,15 +159,18 @@ class _GenericSlider(QSlider, Generic[_T]): option.orientation = self.orientation() option.tickPosition = self.tickPosition() option.upsideDown = ( - self.invertedAppearance() != (option.direction == Qt.RightToLeft) - if self.orientation() == Qt.Horizontal + self.invertedAppearance() + != (option.direction == Qt.LayoutDirection.RightToLeft) + if self.orientation() == Qt.Orientation.Horizontal else not self.invertedAppearance() ) - option.direction = Qt.LeftToRight # we use the upsideDown option instead + option.direction = ( + Qt.LayoutDirection.LeftToRight + ) # we use the upsideDown option instead # option.sliderValue = self._value # type: ignore # option.singleStep = self._singleStep # type: ignore - if self.orientation() == Qt.Horizontal: - option.state |= QStyle.State_Horizontal + if self.orientation() == Qt.Orientation.Horizontal: + option.state |= QStyle.StateFlag.State_Horizontal # scale style option to integer space option.minimum = 0 @@ -178,11 +181,11 @@ class _GenericSlider(QSlider, Generic[_T]): self._fixStyleOption(option) def event(self, ev: QEvent) -> bool: - if ev.type() == QEvent.WindowActivate: + if ev.type() == QEvent.Type.WindowActivate: self.update() - elif ev.type() in (QEvent.HoverEnter, QEvent.HoverMove): + elif ev.type() in (QEvent.Type.HoverEnter, QEvent.Type.HoverMove): self._updateHoverControl(_event_position(ev)) - elif ev.type() == QEvent.HoverLeave: + elif ev.type() == QEvent.Type.HoverLeave: self._hoverControl = SC_NONE lastHoverRect, self._hoverRect = self._hoverRect, QRect() self.update(lastHoverRect) @@ -198,7 +201,7 @@ class _GenericSlider(QSlider, Generic[_T]): pos = _event_position(ev) # If the mouse button used is allowed to set the value - if ev.button() in (Qt.LeftButton, Qt.MiddleButton): + if ev.button() in (Qt.MouseButton.LeftButton, Qt.MouseButton.MiddleButton): self._updatePressedControl(pos) if self._pressedControl == SC_HANDLE: opt = self._styleOption @@ -206,8 +209,8 @@ class _GenericSlider(QSlider, Generic[_T]): offset = sr.center() - sr.topLeft() new_pos = self._pixelPosToRangeValue(self._pick(pos - offset)) self.setSliderPosition(new_pos) - self.triggerAction(QSlider.SliderMove) - self.setRepeatAction(QSlider.SliderNoAction) + self.triggerAction(QSlider.SliderAction.SliderMove) + self.setRepeatAction(QSlider.SliderAction.SliderNoAction) self.update() # elif: deal with PageSetButtons @@ -215,7 +218,7 @@ class _GenericSlider(QSlider, Generic[_T]): ev.ignore() if self._pressedControl != SC_NONE: - self.setRepeatAction(QSlider.SliderNoAction) + self.setRepeatAction(QSlider.SliderAction.SliderNoAction) self._setClickOffset(pos) self.update() self.setSliderDown(True) @@ -238,7 +241,7 @@ class _GenericSlider(QSlider, Generic[_T]): ev.accept() oldPressed = self._pressedControl self._pressedControl = SC_NONE - self.setRepeatAction(QSlider.SliderNoAction) + self.setRepeatAction(QSlider.SliderAction.SliderNoAction) if oldPressed != SC_NONE: self.setSliderDown(False) self.update() @@ -251,7 +254,7 @@ class _GenericSlider(QSlider, Generic[_T]): if e.inverted(): delta *= -1 - orientation = Qt.Vertical if vertical else Qt.Horizontal + orientation = Qt.Orientation.Vertical if vertical else Qt.Orientation.Horizontal if self._scrollByDelta(orientation, e.modifiers(), delta): e.accept() @@ -261,7 +264,7 @@ class _GenericSlider(QSlider, Generic[_T]): # draw groove and ticks opt.subControls = SC_GROOVE - if opt.tickPosition != QSlider.NoTicks: + if opt.tickPosition != QSlider.TickPosition.NoTicks: opt.subControls |= SC_TICKMARKS painter.drawComplexControl(CC_SLIDER, opt) @@ -287,12 +290,12 @@ class _GenericSlider(QSlider, Generic[_T]): return int(min(QOVERFLOW, val / (self._maximum - self._minimum) * _max)) def _pick(self, pt: QPoint) -> int: - return pt.x() if self.orientation() == Qt.Horizontal else pt.y() + return pt.x() if self.orientation() == Qt.Orientation.Horizontal else pt.y() def _setSteps(self, single: float, page: float): self._singleStep = single self._pageStep = page - self.sliderChange(QSlider.SliderStepsChange) + self.sliderChange(QSlider.SliderChange.SliderStepsChange) def _doSliderMove(self): if not self.hasTracking(): @@ -300,7 +303,7 @@ class _GenericSlider(QSlider, Generic[_T]): if self.isSliderDown(): self.sliderMoved.emit(self.sliderPosition()) if self.hasTracking() and not self._blocktracking: - self.triggerAction(QSlider.SliderMove) + self.triggerAction(QSlider.SliderAction.SliderMove) @property def _styleOption(self): @@ -311,7 +314,7 @@ class _GenericSlider(QSlider, Generic[_T]): def _updateHoverControl(self, pos: QPoint) -> bool: lastHoverRect = self._hoverRect lastHoverControl = self._hoverControl - doesHover = self.testAttribute(Qt.WA_Hover) + doesHover = self.testAttribute(Qt.WidgetAttribute.WA_Hover) if lastHoverControl != self._newHoverControl(pos) and doesHover: self.update(lastHoverRect) self.update(self._hoverRect) @@ -351,7 +354,7 @@ class _GenericSlider(QSlider, Generic[_T]): opt.subControls = SC_HANDLE if self._pressedControl: opt.activeSubControls = self._pressedControl - opt.state |= QStyle.State_Sunken + opt.state |= QStyle.StateFlag.State_Sunken else: opt.activeSubControls = self._hoverControl @@ -364,7 +367,7 @@ class _GenericSlider(QSlider, Generic[_T]): gr = self.style().subControlRect(CC_SLIDER, opt, SC_GROOVE, self) sr = self.style().subControlRect(CC_SLIDER, opt, SC_HANDLE, self) - if self.orientation() == Qt.Horizontal: + if self.orientation() == Qt.Orientation.Horizontal: sliderLength = sr.width() sliderMin = gr.x() sliderMax = gr.right() - sliderLength + 1 @@ -385,14 +388,14 @@ class _GenericSlider(QSlider, Generic[_T]): pg_step = self._pageStep # in Qt scrolling to the right gives negative values. - if orientation == Qt.Horizontal: + if orientation == Qt.Orientation.Horizontal: delta *= -1 offset = delta / 120 - if modifiers & Qt.ShiftModifier: + if modifiers & Qt.KeyboardModifier.ShiftModifier: # Scroll one page regardless of delta: steps_to_scroll = max(-pg_step, min(pg_step, offset * pg_step)) self._offset_accum = 0 - elif modifiers & Qt.ControlModifier: + elif modifiers & Qt.KeyboardModifier.ControlModifier: _range = self._maximum - self._minimum steps_to_scroll = offset * _range * self._control_fraction self._offset_accum = 0 @@ -440,7 +443,7 @@ class _GenericSlider(QSlider, Generic[_T]): def _execute_scroll(self, steps_to_scroll, modifiers): self._setPosition(self._bound(self._overflowSafeAdd(steps_to_scroll))) - self.triggerAction(QSlider.SliderMove) + self.triggerAction(QSlider.SliderAction.SliderMove) def _effectiveSingleStep(self) -> float: return self._singleStep * self._repeatMultiplier diff --git a/src/superqt/sliders/_labeled.py b/src/superqt/sliders/_labeled.py index 9469414..fdf6e61 100644 --- a/src/superqt/sliders/_labeled.py +++ b/src/superqt/sliders/_labeled.py @@ -93,7 +93,7 @@ class _SliderProxy: def _handle_overloaded_slider_sig(args, kwargs): parent = None - orientation = Qt.Vertical + orientation = Qt.Orientation.Vertical errmsg = ( "TypeError: arguments did not match any overloaded call:\n" " QSlider(parent: QWidget = None)\n" @@ -137,17 +137,17 @@ class QLabeledSlider(_SliderProxy, QAbstractSlider): def setOrientation(self, orientation): """Set orientation, value will be 'horizontal' or 'vertical'.""" self._slider.setOrientation(orientation) - if orientation == Qt.Vertical: + if orientation == Qt.Orientation.Vertical: layout = QVBoxLayout() - layout.addWidget(self._slider, alignment=Qt.AlignHCenter) - layout.addWidget(self._label, alignment=Qt.AlignHCenter) - self._label.setAlignment(Qt.AlignCenter) + layout.addWidget(self._slider, alignment=Qt.AlignmentFlag.AlignHCenter) + layout.addWidget(self._label, alignment=Qt.AlignmentFlag.AlignHCenter) + self._label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.setSpacing(1) else: layout = QHBoxLayout() layout.addWidget(self._slider) layout.addWidget(self._label) - self._label.setAlignment(Qt.AlignRight) + self._label.setAlignment(Qt.AlignmentFlag.AlignRight) layout.setSpacing(6) old_layout = self.layout() @@ -185,7 +185,7 @@ class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): def __init__(self, *args, **kwargs) -> None: parent, orientation = _handle_overloaded_slider_sig(args, kwargs) super().__init__(parent) - self.setAttribute(Qt.WA_ShowWithoutActivating) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) self._handle_labels = [] self._handle_label_position: LabelPosition = LabelPosition.LabelsAbove @@ -198,10 +198,14 @@ class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): self._slider.rangeChanged.connect(self.rangeChanged.emit) self._min_label = SliderLabel( - self._slider, alignment=Qt.AlignLeft, connect=self._min_label_edited + self._slider, + alignment=Qt.AlignmentFlag.AlignLeft, + connect=self._min_label_edited, ) self._max_label = SliderLabel( - self._slider, alignment=Qt.AlignRight, connect=self._max_label_edited + self._slider, + alignment=Qt.AlignmentFlag.AlignRight, + connect=self._max_label_edited, ) self.setEdgeLabelMode(EdgeLabelMode.LabelIsRange) @@ -252,7 +256,7 @@ class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): if not self._handle_labels: return - horizontal = self.orientation() == Qt.Horizontal + horizontal = self.orientation() == Qt.Orientation.Horizontal labels_above = self._handle_label_position == LabelPosition.LabelsAbove last_edge = None @@ -342,7 +346,7 @@ class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): """Set orientation, value will be 'horizontal' or 'vertical'.""" self._slider.setOrientation(orientation) - if orientation == Qt.Vertical: + if orientation == Qt.Orientation.Vertical: layout = QVBoxLayout() layout.setSpacing(1) layout.addWidget(self._max_label) @@ -355,7 +359,7 @@ class QLabeledRangeSlider(_SliderProxy, QAbstractSlider): marg = (0, 0, 0, 0) else: marg = (0, 0, 20, 0) - layout.setAlignment(Qt.AlignCenter) + layout.setAlignment(Qt.AlignmentFlag.AlignCenter) else: layout = QHBoxLayout() layout.setSpacing(7) @@ -406,18 +410,22 @@ class QLabeledDoubleRangeSlider(QLabeledRangeSlider): class SliderLabel(QDoubleSpinBox): def __init__( - self, slider: QSlider, parent=None, alignment=Qt.AlignCenter, connect=None + self, + slider: QSlider, + parent=None, + alignment=Qt.AlignmentFlag.AlignCenter, + connect=None, ) -> None: super().__init__(parent=parent) self._slider = slider - self.setFocusPolicy(Qt.ClickFocus) + self.setFocusPolicy(Qt.FocusPolicy.ClickFocus) self.setMode(EdgeLabelMode.LabelIsValue) self.setDecimals(0) self.setRange(slider.minimum(), slider.maximum()) slider.rangeChanged.connect(self._update_size) self.setAlignment(alignment) - self.setButtonSymbols(QSpinBox.NoButtons) + self.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons) self.setStyleSheet("background:transparent; border: 0;") if connect is not None: self.editingFinished.connect(lambda: connect(self.value())) @@ -449,7 +457,9 @@ class SliderLabel(QDoubleSpinBox): # get the final size hint opt = QStyleOptionSpinBox() self.initStyleOption(opt) - size = self.style().sizeFromContents(QStyle.CT_SpinBox, opt, QSize(w, h), self) + size = self.style().sizeFromContents( + QStyle.ContentsType.CT_SpinBox, opt, QSize(w, h), self + ) self.setFixedSize(size) def setValue(self, val): diff --git a/src/superqt/sliders/_range_style.py b/src/superqt/sliders/_range_style.py index 6fc2764..7dc1de1 100644 --- a/src/superqt/sliders/_range_style.py +++ b/src/superqt/sliders/_range_style.py @@ -5,7 +5,7 @@ import re from dataclasses import dataclass, replace from typing import TYPE_CHECKING -from ..qtcompat import PYQT_VERSION +from ..qtcompat import QT_VERSION from ..qtcompat.QtCore import Qt from ..qtcompat.QtGui import ( QBrush, @@ -40,9 +40,9 @@ class RangeSliderStyle: def brush(self, opt: QStyleOptionSlider) -> QBrush: cg = opt.palette.currentColorGroup() attr = { - QPalette.Active: "brush_active", # 0 - QPalette.Disabled: "brush_disabled", # 1 - QPalette.Inactive: "brush_inactive", # 2 + QPalette.ColorGroup.Active: "brush_active", # 0 + QPalette.ColorGroup.Disabled: "brush_disabled", # 1 + QPalette.ColorGroup.Inactive: "brush_inactive", # 2 }[cg] _val = getattr(self, attr) if not _val: @@ -67,7 +67,7 @@ class RangeSliderStyle: else: val = _val - if opt.tickPosition != QSlider.NoTicks: + if opt.tickPosition != QSlider.TickPosition.NoTicks: val.setAlphaF(self.tick_bar_alpha or SYSTEM_STYLE.tick_bar_alpha) return QBrush(val) @@ -75,16 +75,16 @@ class RangeSliderStyle: def pen(self, opt: QStyleOptionSlider) -> Qt.PenStyle | QColor: cg = opt.palette.currentColorGroup() attr = { - QPalette.Active: "pen_active", # 0 - QPalette.Disabled: "pen_disabled", # 1 - QPalette.Inactive: "pen_inactive", # 2 + QPalette.ColorGroup.Active: "pen_active", # 0 + QPalette.ColorGroup.Disabled: "pen_disabled", # 1 + QPalette.ColorGroup.Inactive: "pen_inactive", # 2 }[cg] val = getattr(self, attr) or getattr(SYSTEM_STYLE, attr) if not val: - return Qt.NoPen + return Qt.PenStyle.NoPen if isinstance(val, str): val = QColor(val) - if opt.tickPosition != QSlider.NoTicks: + if opt.tickPosition != QSlider.TickPosition.NoTicks: val.setAlphaF(self.tick_bar_alpha or SYSTEM_STYLE.tick_bar_alpha) return val @@ -93,18 +93,18 @@ class RangeSliderStyle: tp = opt.tickPosition off = 0 if not self.has_stylesheet: - if opt.orientation == Qt.Horizontal: + if opt.orientation == Qt.Orientation.Horizontal: off += self.h_offset or SYSTEM_STYLE.h_offset or 0 else: off += self.v_offset or SYSTEM_STYLE.v_offset or 0 - if tp == QSlider.TicksAbove: + if tp == QSlider.TickPosition.TicksAbove: off += self.tick_offset or SYSTEM_STYLE.tick_offset - elif tp == QSlider.TicksBelow: + elif tp == QSlider.TickPosition.TicksBelow: off -= self.tick_offset or SYSTEM_STYLE.tick_offset return off def thickness(self, opt: QStyleOptionSlider) -> float: - if opt.orientation == Qt.Horizontal: + if opt.orientation == Qt.Orientation.Horizontal: return self.horizontal_thickness or SYSTEM_STYLE.horizontal_thickness else: return self.vertical_thickness or SYSTEM_STYLE.vertical_thickness @@ -139,7 +139,7 @@ CATALINA_STYLE = replace( tick_offset=4, ) -if PYQT_VERSION and int(PYQT_VERSION.split(".")[0]) == 6: +if QT_VERSION and int(QT_VERSION.split(".")[0]) == 6: CATALINA_STYLE = replace(CATALINA_STYLE, tick_offset=2) BIG_SUR_STYLE = replace( @@ -154,7 +154,7 @@ BIG_SUR_STYLE = replace( tick_bar_alpha=0.2, ) -if PYQT_VERSION and int(PYQT_VERSION.split(".")[0]) == 6: +if QT_VERSION and int(QT_VERSION.split(".")[0]) == 6: BIG_SUR_STYLE = replace(BIG_SUR_STYLE, tick_offset=-3) WINDOWS_STYLE = replace( diff --git a/src/superqt/spinbox/_intspin.py b/src/superqt/spinbox/_intspin.py index 6d2a024..0b6cef6 100644 --- a/src/superqt/spinbox/_intspin.py +++ b/src/superqt/spinbox/_intspin.py @@ -93,7 +93,7 @@ class QLargeIntSpinBox(QAbstractSpinBox): return super().closeEvent(e) def keyPressEvent(self, e) -> None: - if e.key() in (Qt.Key_Enter, Qt.Key_Return): + if e.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return): self._interpret( _EmitPolicy.AlwaysEmit if self.keyboardTracking() @@ -112,13 +112,13 @@ class QLargeIntSpinBox(QAbstractSpinBox): self._setValue(self._bound(self._value + (step * steps)), e) def stepEnabled(self): - flags = QAbstractSpinBox.StepNone + flags = QAbstractSpinBox.StepEnabledFlag.StepNone if self.isReadOnly(): return flags if self._value < self._maximum: - flags |= QAbstractSpinBox.StepUpEnabled + flags |= QAbstractSpinBox.StepEnabledFlag.StepUpEnabled if self._value > self._minimum: - flags |= QAbstractSpinBox.StepDownEnabled + flags |= QAbstractSpinBox.StepEnabledFlag.StepDownEnabled return flags def sizeHint(self): @@ -134,7 +134,9 @@ class QLargeIntSpinBox(QAbstractSpinBox): opt = QStyleOptionSpinBox() self.initStyleOption(opt) hint = QSize(w, h) - return self.style().sizeFromContents(QStyle.CT_SpinBox, opt, hint, self) + return self.style().sizeFromContents( + QStyle.ContentsType.CT_SpinBox, opt, hint, self + ) # ############### Implementation Details ####################### diff --git a/tests/test_eliding_label.py b/tests/test_eliding_label.py index 318bacf..6b243f7 100644 --- a/tests/test_eliding_label.py +++ b/tests/test_eliding_label.py @@ -29,7 +29,7 @@ def test_wrapped_eliding_label(qtbot): wdg = QElidingLabel(TEXT) qtbot.addWidget(wdg) assert not wdg.wordWrap() - assert 630 < wdg.sizeHint().width() < 638 + assert 630 < wdg.sizeHint().width() < 640 assert wdg._elidedText().endswith("…") wdg.resize(QSize(200, 100)) assert wdg.text() == TEXT diff --git a/tests/test_large_int_spinbox.py b/tests/test_large_int_spinbox.py index 14d63f8..e9845fd 100644 --- a/tests/test_large_int_spinbox.py +++ b/tests/test_large_int_spinbox.py @@ -62,7 +62,7 @@ def test_keyboard_tracking(qtbot): assert sb._pending_emit is True with qtbot.waitSignal(sb.valueChanged) as sgnl: - qtbot.keyPress(sb, Qt.Key_Enter) + qtbot.keyPress(sb, Qt.Key.Key_Enter) assert sgnl.args == [20] assert sb._pending_emit is False diff --git a/tests/test_sliders/_testutil.py b/tests/test_sliders/_testutil.py index 9a41ab7..93ecd4c 100644 --- a/tests/test_sliders/_testutil.py +++ b/tests/test_sliders/_testutil.py @@ -18,9 +18,15 @@ skip_on_linux_qt6 = pytest.mark.skipif( ) -def _mouse_event(pos=QPointF(), type_=QEvent.MouseMove): +def _mouse_event(pos=QPointF(), type_=QEvent.Type.MouseMove): """Create a mouse event of `type_` at `pos`.""" - return QMouseEvent(type_, QPointF(pos), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) + return QMouseEvent( + type_, + QPointF(pos), + Qt.MouseButton.LeftButton, + Qt.MouseButton.LeftButton, + Qt.KeyboardModifier.NoModifier, + ) def _wheel_event(arc): @@ -31,11 +37,11 @@ def _wheel_event(arc): QPointF(), QPoint(arc, arc), QPoint(arc, arc), - Qt.NoButton, - Qt.NoModifier, - Qt.ScrollBegin, + Qt.MouseButton.NoButton, + Qt.KeyboardModifier.NoModifier, + Qt.ScrollPhase.ScrollBegin, False, - Qt.MouseEventSynthesizedByQt, + Qt.MouseEventSource.MouseEventSynthesizedByQt, ) with suppress(TypeError): return QWheelEvent( @@ -44,12 +50,12 @@ def _wheel_event(arc): QPoint(-arc, -arc), QPoint(-arc, -arc), 1, - Qt.Vertical, - Qt.NoButton, - Qt.NoModifier, - Qt.ScrollBegin, + Qt.Orientation.Vertical, + Qt.MouseButton.NoButton, + Qt.KeyboardModifier.NoModifier, + Qt.ScrollPhase.ScrollBegin, False, - Qt.MouseEventSynthesizedByQt, + Qt.MouseEventSource.MouseEventSynthesizedByQt, ) return QWheelEvent( @@ -58,9 +64,9 @@ def _wheel_event(arc): QPoint(arc, arc), QPoint(arc, arc), 1, - Qt.Vertical, - Qt.NoButton, - Qt.NoModifier, + Qt.Orientation.Vertical, + Qt.MouseButton.NoButton, + Qt.KeyboardModifier.NoModifier, ) diff --git a/tests/test_sliders/test_generic_slider.py b/tests/test_sliders/test_generic_slider.py index 45ab056..8ff6983 100644 --- a/tests/test_sliders/test_generic_slider.py +++ b/tests/test_sliders/test_generic_slider.py @@ -11,7 +11,7 @@ from superqt.sliders._generic_slider import _GenericSlider, _sliderValueFromPosi from ._testutil import _linspace, _mouse_event, _wheel_event, skip_on_linux_qt6 -@pytest.fixture(params=[Qt.Horizontal, Qt.Vertical]) +@pytest.fixture(params=[Qt.Orientation.Horizontal, Qt.Orientation.Vertical]) def gslider(qtbot, request): slider = _GenericSlider(request.param) qtbot.addWidget(slider) @@ -67,7 +67,7 @@ def test_float_values(gslider: _GenericSlider, qtbot): def test_ticks(gslider: _GenericSlider, qtbot): gslider.setTickInterval(0.3) assert gslider.tickInterval() == 0.3 - gslider.setTickPosition(gslider.TicksAbove) + gslider.setTickPosition(gslider.TickPosition.TicksAbove) gslider.show() @@ -82,26 +82,32 @@ def test_press_move_release(gslider: _GenericSlider, qtbot): opt = QStyleOptionSlider() gslider.initStyleOption(opt) style = gslider.style() - hrect = style.subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle) + hrect = style.subControlRect( + QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle + ) handle_pos = gslider.mapToGlobal(hrect.center()) with qtbot.waitSignal(gslider.sliderPressed): - qtbot.mousePress(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_SliderHandle with qtbot.waitSignals([gslider.sliderMoved, gslider.valueChanged]): - shift = QPoint(0, -8) if gslider.orientation() == Qt.Vertical else QPoint(8, 0) + shift = ( + QPoint(0, -8) + if gslider.orientation() == Qt.Orientation.Vertical + else QPoint(8, 0) + ) gslider.mouseMoveEvent(_mouse_event(handle_pos + shift)) with qtbot.waitSignal(gslider.sliderReleased): - qtbot.mouseRelease(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mouseRelease(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_None gslider.show() with qtbot.waitSignal(gslider.sliderPressed): - qtbot.mousePress(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) @skip_on_linux_qt6 @@ -110,15 +116,19 @@ def test_hover(gslider: _GenericSlider): opt = QStyleOptionSlider() gslider.initStyleOption(opt) style = gslider.style() - hrect = style.subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle) + hrect = style.subControlRect( + QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle + ) handle_pos = QPointF(gslider.mapToGlobal(hrect.center())) assert gslider._hoverControl == QStyle.SubControl.SC_None - gslider.event(QHoverEvent(QEvent.HoverEnter, handle_pos, QPointF())) + gslider.event(QHoverEvent(QEvent.Type.HoverEnter, handle_pos, QPointF())) assert gslider._hoverControl == QStyle.SubControl.SC_SliderHandle - gslider.event(QHoverEvent(QEvent.HoverLeave, QPointF(-1000, -1000), handle_pos)) + gslider.event( + QHoverEvent(QEvent.Type.HoverLeave, QPointF(-1000, -1000), handle_pos) + ) assert gslider._hoverControl == QStyle.SubControl.SC_None diff --git a/tests/test_sliders/test_range_slider.py b/tests/test_sliders/test_range_slider.py index a7d33d3..ea40e70 100644 --- a/tests/test_sliders/test_range_slider.py +++ b/tests/test_sliders/test_range_slider.py @@ -10,7 +10,7 @@ from superqt.qtcompat.QtWidgets import QStyle, QStyleOptionSlider from ._testutil import _linspace, _mouse_event, _wheel_event, skip_on_linux_qt6 -@pytest.fixture(params=[Qt.Horizontal, Qt.Vertical]) +@pytest.fixture(params=[Qt.Orientation.Horizontal, Qt.Orientation.Vertical]) def gslider(qtbot, request): slider = QDoubleRangeSlider(request.param) qtbot.addWidget(slider) @@ -98,7 +98,7 @@ def test_slider_extremes(gslider: QRangeSlider, mag, qtbot): def test_ticks(gslider: QRangeSlider, qtbot): gslider.setTickInterval(0.3) assert gslider.tickInterval() == 0.3 - gslider.setTickPosition(gslider.TicksAbove) + gslider.setTickPosition(gslider.TickPosition.TicksAbove) gslider.show() @@ -112,26 +112,32 @@ def test_press_move_release(gslider: QRangeSlider, qtbot): opt = QStyleOptionSlider() gslider.initStyleOption(opt) style = gslider.style() - hrect = style.subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle) + hrect = style.subControlRect( + QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle + ) handle_pos = gslider.mapToGlobal(hrect.center()) with qtbot.waitSignal(gslider.sliderPressed): - qtbot.mousePress(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_SliderHandle with qtbot.waitSignals([gslider.sliderMoved, gslider.valueChanged]): - shift = QPoint(0, -8) if gslider.orientation() == Qt.Vertical else QPoint(8, 0) + shift = ( + QPoint(0, -8) + if gslider.orientation() == Qt.Orientation.Vertical + else QPoint(8, 0) + ) gslider.mouseMoveEvent(_mouse_event(handle_pos + shift)) with qtbot.waitSignal(gslider.sliderReleased): - qtbot.mouseRelease(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mouseRelease(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) assert gslider._pressedControl == QStyle.SubControl.SC_None gslider.show() with qtbot.waitSignal(gslider.sliderPressed): - qtbot.mousePress(gslider, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(gslider, Qt.MouseButton.LeftButton, pos=handle_pos) @skip_on_linux_qt6 @@ -142,10 +148,12 @@ def test_hover(gslider: QRangeSlider): assert gslider._hoverControl == QStyle.SubControl.SC_None - gslider.event(QHoverEvent(QEvent.HoverEnter, handle_pos, QPointF())) + gslider.event(QHoverEvent(QEvent.Type.HoverEnter, handle_pos, QPointF())) assert gslider._hoverControl == QStyle.SubControl.SC_SliderHandle - gslider.event(QHoverEvent(QEvent.HoverLeave, QPointF(-1000, -1000), handle_pos)) + gslider.event( + QHoverEvent(QEvent.Type.HoverLeave, QPointF(-1000, -1000), handle_pos) + ) assert gslider._hoverControl == QStyle.SubControl.SC_None diff --git a/tests/test_sliders/test_single_value_sliders.py b/tests/test_sliders/test_single_value_sliders.py index f35c670..a1ac30f 100644 --- a/tests/test_sliders/test_single_value_sliders.py +++ b/tests/test_sliders/test_single_value_sliders.py @@ -20,7 +20,10 @@ from ._testutil import ( ) -@pytest.fixture(params=[Qt.Horizontal, Qt.Vertical], ids=["horizontal", "vertical"]) +@pytest.fixture( + params=[Qt.Orientation.Horizontal, Qt.Orientation.Vertical], + ids=["horizontal", "vertical"], +) def orientation(request): return request.param @@ -97,13 +100,13 @@ def test_float_values(sld: _GenericSlider, qtbot): def test_ticks(sld: _GenericSlider, qtbot): sld.setTickInterval(3) assert sld.tickInterval() == 3 - sld.setTickPosition(QSlider.TicksAbove) + sld.setTickPosition(QSlider.TickPosition.TicksAbove) sld.show() @pytest.mark.skipif(platform.system() != "Darwin", reason="cross-platform is tricky") def test_press_move_release(sld: _GenericSlider, qtbot): - if hasattr(sld, "_slider") and sld._slider.orientation() == Qt.Vertical: + if hasattr(sld, "_slider") and sld._slider.orientation() == Qt.Orientation.Vertical: pytest.xfail("test failing for vertical at the moment") _real_sld = getattr(sld, "_slider", sld) @@ -114,11 +117,13 @@ def test_press_move_release(sld: _GenericSlider, qtbot): opt = QStyleOptionSlider() _real_sld.initStyleOption(opt) style = _real_sld.style() - hrect = style.subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle) + hrect = style.subControlRect( + QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle + ) handle_pos = _real_sld.mapToGlobal(hrect.center()) with qtbot.waitSignal(_real_sld.sliderPressed, timeout=300): - qtbot.mousePress(_real_sld, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(_real_sld, Qt.MouseButton.LeftButton, pos=handle_pos) with suppress(AttributeError): assert sld._pressedControl == QStyle.SubControl.SC_SliderHandle @@ -127,19 +132,21 @@ def test_press_move_release(sld: _GenericSlider, qtbot): [_real_sld.sliderMoved, _real_sld.valueChanged], timeout=300 ): shift = ( - QPoint(0, -8) if _real_sld.orientation() == Qt.Vertical else QPoint(8, 0) + QPoint(0, -8) + if _real_sld.orientation() == Qt.Orientation.Vertical + else QPoint(8, 0) ) _real_sld.mouseMoveEvent(_mouse_event(handle_pos + shift)) with qtbot.waitSignal(_real_sld.sliderReleased, timeout=300): - qtbot.mouseRelease(_real_sld, Qt.LeftButton, pos=handle_pos) + qtbot.mouseRelease(_real_sld, Qt.MouseButton.LeftButton, pos=handle_pos) with suppress(AttributeError): assert _real_sld._pressedControl == QStyle.SubControl.SC_None sld.show() with qtbot.waitSignal(_real_sld.sliderPressed, timeout=300): - qtbot.mousePress(_real_sld, Qt.LeftButton, pos=handle_pos) + qtbot.mousePress(_real_sld, Qt.MouseButton.LeftButton, pos=handle_pos) @skip_on_linux_qt6 @@ -150,18 +157,20 @@ def test_hover(sld: _GenericSlider): opt = QStyleOptionSlider() _real_sld.initStyleOption(opt) hrect = _real_sld.style().subControlRect( - QStyle.CC_Slider, opt, QStyle.SC_SliderHandle + QStyle.ComplexControl.CC_Slider, opt, QStyle.SubControl.SC_SliderHandle ) handle_pos = QPointF(sld.mapToGlobal(hrect.center())) with suppress(AttributeError): # for QSlider assert _real_sld._hoverControl == QStyle.SubControl.SC_None - _real_sld.event(QHoverEvent(QEvent.HoverEnter, handle_pos, QPointF())) + _real_sld.event(QHoverEvent(QEvent.Type.HoverEnter, handle_pos, QPointF())) with suppress(AttributeError): # for QSlider assert _real_sld._hoverControl == QStyle.SubControl.SC_SliderHandle - _real_sld.event(QHoverEvent(QEvent.HoverLeave, QPointF(-1000, -1000), handle_pos)) + _real_sld.event( + QHoverEvent(QEvent.Type.HoverLeave, QPointF(-1000, -1000), handle_pos) + ) with suppress(AttributeError): # for QSlider assert _real_sld._hoverControl == QStyle.SubControl.SC_None diff --git a/tests/test_sliders/test_slider.py b/tests/test_sliders/test_slider.py index f81e78c..6abd312 100644 --- a/tests/test_sliders/test_slider.py +++ b/tests/test_sliders/test_slider.py @@ -15,13 +15,13 @@ skipmouse = pytest.mark.skipif(NOT_LINUX or NOT_PYSIDE2, reason="mouse tests fin @pytest.mark.parametrize("orientation", ["Horizontal", "Vertical"]) def test_basic(qtbot, orientation): - rs = QRangeSlider(getattr(Qt, orientation)) + rs = QRangeSlider(getattr(Qt.Orientation, orientation)) qtbot.addWidget(rs) @pytest.mark.parametrize("orientation", ["Horizontal", "Vertical"]) def test_value(qtbot, orientation): - rs = QRangeSlider(getattr(Qt, orientation)) + rs = QRangeSlider(getattr(Qt.Orientation, orientation)) qtbot.addWidget(rs) rs.setValue([10, 20]) assert rs.value() == (10, 20) @@ -29,7 +29,7 @@ def test_value(qtbot, orientation): @pytest.mark.parametrize("orientation", ["Horizontal", "Vertical"]) def test_range(qtbot, orientation): - rs = QRangeSlider(getattr(Qt, orientation)) + rs = QRangeSlider(getattr(Qt.Orientation, orientation)) qtbot.addWidget(rs) rs.setValue([10, 20]) assert rs.value() == (10, 20) @@ -41,7 +41,7 @@ def test_range(qtbot, orientation): @skipmouse def test_drag_handles(qtbot): - rs = QRangeSlider(Qt.Horizontal) + rs = QRangeSlider(Qt.Orientation.Horizontal) qtbot.addWidget(rs) rs.setRange(0, 99) rs.setValue((20, 80)) @@ -51,7 +51,7 @@ def test_drag_handles(qtbot): # press the left handle pos = rs._handleRect(0).center() with qtbot.waitSignal(rs.sliderPressed): - qtbot.mousePress(rs, Qt.LeftButton, pos=pos) + qtbot.mousePress(rs, Qt.MouseButton.LeftButton, pos=pos) assert rs._pressedControl == SC_HANDLE assert rs._pressedIndex == 0 @@ -62,7 +62,7 @@ def test_drag_handles(qtbot): qtbot.mouseMove(rs, pos) with qtbot.waitSignal(rs.sliderReleased): - qtbot.mouseRelease(rs, Qt.LeftButton) + qtbot.mouseRelease(rs, Qt.MouseButton.LeftButton) # check the values assert rs.value()[0] > 30 @@ -71,7 +71,7 @@ def test_drag_handles(qtbot): # press the right handle pos = rs._handleRect(1).center() with qtbot.waitSignal(rs.sliderPressed): - qtbot.mousePress(rs, Qt.LeftButton, pos=pos) + qtbot.mousePress(rs, Qt.MouseButton.LeftButton, pos=pos) assert rs._pressedControl == SC_HANDLE assert rs._pressedIndex == 1 @@ -81,7 +81,7 @@ def test_drag_handles(qtbot): pos.setX(pos.x() - 2) qtbot.mouseMove(rs, pos) with qtbot.waitSignal(rs.sliderReleased): - qtbot.mouseRelease(rs, Qt.LeftButton) + qtbot.mouseRelease(rs, Qt.MouseButton.LeftButton) # check the values assert rs.value()[1] < 70 @@ -90,7 +90,7 @@ def test_drag_handles(qtbot): @skipmouse def test_drag_handles_beyond_edge(qtbot): - rs = QRangeSlider(Qt.Horizontal) + rs = QRangeSlider(Qt.Orientation.Horizontal) qtbot.addWidget(rs) rs.setRange(0, 99) rs.setValue((20, 80)) @@ -100,7 +100,7 @@ def test_drag_handles_beyond_edge(qtbot): # press the right handle pos = rs._handleRect(1).center() with qtbot.waitSignal(rs.sliderPressed): - qtbot.mousePress(rs, Qt.LeftButton, pos=pos) + qtbot.mousePress(rs, Qt.MouseButton.LeftButton, pos=pos) assert rs._pressedControl == SC_HANDLE assert rs._pressedIndex == 1 @@ -110,14 +110,14 @@ def test_drag_handles_beyond_edge(qtbot): qtbot.mouseMove(rs, pos) with qtbot.waitSignal(rs.sliderReleased): - qtbot.mouseRelease(rs, Qt.LeftButton) + qtbot.mouseRelease(rs, Qt.MouseButton.LeftButton) assert rs.value()[1] == 99 @skipmouse def test_bar_drag_beyond_edge(qtbot): - rs = QRangeSlider(Qt.Horizontal) + rs = QRangeSlider(Qt.Orientation.Horizontal) qtbot.addWidget(rs) rs.setRange(0, 99) rs.setValue((20, 80)) @@ -127,7 +127,7 @@ def test_bar_drag_beyond_edge(qtbot): # press the right handle pos = rs.rect().center() with qtbot.waitSignal(rs.sliderPressed): - qtbot.mousePress(rs, Qt.LeftButton, pos=pos) + qtbot.mousePress(rs, Qt.MouseButton.LeftButton, pos=pos) assert rs._pressedControl == SC_BAR assert rs._pressedIndex == 1 @@ -137,6 +137,6 @@ def test_bar_drag_beyond_edge(qtbot): qtbot.mouseMove(rs, pos) with qtbot.waitSignal(rs.sliderReleased): - qtbot.mouseRelease(rs, Qt.LeftButton) + qtbot.mouseRelease(rs, Qt.MouseButton.LeftButton) assert rs.value()[1] == 99