From df60d73d62b7d5c98eab8b84bb5617ec5f8e5249 Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:08:29 +0300 Subject: [PATCH 1/6] Make `QtWidgets.QToolBar.addAction` compatible with Qt6 arguments' order --- qtpy/QtWidgets.py | 10 ++++++- qtpy/_utils.py | 54 ++++++++++++++++++++++++++++++++++++ qtpy/tests/test_qtwidgets.py | 12 ++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 9a83ee6f..d5d58bbe 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -10,7 +10,7 @@ from functools import wraps from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from ._utils import possibly_static_exec, getattr_missing_optional_dep +from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -114,3 +114,11 @@ 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 +if PYQT5 or PYSIDE2: + from functools import partialmethod + + 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 c009f17b..90211863 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -44,3 +44,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): + 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..cb7e8b5e 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.StandardKey.UnknownKey) + menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.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.StandardKey.UnknownKey) + toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.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") From 878e22734d5a4000e3e4df938744b24a063f0801 Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:32:38 +0300 Subject: [PATCH 2/6] Fix a test failure on PyQt5 5.9 --- qtpy/tests/test_qtwidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index cb7e8b5e..74461e11 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -123,8 +123,8 @@ def test_QMenu_functions(qtbot): 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.StandardKey.UnknownKey) - toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + 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'), From 0e79728fd47e0dc9463a25d0ddcde4bf42b72cbc Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:46:31 +0300 Subject: [PATCH 3/6] Make `Q*.addAction` compatible for Qt < 6.3 --- qtpy/QtWidgets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d5d58bbe..8d6d8edf 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -7,10 +7,13 @@ # ----------------------------------------------------------------------------- """Provides widget classes and functions.""" -from functools import wraps +from functools import partialmethod, wraps + +from packaging.version import parse from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep +from .QtCore import __version__ as _qt_version _missing_optional_names = {} @@ -115,10 +118,7 @@ def _directory_to_dir_(*args, **kwargs): QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames) QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName) - -# Make `addAction` compatible with Qt6 -if PYQT5 or PYSIDE2: - from functools import partialmethod - +# 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) From 9b90a97d14f19e136635aae228d884d95f41aa8e Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:55:47 +0300 Subject: [PATCH 4/6] Fix a test failure on PyQt5 5.9 --- qtpy/tests/test_qtwidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 74461e11..2858a3f3 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -100,8 +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.StandardKey.UnknownKey) - menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + 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): From bb524b6913538196d1e959168783f488804c2fcc Mon Sep 17 00:00:00 2001 From: StSav012 Date: Thu, 6 Jul 2023 10:48:39 +0300 Subject: [PATCH 5/6] Add a description for `add_action` --- qtpy/_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/_utils.py b/qtpy/_utils.py index de860037..4dce8bb0 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -61,6 +61,7 @@ def possibly_static_exec_(cls, *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 From 6d7b3c24231f7bbdb20c15e292405420e3bb859e Mon Sep 17 00:00:00 2001 From: StSav012 Date: Mon, 14 Aug 2023 19:58:14 +0300 Subject: [PATCH 6/6] Use `QT_VERSION` instead of `QtCore.__version__` --- qtpy/QtWidgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 8d6d8edf..6dbeec2e 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -11,9 +11,8 @@ from packaging.version import parse -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep -from .QtCore import __version__ as _qt_version _missing_optional_names = {}