gpt4 book ai didi

c++ - 在 QMenuBar/QMenu 上捕获 keyPressEvent?

转载 作者:太空狗 更新时间:2023-10-29 22:55:22 26 4
gpt4 key购买 nike

如果用户按住特定键,我想修改 QAction。您可以在 Mac 上看到类似的行为,例如,如果您拉下 Apple 菜单并按下/释放选项键 - “关于 native ”更改为“系统信息...”等。这是我想在我的应用程序中模拟的行为。

为此,我尝试覆盖 QMenuBar 和包含 QAction 的 QMenu 上的 keyPressEvent。但是,我的调试表明在显示相关 QMenu 的情况下按下任何键时,这些 keyPressEvent 函数都没有被调用。

我有点想知道这是否可能是由于在较低级别处理事件以在菜单中提供“提前输入”类型功能?我确实注意到在键入各种键时选择了不同的菜单条目,我认为这是正常行为。

如何响应 QMenuBar 或 QMenu 中的 keyPress 类型事件?

编辑:这是我试过的:

class MainMenu: public QMenuBar
{
Q_OBJECT
protected:
void keyPressEvent(QKeyEvent *event) override
{qDebug("Got Key Press Event in Menu Bar: %i",event->key());}
}

class FileMenu: public QMenu
{
Q_OBJECT
protected:
void keyPressEvent(QKeyEvent *event) override
{qDebug("Got Key Press Event in Menu: %i",event->key());}
}

然后我像往常一样实例化菜单栏,使用许多 QAction 填充 FileMenu 对象,并将其添加到菜单栏。这一切都奏效了,但 qDebug 行永远不会打印,并且手动调试应用程序显示该事件从未被调用。

编辑 2:有关更多信息,我在 MacOS X 10.14.0 上使用 Qt 5.9.6。

编辑 3:作为进一步的测试,我尝试安装一个 eventFilter,使用来自 http://doc.qt.io/qt-5/qobject.html#installEventFilter 的代码根据 http://doc.qt.io/qt-5/eventsandfilters.html#event-filters 上的文档,将 KeyPressEater 对象安装为两个对象上的事件过滤器:一个是我的菜单栏对象,两个是 QApplication 对象:

It is also possible to filter all events for the entire application, by installing an event filter on the QApplication or QCoreApplication object.



我用这种方法看到的是 KeyPressEater eventFilter DID 被调用用于 keyPresses - 但仅当菜单未激活时。一旦我激活菜单(任何菜单,包括苹果菜单), eventFilter 函数就会停止调用。

我在这里得到的印象是系统接管了菜单处理,从而防止任何事件进入应用程序。当然,这只是基于观察到的行为。

编辑 4:更多的挖掘发现了这个:(doc.qt.io/qt-5/osx-issues.html#limitations):

"QMenu objects used in the native menu bar are not able to handle Qt events via the normal event handlers. Install a delegate on the menu itself to be notified of these changes



所以我想“答案”是我需要“在菜单本身上安装一个委托(delegate)”,“通知我这些更改”。有人可以帮忙吗?

最佳答案

我做了一个 MCVE对于 OP 的问题。

我相信我无法重现 OP 的问题。可能是,OP 对我有其他期望。当然,菜单栏只要没有激活就不会接收按键事件。 (我暂时忽略了捷径的主题。)

在我的测试中,我总是先点击菜单栏激活菜单,然后接收按键事件。我用菜单栏和子菜单对此进行了测试。

我观察到,一旦打开子菜单,甚至两个(菜单栏和菜单)都会收​​到(相同的)键事件。 (考虑到 ← 和 → 作用于菜单栏,而 ↑ 和 ↓ 作用于事件菜单,这似乎是合理的。)

这是我的示例代码 testQMenuKeyEvent.cc :

#include <QtWidgets>

class FileMenu: public QMenu {
private:
QAction qCmdNew, qCmdOpen, qCmdQuit;
bool alt;

public:
FileMenu(): QMenu(),
qCmdNew(QString::fromUtf8("New")),
qCmdOpen(QString::fromUtf8("Open")),
qCmdQuit(QString::fromUtf8("Quit")),
alt(false)
{
addAction(&qCmdNew);
addAction(&qCmdOpen);
addAction(&qCmdQuit);
// install signal handlers
connect(&qCmdNew, &QAction::triggered,
[&]() {
qDebug() << (alt ? "Reset" : "New") << "triggered";
});
connect(&qCmdOpen, &QAction::triggered,
[&]() {
qDebug() << (alt ? "Save" : "Open") << "triggered";
});

}
protected:
virtual void showEvent(QShowEvent *pQEvent) override
{
qDebug() << "FileMenu::showEvent";
update();
QMenu::showEvent(pQEvent);
}

virtual void keyPressEvent(QKeyEvent *pQEvent) override
{
qDebug() << "FileMenu::keyPressEvent";
update(pQEvent->modifiers());
QMenu::keyPressEvent(pQEvent);
}
virtual void keyReleaseEvent(QKeyEvent *pQEvent) override
{
qDebug() << "FileMenu::keyReleaseEvent";
update(pQEvent->modifiers());
QMenu::keyReleaseEvent(pQEvent);
}

private:
void update()
{
update(
(QApplication::keyboardModifiers()
& Qt::ControlModifier)
!= 0);
}
void update(bool alt)
{
qDebug() << "alt:" << alt;
if (!alt != !this->alt) {
qCmdNew.setText(QString::fromUtf8(alt ? "Reset" : "New"));
qCmdOpen.setText(QString::fromUtf8(alt ? "Save" : "Open"));
}
this->alt = alt;
}
};

int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QMainWindow qWin;
QMenuBar qMenuMain;
QAction qCmdFile(QString::fromUtf8("File"));
FileMenu qMenuFile;
qCmdFile.setMenu(&qMenuFile);
qMenuMain.addAction(&qCmdFile);
QAction qCmdEdit(QString::fromUtf8("Edit"));
qMenuMain.addAction(&qCmdEdit);
QAction qCmdHelp(QString::fromUtf8("Help"));
qMenuMain.addAction(&qCmdHelp);
qWin.setMenuBar(&qMenuMain);
qWin.show();
return app.exec();
}

Qt项目 testQMenuKeyEvent.pro我曾经建立:

SOURCES = testQMenuKeyEvent.cc

QT = widgets

cygwin64 中编译和测试在 Windows 10 上:

$ qmake-qt5 testQMenuKeyEvent.pro

$ make && ./testQMenuKeyEvent
Qt Version: 5.9.4
FileMenu::showEvent
alt: false
New triggered
FileMenu::showEvent
alt: false
FileMenu::keyPressEvent
alt: true
Reset triggered

Snapshot of testQMenuKeyEvent in cygwin/X11 Snapshot of testQMenuKeyEvent in cygwin/X11 (Ctrl key pressed)

之后,我在VS2013中再次构建了Qt绑定(bind)到win32 API:

Snapshot of testQMenuKeyEvent in Windows 10 Snapshot of testQMenuKeyEvent in Windows 10 (Ctrl key pressed)

尽管外观略有不同,但它的行为是相同的。

备注:
  • 当我最初测试代码时,我注意到按键导航被破坏了。因此,我觉得值得一提的是override -ing 应该调用基类的重写方法以确保原始行为。
  • 我用来切换菜单项的 Ctrl 键可能在菜单被激活之前被按下。考虑到这一点,我重载了 showEvent()以及。
  • 对于触发操作,Ctrl 会再次检查到最近的可能时刻。这是使用 lambdas 作为 QAction 的信号处理程序完成的。 s。将它移动到处理程序函数本身将确保这对这些操作的其他发生也有效。 (我的意思是,这些操作可以在工具栏中“重用”。)
  • QApplication::keyboardModifiers()内部调用 keyPressEvent()keyReleaseEvent()它返回了错误的值,但使用了 QKeyEvent::modifiers()相反工作正常。这让我想到,全局状态的更新是在处理这些事件之后完成的。
  • 如果菜单栏本身应该实现显示的行为,它会变得有点复杂。在这种情况下,keyPressEvent()没有多大帮助。 (只要菜单栏不活动(聚焦),它就不会被调用)。在这种情况下,一个 event filter可用于捕捉任何按键按下并更新菜单栏操作以防万一。


  • OP 提到上述解决方案在他的 MacBook 上不起作用。

    我研究了 Qt。博士。的 QMenu .我发现的只是:

    QMenu on macOS with Qt Build Against Cocoa

    QMenu can be inserted only once in a menu/menubar. Subsequent insertions will have no effect or will result in a disabled menu item.



    这似乎没有直接关系。不过,这让我觉得 Mac 上的情况可能会有所不同......

    所以,我遵循了 event filter 的想法并分别更改了示例代码:
    #include <functional>
    #include <vector>

    #include <QtWidgets>

    class CtrlNotifier: public QObject {
    private:
    bool ctrl;
    public:
    // to be notified
    std::vector<std::function<void(bool)> > sigNotify;
    public:
    CtrlNotifier():
    ctrl(
    (QApplication::keyboardModifiers() & Qt::ControlModifier)
    != 0)
    { }
    bool isCtrl() const { return ctrl; }
    protected:
    virtual bool eventFilter(QObject *pQObj, QEvent *pQEvent) override
    {
    if (pQEvent->type() == QEvent::KeyPress
    || pQEvent->type() == QEvent::KeyRelease) {
    const bool ctrl
    = (dynamic_cast<QKeyEvent*>(pQEvent)->modifiers()
    & Qt::ControlModifier)
    != 0;
    if (!this->ctrl != !ctrl) {
    qDebug() << "CtrlNotifier::eventFilter: Ctrl:" << ctrl;
    for (std::function<void(bool)> &func : sigNotify) {
    if (func) func(ctrl);
    }
    this->ctrl = ctrl;
    }
    }
    // standard event processing
    return QObject::eventFilter(pQObj, pQEvent);
    }
    };

    int main(int argc, char **argv)
    {
    qDebug() << "Qt Version:" << QT_VERSION_STR;
    QApplication app(argc, argv);
    QMainWindow qWin;
    QMenuBar qMenuMain;
    QAction qCmdFile(QString::fromUtf8("File"));
    QMenu qMenuFile;
    QAction qCmdNew(QString::fromUtf8("New"));
    qMenuFile.addAction(&qCmdNew);
    QAction qCmdOpen(QString::fromUtf8("Open"));
    qMenuFile.addAction(&qCmdOpen);
    QAction qCmdQuit(QString::fromUtf8("Quit"));
    qMenuFile.addAction(&qCmdQuit);
    qCmdFile.setMenu(&qMenuFile);
    qMenuMain.addAction(&qCmdFile);
    QAction qCmdEdit(QString::fromUtf8("Edit"));
    qMenuMain.addAction(&qCmdEdit);
    QAction qCmdHelp(QString::fromUtf8("Help"));
    qMenuMain.addAction(&qCmdHelp);
    qWin.setMenuBar(&qMenuMain);
    qWin.show();
    // install event filter
    CtrlNotifier ctrlNotifier;
    app.installEventFilter(&ctrlNotifier);
    // install signal handlers
    ctrlNotifier.sigNotify.push_back(
    [&](bool ctrl) {
    qCmdNew.setText(QString::fromUtf8(ctrl ? "Reset" : "New"));
    qCmdOpen.setText(QString::fromUtf8(ctrl ? "Save" : "Open"));
    });
    // install signal handlers
    QObject::connect(&qCmdNew, &QAction::triggered,
    [&]() {
    qDebug() << (ctrlNotifier.isCtrl() ? "Reset" : "New") << "triggered";
    });
    QObject::connect(&qCmdOpen, &QAction::triggered,
    [&]() {
    qDebug() << (ctrlNotifier.isCtrl() ? "Save" : "Open") << "triggered";
    });
    // runtime-loop
    return app.exec();
    }

    我在 Windows 10(Qt 绑定(bind)到 win32)和 cygwin(Qt 绑定(bind)到 X11)上再次测试。
    它在两种情况下都有效。

    注:
  • QApplication::keyboardModifiers()内部调用 CtrlNotifier::eventFilter()它再次返回错误的值,但使用了 QKeyEvent::modifiers()相反工作正常。
  • 我不小心先尝试过滤主窗口的事件。只要我激活菜单,这就会起作用。幸运的是,我意识到了文档中的注释。 (Event Filters章最后一段):

    It is also possible to filter all events for the entire application, by installing an event filter on the QApplication or QCoreApplication object. Such global event filters are called before the object-specific filters. This is very powerful, but it also slows down event delivery of every single event in the entire application; the other techniques discussed should generally be used instead.



    因此,将其安装在 app 上而是带来了预期的行为。
  • 我为 CtrlNotifier 中的非 Qt-ish 信号道歉.我正在为 VS2013 (CMake) 和 cygwin (qmake-qt5) 使用不同的构建脚本。这使得正确的 MOC 处理像往常一样有点棘手。因此,我尽量避免它的必要性。 (我并不是说在这两种情况下都不能集成 MOC。– 我曾经成功地管理过它。)
  • 关于c++ - 在 QMenuBar/QMenu 上捕获 keyPressEvent?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52747343/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com