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
This commit is contained in:
Talley Lambert
2021-11-02 11:13:52 -04:00
committed by GitHub
parent 055a4fc1a7
commit e1d2edb204
68 changed files with 533 additions and 432 deletions

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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))

View File

@@ -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()

View File

@@ -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)

7
mypy.ini Normal file
View File

@@ -0,0 +1,7 @@
[mypy]
strict = True
[mypy-superqt.qtcompat.*]
ignore_missing_imports = True
warn_unused_ignores = False
allow_redefinition = True

View File

@@ -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

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DAnimation import *
from PyQt6.Qt3DAnimation import *
from PySide2.Qt3DAnimation import *
from PySide6.Qt3DAnimation import *

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DCore import *
from PyQt6.Qt3DCore import *
from PySide2.Qt3DCore import *
from PySide6.Qt3DCore import *

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DExtras import *
from PyQt6.Qt3DExtras import *
from PySide2.Qt3DExtras import *
from PySide6.Qt3DExtras import *

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DInput import *
from PyQt6.Qt3DInput import *
from PySide2.Qt3DInput import *
from PySide6.Qt3DInput import *

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DLogic import *
from PyQt6.Qt3DLogic import *
from PySide2.Qt3DLogic import *
from PySide6.Qt3DLogic import *

View File

@@ -0,0 +1,4 @@
from PyQt5.Qt3DRender import *
from PyQt6.Qt3DRender import *
from PySide2.Qt3DRender import *
from PySide6.Qt3DRender import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtCharts import *
from PyQt6.QtCharts import *
from PySide2.QtCharts import *
from PySide6.QtCharts import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtConcurrent import *
from PyQt6.QtConcurrent import *
from PySide2.QtConcurrent import *
from PySide6.QtConcurrent import *

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
from PyQt5.QtDataVisualization import *
from PyQt6.QtDataVisualization import *
from PySide2.QtDataVisualization import *
from PySide6.QtDataVisualization import *

View File

@@ -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

View File

@@ -0,0 +1,4 @@
from PyQt5.QtGui import *
from PyQt6.QtGui import *
from PySide2.QtGui import *
from PySide6.QtGui import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtHelp import *
from PyQt6.QtHelp import *
from PySide2.QtHelp import *
from PySide6.QtHelp import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtLocation import *
from PyQt6.QtLocation import *
from PySide2.QtLocation import *
from PySide6.QtLocation import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtMacExtras import *
from PyQt6.QtMacExtras import *
from PySide2.QtMacExtras import *
from PySide6.QtMacExtras import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtMultimedia import *
from PyQt6.QtMultimedia import *
from PySide2.QtMultimedia import *
from PySide6.QtMultimedia import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtMultimediaWidgets import *
from PyQt6.QtMultimediaWidgets import *
from PySide2.QtMultimediaWidgets import *
from PySide6.QtMultimediaWidgets import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtNetwork import *
from PyQt6.QtNetwork import *
from PySide2.QtNetwork import *
from PySide6.QtNetwork import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtOpenGL import *
from PyQt6.QtOpenGL import *
from PySide2.QtOpenGL import *
from PySide6.QtOpenGL import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtOpenGLFunctions import *
from PyQt6.QtOpenGLFunctions import *
from PySide2.QtOpenGLFunctions import *
from PySide6.QtOpenGLFunctions import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtPositioning import *
from PyQt6.QtPositioning import *
from PySide2.QtPositioning import *
from PySide6.QtPositioning import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtPrintSupport import *
from PyQt6.QtPrintSupport import *
from PySide2.QtPrintSupport import *
from PySide6.QtPrintSupport import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtQml import *
from PyQt6.QtQml import *
from PySide2.QtQml import *
from PySide6.QtQml import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtQuick import *
from PyQt6.QtQuick import *
from PySide2.QtQuick import *
from PySide6.QtQuick import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtQuickControls2 import *
from PyQt6.QtQuickControls2 import *
from PySide2.QtQuickControls2 import *
from PySide6.QtQuickControls2 import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtQuickWidgets import *
from PyQt6.QtQuickWidgets import *
from PySide2.QtQuickWidgets import *
from PySide6.QtQuickWidgets import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtRemoteObjects import *
from PyQt6.QtRemoteObjects import *
from PySide2.QtRemoteObjects import *
from PySide6.QtRemoteObjects import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtScript import *
from PyQt6.QtScript import *
from PySide2.QtScript import *
from PySide6.QtScript import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtScriptTools import *
from PyQt6.QtScriptTools import *
from PySide2.QtScriptTools import *
from PySide6.QtScriptTools import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtScxml import *
from PyQt6.QtScxml import *
from PySide2.QtScxml import *
from PySide6.QtScxml import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtSensors import *
from PyQt6.QtSensors import *
from PySide2.QtSensors import *
from PySide6.QtSensors import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtSerialPort import *
from PyQt6.QtSerialPort import *
from PySide2.QtSerialPort import *
from PySide6.QtSerialPort import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtSql import *
from PyQt6.QtSql import *
from PySide2.QtSql import *
from PySide6.QtSql import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtSvg import *
from PyQt6.QtSvg import *
from PySide2.QtSvg import *
from PySide6.QtSvg import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtTest import *
from PyQt6.QtTest import *
from PySide2.QtTest import *
from PySide6.QtTest import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtTextToSpeech import *
from PyQt6.QtTextToSpeech import *
from PySide2.QtTextToSpeech import *
from PySide6.QtTextToSpeech import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtUiTools import *
from PyQt6.QtUiTools import *
from PySide2.QtUiTools import *
from PySide6.QtUiTools import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtWebChannel import *
from PyQt6.QtWebChannel import *
from PySide2.QtWebChannel import *
from PySide6.QtWebChannel import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtWebEngine import *
from PyQt6.QtWebEngine import *
from PySide2.QtWebEngine import *
from PySide6.QtWebEngine import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtWebEngineCore import *
from PyQt6.QtWebEngineCore import *
from PySide2.QtWebEngineCore import *
from PySide6.QtWebEngineCore import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtWebEngineWidgets import *
from PyQt6.QtWebEngineWidgets import *
from PySide2.QtWebEngineWidgets import *
from PySide6.QtWebEngineWidgets import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtWebSockets import *
from PyQt6.QtWebSockets import *
from PySide2.QtWebSockets import *
from PySide6.QtWebSockets import *

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
from PyQt5.QtXml import *
from PyQt6.QtXml import *
from PySide2.QtXml import *
from PySide6.QtXml import *

View File

@@ -0,0 +1,4 @@
from PyQt5.QtXmlPatterns import *
from PyQt6.QtXmlPatterns import *
from PySide2.QtXmlPatterns import *
from PySide6.QtXmlPatterns import *

View File

@@ -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())

View File

@@ -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 (

View File

@@ -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

View File

@@ -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):

View File

@@ -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(

View File

@@ -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 #######################

View File

@@ -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

View File

@@ -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

View File

@@ -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,
)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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