diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 9a83ee6f..6dbeec2e 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -7,10 +7,12 @@ # ----------------------------------------------------------------------------- """Provides widget classes and functions.""" -from functools import wraps +from functools import partialmethod, wraps -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from ._utils import possibly_static_exec, getattr_missing_optional_dep +from packaging.version import parse + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version +from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -114,3 +116,8 @@ def _directory_to_dir_(*args, **kwargs): QFileDialog.getOpenFileName = _dir_to_directory(QFileDialog.getOpenFileName) QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames) QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName) + +# Make `addAction` compatible with Qt6 >= 6.3 +if PYQT5 or PYSIDE2 or parse(_qt_version) < parse('6.3'): + QMenu.addAction = partialmethod(add_action, old_add_action=QMenu.addAction) + QToolBar.addAction = partialmethod(add_action, old_add_action=QToolBar.addAction) diff --git a/qtpy/_utils.py b/qtpy/_utils.py index ce1ba461..4dce8bb0 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -58,3 +58,57 @@ def possibly_static_exec_(cls, *args, **kwargs): return args[0].exec_(*args[1:], **kwargs) else: return cls.exec_(*args, **kwargs) + + +def add_action(self, *args, old_add_action): + """Re-order arguments of `addAction` to backport compatibility with Qt>=6.3.""" + from qtpy.QtCore import QObject + from qtpy.QtGui import QIcon, QKeySequence + from qtpy.QtWidgets import QAction + + action: QAction + icon: QIcon + text: str + shortcut: QKeySequence | QKeySequence.StandardKey | str | int + receiver: QObject + member: bytes + if all(isinstance(arg, t) + for arg, t in zip(args, [str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes])): + if len(args) == 2: + text, shortcut = args + action = old_add_action(self, text) + action.setShortcut(shortcut) + elif len(args) == 3: + text, shortcut, receiver = args + action = old_add_action(self, text, receiver) + action.setShortcut(shortcut) + elif len(args) == 4: + text, shortcut, receiver, member = args + action = old_add_action(self, text, receiver, member, shortcut) + else: + return old_add_action(self, *args) + return action + elif all(isinstance(arg, t) + for arg, t in zip(args, [QIcon, + str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes])): + if len(args) == 3: + icon, text, shortcut = args + action = old_add_action(self, icon, text) + action.setShortcut(QKeySequence(shortcut)) + elif len(args) == 4: + icon, text, shortcut, receiver = args + action = old_add_action(self, icon, text, receiver) + action.setShortcut(QKeySequence(shortcut)) + elif len(args) == 5: + icon, text, shortcut, receiver, member = args + action = old_add_action(self, icon, text, receiver, member, QKeySequence(shortcut)) + else: + return old_add_action(self, *args) + return action + return old_add_action(self, *args) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index fa982c4e..2858a3f3 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -100,6 +100,8 @@ def test_QMenu_functions(qtbot): window = QtWidgets.QMainWindow() menu = QtWidgets.QMenu(window) menu.addAction('QtPy') + menu.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) + menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) window.show() with qtbot.waitExposed(window): @@ -115,6 +117,16 @@ def test_QMenu_functions(qtbot): QtWidgets.QMenu.exec_(menu.actions(), QtCore.QPoint(1, 1)) +@pytest.mark.skipif( + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") +def test_QToolBar_functions(qtbot): + """Test `QtWidgets.QToolBar.addAction` compatibility with Qt6 arguments' order.""" + toolbar = QtWidgets.QToolBar() + toolbar.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) + toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) + + @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access")