本节通过鼠标进入和离开一个标签,来演示这这两个事件:

  • 鼠标进入事件(QEvent::Enter
  • 鼠标离开事件(QEvent::Leave

当鼠标进入或者离开窗口/控件时,会被触发

本节实现效果,如下:
122.gif

1. 通过自定义控件实现

自定义一个标签控件LabelX,让它继承自QLabel,然后重写父类的enterEvent()leaveEvent()

首先,在左侧项目文件名上右键,选择 “Add New…”,选择 “C++ Class”,先继承自QWidget,如下:
image-20260820095436753

然后,把父类由QWidget修改为QLabel,如下:

// labelx.h
#include <QLabel>

class LabelX : public QLabel {
    Q_OBJECT
public:
    explicit LabelX(QWidget *parent = nullptr);

signals:
};

// labelx.cpp
#include "labelx.h"

LabelX::LabelX(QWidget *parent) : QLabel{parent}
{
}

然后,来到LabelX类中,声明并实现enterEvent()leaveEvent()两个函数,如下:

// labelx.h
class LabelX : public QLabel {
protected:
    void enterEvent(QEnterEvent *event);
    void leaveEvent(QEvent *event);
};

// labelx.cpp
static int cnt = 0;     // 定义了一个静态变量,实现对进入和离开的计数;

void LabelX::enterEvent(QEnterEvent *event)
{
    // 使用 Q_UNUSED 宏,可以消除 “unused parameter” 的警告
    Q_UNUSED(event)
    
    // 可以通过 qDebug() 在控制台输出,不过为了更直观,直接显示到标签上;
    // qDebug() << "enterEvent: " << cnt++;
    this->setText(QString("enterEvent: %1").arg(++cnt));
}

void LabelX::leaveEvent(QEvent *event)
{
    Q_UNUSED(event)
    // qDebug() << "leaveEvent: " << cnt++;
    this->setText(QString("leaveEvent: %1").arg(cnt));
}

最后,不要忘记来到enter_leave_widget.cpp,在构造函数中将QLabel修改为LabelX,如下:

#include "labelx.h"

EnterLeaveWidget::EnterLeaveWidget(QWidget* parent) : QWidget{parent}
{
    QVBoxLayout* verticalLayout = new QVBoxLayout(this);
    verticalLayout->setSpacing(0);
    verticalLayout->setContentsMargins(0, 0, 0, 0);

    LabelX* lblx = new LabelX();
    lblx->setFrameShape(QFrame::Box);
    lblx->setFixedHeight(50);
    lblx->setAlignment(Qt::AlignCenter);
    lblx->setStyleSheet("background-color: blue; color: white; font-size: 25px");
    verticalLayout->addWidget(lblx);
}

2. 通过事件过滤器实现

给一个被监控对象,安装一个事件过滤器对象,语法如下:

// 所有发向 monitoredObj 的事件,都会被 filterObj 对象的 eventFilter() 函数截获/过滤
// eventFilter() 函数返回一个 bool 类型:
//     1)返回 true 表示事件被截获,不再发送给 monitoredObj
//     2)返回 false 表示事件会接着传递给 monitoredObj 
monitoredObj->installEventFilter(filterObj);

接下来,通过一个案例来演示事件过滤器的使用

首先,修改EnterLeaveWidget类中, 添加一个QLabel标签,如下:

// labelx.h
#include <QLabel>

class EnterLeaveWidget : public QWidget {
private:
    QLabel* lbl;
};

// labelx.cpp
EnterLeaveWidget::EnterLeaveWidget(QWidget* parent) : QWidget{parent}
{
    // ...

    // 2. 添加一个标准的标签控件
    lbl = new QLabel(this);
    lbl->setFrameShape(QFrame::Box);
    lbl->setFixedHeight(50);
    lbl->setAlignment(Qt::AlignCenter);
    lbl->setStyleSheet("background-color: red; color:white;font-size:25px");
    verticalLayout->addWidget(lbl);
}

然后,在enter_leave_widget.cpp的构造中,为QLabel控件安装事件过滤器,如下:

EnterLeaveWidget::EnterLeaveWidget(QWidget* parent) : QWidget{parent}
{
    // ...

    // 这里将 lbl 的事件过滤器指定为 this,那么所有的发给 lbl 的事件都会被当前窗口截获。
    lbl->installEventFilter(this);
}

最后,修改EnterLeaveWidget类,声明并实现eventFilter()函数,如下:

// enter_leave_widget.h
class EnterLeaveWidget : public QWidget
{
protected:
    bool eventFilter(QObject* watched, QEvent* event);
};

// enter_leave_widget.cpp
#include <QEvent>

static int cnt = 0;

bool EnterLeaveWidget::eventFilter(QObject* watched, QEvent* event)
{
    if (watched == lbl) {
        if (event->type() == QEvent::Enter) {
            lbl->setText(QString("enterEvent: %1").arg(++cnt));
        } else if (event->type() == QEvent::Leave) {
            lbl->setText(QString("leaveEvent: %1").arg(cnt));
        }
    }

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

说明:

  • 这里判断下事件是否是发向lbl的,然后再做处理,因为还有可能监控其他的控件;
  • 通过QEvent类的type()函数,可以判断事件的类型;
  • 以上还通过一个静态变量,来对事件计数;
  • 最后调用下父类的QWidget::eventFilter(watched, event),其他事件交由父类处理;