diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 8e2ee8cb..6a3996ac 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -222,8 +222,8 @@ def __getattr__(name): "directory", ) -if PYQT5 or PYSIDE2 or _parse_version(_qt_version) < _parse_version("6.4"): - # Make `addAction` compatible with Qt6 >= 6.4 +if PYQT5 or PYSIDE2 or _parse_version(_qt_version) < _parse_version("6.3"): + # Make `addAction` compatible with Qt6 >= 6.3 _menu_add_action = partialmethod( add_action, old_add_action=QMenu.addAction, diff --git a/qtpy/_utils.py b/qtpy/_utils.py index 4b6ee5df..503982f9 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -102,85 +102,55 @@ def set_shortcuts(self, shortcuts, old_set_shortcuts): old_set_shortcuts(self, shortcuts) -def add_action(self, *args, old_add_action): - """Re-order arguments of `addAction` to backport compatibility with Qt>=6.3.""" - from .QtCore import QObject, Qt +def add_action(self, *args, old_add_action, **kwargs): + """ + Re-order arguments when calling the `addAction` function with the signature + introduced in Qt 6.3. + """ + import warnings + from collections.abc import Callable + from .QtCore import Qt from .QtGui import QIcon, QKeySequence - action: QAction - icon: QIcon - text: str - shortcut: QKeySequence | QKeySequence.StandardKey | Qt.Key | str | int - receiver: QObject - member: bytes - - if all( - isinstance(arg, t) - for arg, t in zip( - args, - [ - str, - (QKeySequence, QKeySequence.StandardKey, Qt.Key, 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: - action = old_add_action(self, *args) - elif all( - isinstance(arg, t) - for arg, t in zip( - args, - [ - QIcon, - str, - (QKeySequence, QKeySequence.StandardKey, Qt.Key, str, int), - QObject, - bytes, - ], - ) - ): - if len(args) == 3: - icon, text, shortcut = args - action = old_add_action(self, icon, text) - action.setShortcut(shortcut) - elif len(args) == 4: - icon, text, shortcut, receiver = args - action = old_add_action(self, icon, text, receiver) - action.setShortcut(shortcut) - elif len(args) == 5: - icon, text, shortcut, receiver, member = args - action = old_add_action( - self, - icon, - text, - receiver, - member, - shortcut, - ) - else: - action = old_add_action(self, *args) + new_args = list(args) + if new_args and isinstance(new_args[0], QIcon): + icon = new_args.pop(0) else: - action = old_add_action(self, *args) + icon = None + shortcut = kwargs.pop("shortcut", None) + connection_type = kwargs.pop("type", None) + if connection_type: + warnings.warn("type argument is not supported in Qt<6.3") + + shortcut_types = (QKeySequence, QKeySequence.StandardKey, Qt.Key, str, int) + if len(new_args) > 1 and isinstance(new_args[1], shortcut_types): + # Qt6.3 signature (text, shortcut, receiver, member) + shortcut = new_args.pop(1) + elif ( + len(new_args) > 2 + and isinstance(new_args[1], Callable) + and isinstance(new_args[2], shortcut_types) + ): + # Qt5 signature (arg__1, arg__2, arg__3) + shortcut = new_args.pop(2) + elif len(new_args) > 3 and isinstance(new_args[3], shortcut_types): + # Qt5 signature (text, receiver, member, shortcut) + shortcut = new_args.pop(3) + + if icon is not None: + new_args.insert(0, icon) + action = old_add_action(self, *new_args, **kwargs) + + if shortcut is not None: + action.setShortcut(shortcut) return action def static_method_kwargs_wrapper(func, from_kwarg_name, to_kwarg_name): """ - Helper function to manage `from_kwarg_name` to `to_kwarg_name` kwargs name changes in static methods. + Helper function to manage `from_kwarg_name` to `to_kwarg_name` kwargs name changes + in static methods. Makes static methods accept the `from_kwarg_name` kwarg as `to_kwarg_name`. """ diff --git a/tests/test_qtwidgets.py b/tests/test_qtwidgets.py index cc5b3ffa..3b8233a9 100644 --- a/tests/test_qtwidgets.py +++ b/tests/test_qtwidgets.py @@ -112,13 +112,53 @@ def test_QMenu_functions(qtbot): # A window is required for static calls window = QtWidgets.QMainWindow() menu = QtWidgets.QMenu(window) - menu.addAction("QtPy") - menu.addAction("QtPy with a Qt.Key shortcut", QtCore.Qt.Key_F1) - menu.addAction( - QtGui.QIcon(), - "QtPy with an icon and a QKeySequence shortcut", - QtGui.QKeySequence.UnknownKey, + + # Actions + + class Receiver(QtCore.QObject): + triggered = QtCore.Signal + + func = lambda: print('Action') + icon = QtGui.QIcon() + text = "QtPy" + shortcuts = ( + QtGui.QKeySequence.StandardKey.HelpContents, + QtCore.Qt.Key_F1, + 'F1', + 1, ) + receiver = Receiver(parent=window) + member = 'triggered' + connection_type = QtCore.Qt.ConnectionType.DirectConnection + action = QtWidgets.QAction() + + menu.addAction(text) + menu.addAction(text, func) + menu.addAction(icon, text) + menu.addAction(icon, text, func) + + menu.addAction(action) + + for shortcut in shortcuts: + menu.addAction(text, func, shortcut) + menu.addAction(text, func, shortcut=shortcut) + menu.addAction(text, receiver, member, shortcut) + menu.addAction(icon, text, func, shortcut) + menu.addAction(icon, text, func, shortcut=shortcut) + menu.addAction(icon, text, receiver, member, shortcut) + + # Qt 5 + menu.addAction(text, receiver, member, shortcut=shortcut) + menu.addAction(icon, text, receiver, member, shortcut=shortcut) + + # Qt>=6.3 signature + menu.addAction(text, shortcut) + menu.addAction(text, shortcut, receiver, member, type=connection_type) + menu.addAction(icon, text, shortcut) + menu.addAction( + icon, text, shortcut, receiver, member, type=connection_type + ) + window.show() with qtbot.waitExposed(window):