3

我试图了解 Qt 中的事件过滤机制。为此,我对 QLineEdit 进行了子类化,并尝试在实际 QLineEdit 之前捕获任何按键。我编写的代码(主要从 Qt 文档中粘贴)部分有效:如果我按任意一个键,QLineEdit 会正确显示“Ate key press LETTER”。

如果我按下 Option 键(我在 Mac 上)并按下“S”,我正确地得到“Ate key press ∫”;但是,如果我在按住 Option 键的同时再次按“S”,QLineEdit 会显示“Ate key press ∫∫”,我无法解释。看起来第二次(以及后续)按下“S”键不是 QKeyEvent 或 QShortcutEvent,而是直接传递给实际的小部件,但是,它是什么类型的事件?

更复杂的是,如果在按住 Option 键的同时按下“S”以外的键,结果会因它是哪个键而异。例如,对于键序列 Option+{S,D,F,G,H},QLineEdit 读取“Ate key press ∫∂ƒ™”。但是,如果我继续按“J”,那么 QLineEdit 只会读取“Ate key press ¶”。

任何人都可以复制这种行为并更好地解释它吗?谢谢大家。

主.cpp

#include <QApplication>
#include "customlineedit.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    CustomLineEdit w;
    w.show();
    return a.exec();
}

customlineedit.h

#ifndef CUSTOMLINEEDIT_H
#define CUSTOMLINEEDIT_H

#include <QLineEdit>

class CustomLineEdit : public QLineEdit
{
    Q_OBJECT
public:
    explicit CustomLineEdit(QWidget *parent = 0);
    virtual bool eventFilter(QObject *watched, QEvent *event);
};

#endif // CUSTOMLINEEDIT_H

customlineedit.cpp

#include "customlineedit.h"
#include <QKeyEvent>

CustomLineEdit::CustomLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
    this->installEventFilter (this);
}

bool CustomLineEdit::eventFilter (QObject *watched, QEvent *event)
{
    if (event->type () == QEvent::KeyPress
        || event->type () == QEvent::ShortcutOverride) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        this->setText("Ate key press " + keyEvent->text());
        return true;
    } else if (event->type () == QEvent::Shortcut) {
        this->setText ("Shortcut event");
        return true;
    } else {
        return false;
    }
}
4

1 回答 1

2

那里发生了很多不寻常的事情。

首先,事件过滤器旨在允许 QObject 接收并选择性地对用于另一个 QObject 的事件作出反应。你在做什么,尤其是这一行:

 this->installEventFilter (this);

基本上是将小部件的事件重定向到自身。这与该功能的设计方式不一致,也不能真正反映您将如何在实际项目中使用事件过滤。当这与您非常强大的事件消耗相结合时,您实际上是在破坏小部件。例如,修饰键按下是作为 QKeyEvent 传递的,因此通过在 eventfilter 函数中使用它们,您可能会丢弃快捷方式处理。

您可能没有收到 QShortcut 事件的几个原因。我没有看到任何 QShortcut 对象被附加。在你这样做之前,我不希望得到任何 QShortcutEvent 事件。另外,我相信它们旨在与 QMenus 一起使用,而不是作为在所有小部件中获取修饰符+键序列的通用方式。您通常会使用 QKeyEvent::modifiers() 来做到这一点。

此外,还不清楚您通过过滤快捷方式覆盖事件并将它们转换为键事件来尝试做什么。

根据文档,对于修饰键,keyEvent->text() 将为空,因此可以解释字符...在 Windows 上它显示为空白。

不过,事件过滤器非常方便,并且非常适合保持松散耦合和高度可重用。下面的示例可能是您学习时更好的起点:

主文件

#include <QtGui/QApplication>

#include "customlineedit.h"

#include <QWidget>
#include <QLineEdit>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget w;
    QLineEdit *le1 = new QLineEdit( &w );
    le1->move( 4, 4 );
    le1->setMinimumWidth( 200 );
    CustomLineEdit *le2 = new CustomLineEdit( &w );
    le2->move( 4, 35 );
    le2->setMinimumWidth( 200 );

    le1->installEventFilter (le2);

    w.setMinimumWidth( 260);
    w.show();

    return a.exec();
}

customlineedit.h

#ifndef CUSTOMLINEEDIT_H
#define CUSTOMLINEEDIT_H

#include <QLineEdit>

class CustomLineEdit : public QLineEdit
{
    Q_OBJECT
public:
    explicit CustomLineEdit(QWidget *parent = 0);
    virtual bool eventFilter(QObject *watched, QEvent *event);
};

#endif // CUSTOMLINEEDIT_H

customlineedit.cpp

#include "customlineedit.h"

#include<QShortcutEvent>

CustomLineEdit::CustomLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
}


bool CustomLineEdit::eventFilter (QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
        this->setText("QKeyEvent: " + QString::number(keyEvent->modifiers() ) +" / "+ QString::number(keyEvent->key()) );
        return false;
    } else if (event->type () == QEvent::Shortcut) { //you shouldn't see these here...
        QShortcutEvent *shrtcutEvent = static_cast<QShortcutEvent*>(event);
        this->setText ("Shortcut event: " + shrtcutEvent->key().toString() );
        return false;
    } else {
        return false;
    }
}

编辑:所以我从评论中看到您正在尝试添加 Emacs 样式的自定义快捷方式。好主意啊!这是我在不使用事件过滤的情况下处理它的方法:

#include <QtGui/QApplication>

#include <QLineEdit>
#include <QKeyEvent>
#include <QDebug>


class LineEditEmacs : public QLineEdit
{
    void keyPressEvent( QKeyEvent* event )
    {
        //qDebug()<< QString::number( event->key(), 16 ).toUpper();
        switch ( event->key() )
        {
        case Qt::Key_Up:
            { undo(); return; }
        case Qt::Key_Down:
            { redo(); return; }
        case Qt::Key_Minus: //shift+underscore
            if( (event->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier ) { undo(); return; }

        case Qt::Key_U:
            if( (event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) {
                SetSelectionToCase( true );
                return;
            }
        case Qt::Key_L:
            if( (event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier ) {
                SetSelectionToCase( false );
                return;
            }

        default: 
            break;
        }
        QLineEdit::keyPressEvent( event );
    }

    void SetSelectionToCase( bool Upper )
    {
        if( !hasSelectedText() )
            return;

        QString s = selectedText();
        int iPreviousCursorPos = cursorPosition();
        del();
        QString su = Upper? s.toUpper() : s.toLower();
        insert( su );
        int iNewCursorPos = cursorPosition();

        //restore selection
        if( iPreviousCursorPos < iNewCursorPos )
            cursorBackward( true, su.length() );
        else if( iPreviousCursorPos == iNewCursorPos )
            setSelection( iPreviousCursorPos-su.length(), su.length() );
    }



};


int main( int argc, char *argv[] )
{
    QApplication a( argc, argv );
    LineEditEmacs w;
    w.show();
    return a.exec();
}

您也可以通过这种方式吞下键和键+修饰符组合。

于 2013-09-23T17:13:48.383 回答