如何在自定义 QMenu 中触发快捷方式?
How to make shortcuts trigger in a custom QMenu?
我有一个上下文菜单 (QMenu) 并向其添加复制操作,如下所示:
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;
});
菜单是这样打开的(宿主class来源于一个QGraphicsView
):
m_mainContextMenu.exec(mapToGlobal(m_clickedPos));
菜单显示动作正常,但Ctrl+C
没有触发。我在主菜单中对操作使用了相同的方法,为什么会有所不同?
我也试过设置一些其他的快捷方式,但是没有用。
以下示例重现了您的错误。我也在Qt Framework内部调试,单步执行QMenu::keyPressEvent
和QAction::event
,但似乎没有正确处理按下的键。
在QAction::event
中事件类型QEvent::Shortcut
从未发生过。作为一种解决方法,我建议您从 QAction
派生并实现您自己的 event
函数。
#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();
}
这是解决此问题的一种方法:
- 除了将操作添加到上下文菜单之外,还将操作添加到父窗口小部件(操作应该是本地窗口小部件,比方说列表视图):
m_listview->addAction(m_copyNodeAction);
- 将操作的 shortcut context 设置为
Qt::WidgetWithChildrenShortcut
:
m_copyNodeAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- 确保您创建的上下文菜单使用您的小部件作为其父级:
auto* m_mainContextMenu = new QMenu{tr("Main Context Menu"), m_listview};
有几点需要考虑:
默认情况下,这不会在触发操作时关闭上下文菜单,但是您自己实现起来相当简单
这允许您在不显示上下文菜单的情况下触发操作(也很容易绕行,但您为什么要这样做?)
通过一些初步测试,这似乎也是 QtCreator 处理快捷方式的方式,并且似乎是处理此问题的正确 Qt 式方式,尽管那只是我的 2ct。
我有一个上下文菜单 (QMenu) 并向其添加复制操作,如下所示:
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;
});
菜单是这样打开的(宿主class来源于一个QGraphicsView
):
m_mainContextMenu.exec(mapToGlobal(m_clickedPos));
菜单显示动作正常,但Ctrl+C
没有触发。我在主菜单中对操作使用了相同的方法,为什么会有所不同?
我也试过设置一些其他的快捷方式,但是没有用。
以下示例重现了您的错误。我也在Qt Framework内部调试,单步执行QMenu::keyPressEvent
和QAction::event
,但似乎没有正确处理按下的键。
在QAction::event
中事件类型QEvent::Shortcut
从未发生过。作为一种解决方法,我建议您从 QAction
派生并实现您自己的 event
函数。
#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();
}
这是解决此问题的一种方法:
- 除了将操作添加到上下文菜单之外,还将操作添加到父窗口小部件(操作应该是本地窗口小部件,比方说列表视图):
m_listview->addAction(m_copyNodeAction);
- 将操作的 shortcut context 设置为
Qt::WidgetWithChildrenShortcut
:
m_copyNodeAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- 确保您创建的上下文菜单使用您的小部件作为其父级:
auto* m_mainContextMenu = new QMenu{tr("Main Context Menu"), m_listview};
有几点需要考虑:
默认情况下,这不会在触发操作时关闭上下文菜单,但是您自己实现起来相当简单
这允许您在不显示上下文菜单的情况下触发操作(也很容易绕行,但您为什么要这样做?)
通过一些初步测试,这似乎也是 QtCreator 处理快捷方式的方式,并且似乎是处理此问题的正确 Qt 式方式,尽管那只是我的 2ct。