简体   繁体   中英

How to make shortcuts trigger in a custom QMenu?

I have a context menu (QMenu) and add a copy action to it like this:

m_copyNodeAction = new QAction(tr("Copy node"), &m_mainContextMenu);
m_copyNodeAction->setShortcut(QKeySequence("Ctrl+C"));
m_copyNodeAction->setShortcutVisibleInContextMenu(true);

m_mainContextMenu.addAction(m_copyNodeAction);

QObject::connect(m_copyNodeAction, &QAction::triggered, [this] () {
    std::cout << "Copy node triggered!" << std::endl;
});

The menu is opened like this (the hosting class is derived from a QGraphicsView ):

m_mainContextMenu.exec(mapToGlobal(m_clickedPos));

The menu shows the action OK, but it doesn't trigger by Ctrl+C . I have used the same approach for actions in my main menu so why is this different?

I have also tried to set some other shortcuts, but nothing works.

The following example reproduces your error. I also debugged inside the Qt Framework, and stepping through QMenu::keyPressEvent and QAction::event , but there seems to be no correct handling of the pressed key.

In QAction::event the event type QEvent::Shortcut never occurs. As a workaround I suggest, that you derive from QAction and implement your own event function.

#include <QApplication>
#include <QFrame>
#include <QMenu>
#include <QAction>
#include <QDebug>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    QApplication::setAttribute(Qt::ApplicationAttribute::AA_DontShowShortcutsInContextMenus,false);
    auto widget = new QFrame;

    widget->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
    int id=widget->grabShortcut(QKeySequence::Delete, Qt::ShortcutContext::WidgetShortcut);

    QObject::connect(widget, &QFrame::customContextMenuRequested, [widget,id](const QPoint& pos) {
        QMenu menu(widget);
        menu.setShortcutEnabled(id, true);
        auto action = new QAction("&Copy node", &menu);
        action->setShortcut(QKeySequence(QKeySequence::Delete));
        action->setShortcutVisibleInContextMenu(true);
        action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut);

        QObject::connect(action, &QAction::triggered, []() {
            qDebug() << "Copy node triggered!";
        });

        menu.addAction(action);
        menu.exec(widget->mapToGlobal(pos));
        });

    widget->show();
    return a.exec();
}

Here's one way to go about this:

  1. In addition to adding the action to the contextmenu, add the action to the parent widget (the one that the action should be local to, let's say a listview):
m_listview->addAction(m_copyNodeAction);
  1. Set the action's shortcut context to Qt::WidgetWithChildrenShortcut :
m_copyNodeAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
  1. Make sure the context menu you create uses your widget as its parent:
auto* m_mainContextMenu = new QMenu{tr("Main Context Menu"), m_listview};

There's a few things to consider:

  1. By default this doesn't close the context menu when the action is triggered, but that's rather trivial to implement yourself

  2. This allows you to trigger the action without the context menu being shown (also rather trivial to circumnavigate, though why would you want to?)

By some prelimenary testing, this appears to be how QtCreator handles shortcuts as well, and seems like the proper Qt-esque way of approaching this, though that's just my 2ct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM