Qt覆盖小部件快捷键(窗口快捷键)

7

我有一个使用Qt设计师表单操作定义了几个窗口快捷方式的应用程序。这些快捷键在焦点不在处理相同组合的小部件时工作正常(覆盖我的窗口快捷方式)。

我希望有相反的行为:窗口快捷方式覆盖焦点小部件快捷方式。

我尝试使用eventFilter,并且可以捕获所需的事件,但我无法以使全局快捷方式被调用的方式重新发送它们。我可以使用大开关并自己调用操作,但当然,我想避免这种情况。

我在eventFilter中使用postEventsendEvent,将MainWindow作为接收器,但事件被忽略:

bool MainWindow::eventFilter(QObject*, QEvent* event) {
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        if (keyEvent->key() == Qt::Key_Z
            && keyEvent->modifiers() == Qt::ControlModifier) {
            //Calling the triggers directly works
            ui->actionUndo->trigger();
            return true;
        } else if (keyEvent->modifiers().testFlag(
                       Qt::KeypadModifier)) {
            QKeyEvent* ev2
                = new QKeyEvent(keyEvent->type(), keyEvent->key(), 0);
            qDebug() << keyEvent << ev2;
            //This sendEvent doesn't work
            QApplication::sendEvent(ui->widget, ev2);
            event->accept();
            return true;
        } else {
            return false;
        }
    }
    return false;
}

你只需要在Qt::ApplicationShortcut上下文中使用QShortcut。或者我不理解你的逻辑和你想做什么。 - Dmitry Sazonov
你尝试过发布到qApp而不是某个小部件吗? - king_nak
@DmitrySazonov 我尝试了Qt :: ApplicationShortcut,结果是一样的。 - dv1729
@king_nak 我也刚试了一下,结果还是一样的... :( - dv1729
sendEvent 中的 0 是否不正确?它被声明为 _Qt::KeyboardModifiers_,也许这就是为什么您的重新发送事件没有被正确处理的原因?也就是说,您检查接收到的事件是否有一个,然后重新发送新事件而不包含它。 - Mikhail Churbanov
@MikhailChurbanov 我尝试使用“keyEvent->modifiers()”而不是0,但没有任何变化。 - dv1729
1个回答

7
作为解决方案之一,您可以安装 QEvent::ShortcutOverride 事件过滤器:QEvent::ShortcutOverride
对于 QEvent::ShortcutOverride,接收器需要显式地接受事件以触发覆盖操作。在按键事件上调用 ignore() 将其传播到父窗口小部件。该事件沿着父窗口小部件链向上传播,直到某个窗口小部件接受它或事件过滤器消耗它。
当某个窗口小部件尝试覆盖快捷键事件时,将调用该事件,例如一个简单的示例:
我有一个带有一个 lineEdit 和窗口菜单的新 Qt 应用程序,其中 Ctrl+V 快捷键(覆盖了 lineEdit 的粘贴快捷键)。
以下是它的工作方式:
1.创建过滤方法,如果覆盖快捷操作,则会忽略(返回 true)快捷键覆盖(在示例应用程序中使用了 MainWindow::eventFilter,但您可以使用任何所需或想要的过滤对象)。最好遵循 Qt 文档并使用上面提到的 accept()/ignore(),但是在我的应用程序中,即使没有它们,只返回 true/false 也能正常工作。
2.将 p.1 中的事件过滤器安装到应该忽略覆盖操作的窗口小部件上。
3.我在设计师中添加了具有 Ctrl+V 快捷键的菜单操作。在下面的代码中,您将看到“来自窗口快捷方式的问候!”-当尝试粘贴(Ctrl+V)而不是实际的 lineEdit 粘贴操作时,菜单操作结果。
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->lineEdit->installEventFilter(this);
}

bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::ShortcutOverride) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        // Ignore only the Ctrl + V shortcut override, you can customize check for your needs
        if (keyEvent->modifiers().testFlag(Qt::ControlModifier) && keyEvent->key() == 'V') {
            qDebug() << "Ignoring" << keyEvent->modifiers() << "+" << (char)keyEvent->key() << "for" << watched;
            event->ignore();
            return true;
        }
    }

    return QMainWindow::eventFilter(watched, event);
}

void MainWindow::on_action1_triggered()
{
    qDebug() << "Hello from window shortcut!";
}

示例调试输出:

忽略LineEdit(0x575b10,名称="lineEdit")的QFlags(ControlModifier)+ V

来自窗口快捷方式的问候!

注意:不幸的是,您需要为所有希望不手动覆盖快捷方式的小部件安装此类过滤器。

更新:简单地说,您正在忽略底层小部件快捷键事件并将其传播到父小部件。

以下是Ctrl-Z(触发编辑中的撤消)和Ctrl-V(在编辑中被忽略而不是粘贴,并触发菜单操作)的比较:

区块I-开头的事件对Ctrl-Z和忽略的Ctrl-V都是相同的:

  1. QLineEdit接收到QKeyEvent(ShortcutOverride, Key_Control,ControlModifier)
  2. QLineEdit接收到QKeyEvent(KeyPress,Key_Control,ControlModifier)
  3. MainWindow接收到QKeyEvent(KeyPress,Key_Control,ControlModifier)
  4. QLineEdit接收到QKeyEvent(ShortcutOverride,Key_Z,ControlModifier)

区块II-差异发生的地方...

对于Ctrl-Z- lineEdit接收Ctrl + Z KeyPress事件,从而触发撤消操作:

  1. QLineEdit接收到QKeyEvent(KeyPress,Key_Z,ControlModifier)

    这里MainWindow不会收到任何事件,无论它是否具有Ctrl + Z操作快捷方式,它都被QLineEdit吞噬了。

对于Ctrl-V- MainWindow从QLineEdit传播Ctrl + V ShortcutOverride事件:

  1. 忽略QLineEdit代码中的"Ctrl+V"
  2. MainWindow接收到QKeyEvent(ShortcutOverride,Key_V,ControlModifier)
  3. 菜单操作触发的“来自窗口快捷方式的问候!”代码执行了槽函数。

    在过滤器告诉QLineEdit忽略ShortcutOverride后,QLineEdit不会再接收到任何事件,而是执行MainWindow的快捷方式。

区块III-结尾的事件对Ctrl-Z和忽略的Ctrl-V也相同,只是按键释放事件:

  1. QLineEdit接收到QKeyEvent(KeyRelease,Key_Z,ControlModifier)
  2. MainWindow接收到QKeyEvent(KeyRelease,Key_Z,ControlModifier)
  3. QLineEdit接收到QKeyEvent(KeyRelease,Key_Control)
  4. MainWindow接收到QKeyEvent(KeyRelease,Key_Control)

P.S.我真的不知道为什么会发生这种情况-但这就是它的工作方式 :)


谢谢!它有效了,但我并没有完全理解...我是覆盖窗口快捷键,还是禁用子级(在这种情况下是LineEdit)的覆盖?当用户按下键时会发生什么以及窗口快捷键覆盖时函数调用的区别,您能发帖说明一下吗? - dv1729
哦,说实话,我不理解为什么Qt事件流以我观察到的方式精确地流动,但我已经更新了答案,试图展示和解释我所见到的区别。 - Mikhail Churbanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接