您的位置:

installeventfilter —— 一个事件过滤器

installeventfilter作为Qt GUI编程中的一个重要组成部分,在Qt很多实际开发项目中经常被使用。installeventfilter的作用是为对象安装一个事件过滤器,从而可以截获对象的事件。在下面的文章中,我们将从多个方面来详细探讨installeventfilter的作用、使用方法和可能的异常情况。

一、installeventfilter无法

1、安装事件过滤器失败
如果对象的继承链中没有eventFilter函数实现,则调用installEventFilter()函数时会失败,应该确保对象中eventFilter函数的重载实现。

    class CustomWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit CustomWidget(QWidget *parent = nullptr);
    
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override;
    }
    
    CustomWidget::CustomWidget(QWidget *parent)
        : QWidget(parent)
    {
        QWidget *button = new QPushButton();
        button->installEventFilter(this); // 此处将控件button的事件过滤器设置为CustomWidget,需确保CustomWidget实现了eventFilter函数
    }
    
    bool CustomWidget::eventFilter(QObject *obj, QEvent *event)
    {
        Q_UNUSED(obj);
        if (event->type() == QEvent::MouseMove) {
            // 在此处理移动事件
            return true;
        }
        return QWidget::eventFilter(obj, event);
    }

2、事件过滤器重复设置
使用一个对象为另一个对象安装一个事件过滤器时,应该确保只能安装一次。因为重复安装会造成事件被多次过滤,从而产生意料之外的结果。

    QWidget *button = new QPushButton();
    if (!button->parent()) {
        // 检查button是否已有父对象
        CustomWidget *widget = new CustomWidget();
        button->installEventFilter(widget);
    }

二、installeventfilter 异常退出

1、事件过滤器卸载失败
如果对象已经被销毁或事件过滤器并未被正确卸载,则安装事件过滤器会产生异常。应该在Widget被销毁前手动卸载事件过滤器。

    CustomWidget *widget = new CustomWidget();
    QWidget *button = new QPushButton();
    button->installEventFilter(widget);
    // 在程序退出前,手动卸载事件过滤器
    button->removeEventFilter(widget); 

2、过滤的事件类型错误
在事件过滤器中,只能处理特定类型的事件。如果过滤事件类型错误,将会导致程序异常退出。应该在实现过滤器函数时,先判断事件类型是否匹配。

    bool CustomWidget::eventFilter(QObject *obj, QEvent *event)
    {
        if (event->type() == QEvent::KeyPress && obj == button) { // 该事件过滤器处理某个push button对象的按键事件
            QKeyEvent *keyEvent = static_cast(event);
            if (keyEvent->key() == Qt::Key_Delete) { // 只处理某个按键事件
                // 在此处理删除事件
                return true;
            }
        }
        return QWidget::eventFilter(obj, event);
    }

  

三、installeventfilter(this)选取

1、多个对象使用同一个事件过滤器
在处理多个对象的相同事件时,可以为这些对象安装同一个事件过滤器。这时可以使用this作为事件过滤器,因为在对象内部,this实际上就是这个对象的地址。这样可以简化代码,提高程序效率。

    QLineEdit *line1 = new QLineEdit();
    QLineEdit *line2 = new QLineEdit();
    QLineEdit *line3 = new QLineEdit();
    // 三个QLineEdit共用一个事件过滤器
    line1->installEventFilter(this);
    line2->installEventFilter(this);
    line3->installEventFilter(this);
    
    bool CustomWidget::eventFilter(QObject *obj, QEvent *event)
    {
        if (event->type() == QEvent::FocusIn && obj == line1) { // 该事件过滤器处理多个LineEdit对象的获取焦点事件
            // 在此处理焦点事件
            return QWidget::eventFilter(obj, event);
        } else if (event->type() == QEvent::FocusIn && obj == line2) {
            // 在此处理焦点事件
            return QWidget::eventFilter(obj, event);
        } else if (event->type() == QEvent::FocusIn && obj == line3) {
            // 在此处理焦点事件
            return QWidget::eventFilter(obj, event);
        }
        return QWidget::eventFilter(obj, event);
    }

2、给子对象安装事件过滤器
在父对象中,给所有子对象安装同一个事件过滤器也可以使用this作为事件过滤器。这样可以减少代码量,提高程序效率。

    CustomWidget::CustomWidget(QWidget *parent)
        : QWidget(parent)
    {
        QLineEdit *line1 = new QLineEdit();
        QLineEdit *line2 = new QLineEdit();
        QLineEdit *line3 = new QLineEdit();
        // 所有LineEdit共用一个事件过滤器
        line1->installEventFilter(this);
        line2->installEventFilter(this);
        line3->installEventFilter(this);
    }   

四、installeventfilter的使用示例

    // 为一个QLabel安装事件过滤器,当该QLabel鼠标移动时,在状态栏显示该位置的坐标
    class CustomWidget : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit CustomWidget(QWidget *parent = nullptr);
    
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override;
    
    private:
        QLabel *label;
        QStatusBar *statusBar;
    };
    
    CustomWidget::CustomWidget(QWidget *parent)
        : QWidget(parent)
    {
        label = new QLabel("Label");
        statusBar = new QStatusBar();
        QHBoxLayout *layout = new QHBoxLayout();
        layout->addWidget(label);
        setLayout(layout);
        setStatusBar(statusBar);
    
        label->installEventFilter(this); // 为label安装事件过滤器
    }
    
    bool CustomWidget::eventFilter(QObject *obj, QEvent *event)
    {
        if (obj == label && event->type() == QEvent::MouseMove) { // 该事件过滤器处理QLabel鼠标移动事件
            QMouseEvent *mouseEvent = static_cast(event);
            QString status = QString("坐标:%1,%2").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y());
            statusBar->showMessage(status);
        }
        return QWidget::eventFilter(obj, event);
    }