Qt事件系统
在 Qt 里,一个事件就是一个对象,所有事件的祖先都来自于 QEvent 只要有一个事件发生(如鼠标单击事件),此时就会有一个 QEvent 对象被创建出来,然后开始各种传送。由于 Qt 事件系统是依托于元对象系统的,所以所有的 QObject 类都可以接收/处理 QEvent 事件。
Event的来源:
Some events, such as QMouseEvent and QKeyEvent, come from the window system;
一些事件,如QMouseEvent和QKeyEvent,来自窗口系统;
some, such as QTimerEvent, come from other sources;
有些,如QTimerEvent,来自其他来源;
some come from the application itself.
一些来自应用程序本身。
——The Event System
Event的去向:
Qt’s main event loop (QCoreApplication::exec()) fetches “native window system events” from the event queue, translates them into “QEvents”, and sends the translated events to “QObjects”.
Qt的主事件循环(QCoreApplication::exec())从事件队列中获取“本机窗口系统事件”,将它们转换为“QEvents”,并将转换后的事件发送给“QObjects”。
——QEvent
产生一个事件
Qt 自带的事件类有很多很多,有些事件类还附带了简单的函数,例如 QResizeEvent 这个事件类就有 size() 和 oldSize() 函数;有些事件类还支持多种实际操作,比如 QMouseEvent 支持鼠标单击、双击、移动等,也就是说当鼠标进行这些操作的时候,都会产生一个 QMouseEvent 事件对象。
自定义的一个事件类
class MyEvent : public QEvent
{
public:
MyEvent();
MyEvent(int x, int y, int z);
static const Type type;
int x;
int y;
int z;
};
Qt 提供了一个函数 registerEventType() 专门用于自定义事件的注册。如下:
const QEvent::Type MyEvent::type = (QEvent::Type)QEvent::registerEventType()
发送一个事件
Qt 提供了三个 static 事件发送函数:sendEvent、postEvent、sendPostEvents。函数原型如下:
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
void QCoreApplication::sendPostedEvents(QObject *receiver = Q_NULLPTR, int event_type
直接发送:sendEvent
这是最好理解的,两个参数中一个是要发给谁,另一个是发送什么事件。这种方式的发送我们要等待对方处理,所以返回值是一个 bool 量来判断。对于许多事件类,有一个名为 isAccepted() 来告诉你是否被处理。因为事件被发送的时候,event 对象并不会被销毁,因此我们要在栈上创建 event 对象。
发到队列:postEvent
我们创建一个 Qt 程序的时候,一般在 main 下面会看到 QCoreApplication a(argc, argv) 以及 return a.exec() 的字样。这其实就是开启了 Qt 事件循环来维护一个事件队列,exec 的本质就是不停的调用 processEvent() 函数从队列中获取事件来处理。而 postEvent() 的作用就是把事件发送到这个队列中去。
这种方式不需要等待处理结果,只要把事件发到队列中就可以了,所以返回值是 void。由于事件队列会持有发送的事件对象,在 post 的时候会将事件 delete 掉,所以我们必须在堆上创建 event 对象。
在队列中立即处理:sendPostedEvents
看参数我们就可以知道,这个函数的作用就是立刻、马上将队列中的 event_type 类型的事件立马交给 receiver 进行处理。需要注意的是,来自窗口系统的事件并不由这个函数进行处理,而是 processEvent()。
处理事件
事件的传递过程
QEvent的 accept()和ignore()
void MyPushButton::mousePressEvent(QMouseEvent *e)
{
qDebug() << e->isAccepted();//true 这个是事件对象默认的即 在Qt中 事件的传递是默认接收的
if(e->button() == Qt::LeftButton){//表示接受处理了该事件
qDebug() << "LeftButton";
//如果在这里调用了ignor函数
e->ignore();//表示告诉Qt MyPushButton这个对象没有处理该事件,让Qt继续传递给别的对象。
//但是这里要注意,这个事件继续传递的对象不是基类QPushButton,而是MyPushButton所属的的父组件
}else{//表示忽略不处理该事件
QPushButton::mousePressEvent(e);
}
}
而accept()和ignore()函数的正确用法是用在重写CloseEvent函数中
void Widget::closeEvent(QCloseEvent *event)
{
int ret = QMessageBox::question(this,"关闭窗口","是否要关闭窗口");
if(ret == QMessageBox::Yes){
event->accept();
}else if(ret == QMessageBox::No){
event->ignore();//继续传递给它的父组件
QWidget::closeEvent(event);//如果本身就是最顶层的组件 则不需要该语句
}
}
QEvent 和 EventFilter 过滤事件
测试代码
#include <QApplication>
#include<QWidget>
#include<QObject>
#include <iostream>
using namespace std;
QEvent::Type t1=(QEvent::Type)QEvent::registerEventType(1333);
QEvent e1(t1); //使用 QEvent 的构造函数创建自定义事件
//t2 的值与 t1 重复,使用 registerEventType 会自动产生一个合法的值
QEvent::Type t2=(QEvent::Type)QEvent::registerEventType(1333);
QEvent e2(t2);
//event 函数的作用就在于 事件的分发,在 even filter 之后执行
//传入的事件已被识别并且处理,则需要返回 true,否则返回 false。
//如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
class A:public QWidget
{
public:
bool event(QEvent* e){
if(e->type()==t1) {
cout<<"AE"<<e->type()<<",";
f1((QEvent*)e);
//
return true;
}
if(e->type()==t2) {
cout<<"BE "<<e->type()<<",";
f2((QEvent*)e);
return true;
}
//调用父组件
return QWidget::event(e);
} //event 结束
void f1(QEvent *e){cout<<"F1"<<endl;}
void f2(QEvent *e){cout<<"F2"<<endl;}
};
//返回true为不再转发,false则让其继续被处理
class B:public QObject
{
public:
bool eventFilter(QObject *w, QEvent *e)
{
Q_UNUSED(e);
if(e->type()==t1)
{
cout<<"A"<<endl;
//e->accept();
//事件不再进行传播,拦截
return true;
}
if(e->type()==t2)
{
cout<<"B"<<endl;
//e->ignore();
return false;
}
//也可直接 return false; 不进行拦截, 且不继续传播
//return false;
//当不确定是否继续传播时,按照父类的方法来处理
//即调用父类的evenFilter函数
return B::eventFilter(w, e);
}
};
int main(int argc, char *argv[])
{
QApplication aa(argc,argv);
A ma; B mb;
ma.installEventFilter(&mb); //安装事件过滤器
aa.postEvent(&ma,&e1);
aa.postEvent(&ma,&e2);
aa.sendEvent(&ma,&e1);
aa.sendEvent(&ma,&e2);
ma.resize(333,222);
ma.show();
aa.exec();
return 0;
}
定时器
QTimer和timerEvent的功能完全一致,QTimer更为清晰和灵活,而timerEvent更为简约和高效。大家在项目中使用定时器,还是要根据项目的具体情况而定。
//.h文件
private slots:
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
private:
int timerId(0); //用于区分不同的计时器,示例举了2个计时器的情况
int timerId2(0);
//.cpp文件
timerId = startTimer(200);
timerId2 = startTimer(1000);
//timerEvent()函数内
if (event->timerId() == timerId)
{
...
}
else if (event->timerId() == timerId2)
{
...
killTimer(timerId2)
}
reference
QT 事件的传递先后顺序_LearnLHC的博客-优快云博客_qt事件传递顺序
Qt事件的接收和忽略_wei375653972的博客-优快云博客