一、事件类型
1、事件分类
Qt中事件根据源头分为三种类型:
自发事件:由操作系统产生(例如用户按下鼠标,操作系统会产生一个鼠标事件)然后提交到Qt的event loop
Post事件:由应用程序通过postEvent()提交给Qt的event loop的事件
Send事件: 由应用程序通过sendEvent()触发的事件,与Post不一样的是他不会提交到Qt的event loop中,是同步执行的。
2、event loop
当在main()函数中调用QApplication::exec()时会自动开启一个event loop循环,每一次循环都会按照如下的顺序处理事件
while (!exit_was_called) {
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
while (!spontaneous_event_queue_is_empty) {
process_next_spontaneous_event();
}
while (!posted_event_queue_is_empty) {
process_next_posted_event();
}
}
3、postEvent()和sendEvent()
大部分情况下的事件(例如鼠标事件、绘制事件,尺寸调整事件)都是由Qt自动帮我们提交到event loop,然后传递给对应的接受者,我们只需要实现对应的事件hander即可。例如QWidget中对应的各种各样的hander
class MyButton : public QPushButton{
public:
MyButton(QWidget *parent = nullptr) : QPushButton(parent)
{
setStyleSheet("background-color: #ffffff;");
}
void mousePressEvent(QMouseEvent *e) override {
qDebug() << "MyButton::mousePressEvent()";
QPushButton::mousePressEvent(e);
e->ignore();
}
void keyPressEvent(QKeyEvent *) override
{
qDebug() << "MyButton::keyPressEvent()";
}
bool event(QEvent *event) override
{
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "MyButton::event()";
}
return QPushButton::event(event);
}
};
其中event(QEvent *event)的默认实现流程大概如下:
1、根据event->type()的事件类型调用对应的xxxEvent()函数,大部分的系统事件都有对应的hander
2、没有对应xxxEvent()函数的事件则采用默认实现
可以通过QApplication::postEvent()或者QApplication::sendEvent()手动发送一个事件,前者将事件提交到event loop,后者直接触发
// mouseEvent事件必须分配在堆上,内部会持有它,当事件在event loop被使用后,会被自动delete。
auto *mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::postEvent(myButton, mouseEvent);
// mouseEvent事件会被myButton立即处理,且内部不持有它,所以需要自己delete;或者将mouseEvent分配在栈上
auto *mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, QPoint(50, 50), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(myButton, mouseEvent);
delete mouseEvent;
二、事件派发
事件响应链
窗口-->父视图-->子视图1-->子视图2....-->子视图n;越在顶端的子视图则优先处理事件
用户在按钮上点击一下,操作系统会生成一个QMouseEvent事件,添加到Qt的 Event loop,Qt首先找到最终能够响应该鼠标事件的子视图(鼠标点击的位置刚好在子视图上,且该子视图为可见的,处于隐藏状态或者被其它子视图覆盖都算不可见),最后在调用该子视图的event()函数,如果event()返回false,那么事件将传递给事件响应链的上一个视图,否则事件传递终止
三、事件过滤器
正常情况下,发送给一个QObject对象的事件首先会调用event()函数来进行处理,如果想抢在event()函数之前处该事件,可以给该对象添加一个事件过滤器对象,使用方法如下:
class EventFilter:public QObject
{
public:
bool eventFilter(QObject *watched, QEvent *event) override
{
if (event->type() == QEvent::MouseButtonPress) {
qDebug() << "EventFilter::eventFilter()";
// 截获鼠标按下事件
return true;
}
else if event->type() == QEvent::KeyPress{
// 不处理键盘事件
return false;
}
return QObject::eventFilter(watched, event);
}
}
// 给myWidget对象添加事件过滤器
auto *myWidget = new MyWidget;
myWidget->installEventFilter(new EventFilter);
eventFilter()返回true代表事件直接被截获了,false则代表事件会丢回给原先对象的event()函数中去处理
四、消费事件
在事件传递的过程中,除了通过事件过滤器(即eventFilter())决定事件是否消费外,还可以通过调用event的accept()函数来消耗这个事件,当事件被消耗后,它将不再继续传递。