1. 定时器事件
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
oneTimer = startTimer(1000); //开启定时器,并保存定时器Id
}
void Widget::timerEvent(QTimerEvent *event)
{
if(event->timerId() == oneTimer)
{
static int i = 0;
qDebug()<<"定时器 oneTimer"<<i++;
if(i == 10)
{
killTimer(oneTimer); //杀死定时器
}
}
}
自定义事件
首先要明白的是:“在 Qt 里,一个事件就是一个对象,所有事件的祖先都来自于 QEvent”。意思就是说,只要有一个事件发生(如鼠标单击事件),此时就会有一个 QEvent 对象被创建出来,然后开始各种传送。由于 Qt 事件系统是依托于元对象系统的,所以所有的 QObject 类都可以接收/处理 QEvent 事件。
说起事件,其实无非就是围绕着“产生-发送-处理”这个基本流程来说的。
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
1. 如何产生一个事件?
我们从QEvent派生一个子类,取名为MyEvent~
注意的是,每个事件类都有一个唯一的类型标识:type值
class MyEvent : public QEvent
{
public:
MyEvent(const QString&name,int age);
~MyEvent();
friend QDebug operator<<(QDebug&debug,const MyEvent& myEvent);
friend QDebug operator<<(QDebug&debug,const MyEvent* myEvent);
QString name;
int age;
//保存自定义事件的类型,所有自定义MyEvent事件对象共享
inline static QEvent::Type myType = (QEvent::Type)QEvent::registerEventType();
};
MyEvent::MyEvent(const QString&name,int age)
:QEvent(myType)
,name(name)
,age(age)
{}
MyEvent::~MyEvent()
{
qDebug()<<"~MyEvent";
}
QDebug operator<<(QDebug &debug, const MyEvent *myEvent)
{
debug<<*myEvent;
}
QDebug operator<<(QDebug &debug, const MyEvent &myEvent)
{
debug<<"("<<myEvent.name<<myEvent.age<<")";
return debug;
}
好了,现在我们已经认识了 Qt 事件类长什么样了。接下来就是怎么把它发送出去了。
2. 如何发送一个事件?
Qt 提供了三个 static 事件发送函数:sendEvent、postEvent、sendPostEvents。函数原型如下:
void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority)
bool sendEvent(QObject *receiver, QEvent *event)
直接发送:sendEvent
这是最好理解的,两个参数中一个是要发给谁,另一个是发送什么事件。使用notify()函数将事件直接发送给接收方,返回从事件处理程序返回的值(阻塞式),因此事件被发送的时候,event 对象并不会被销毁,因此我们要在栈上创建 event 对象。
MyEvent ev("张三",18);
QApplication::sendEvent(this,&ev);
发到队列:postEvent
我们创建一个 Qt 程序的时候,一般在 main 下面会看到 QCoreApplication a(argc, argv) 以及 return a.exec() 的字样。这其实就是开启了 Qt 事件循环来维护一个事件队列,exec 的本质就是不停的调用 processEvent() 函数从队列中获取事件来处理。而 postEvent() 的作用就是把事件发送到这个队列中去。
这种方式不需要等待处理结果,只要把事件发到队列中就可以了,所以返回值是 void。由于事件队列会持有发送的事件对象,在事件被处理后会自动 delete 掉,所以我们必须在堆上创建 event 对象。
MyEvent *pev = new MyEvent("maye",20);
QApplication::postEvent(this,pev);
3. 如何处理一个事件?
创建了事件,发送了事件,接下来就是怎么接收处理事件了。
- 系统事件通过virtual void event(QEvent *event)处理
- 自定义事件通过virtual void customEvent(QEvent *event)处理,当然也可以通过event来处理
void Widget::customEvent(QEvent *ev)
{
if(ev->type() == MyEvent::myType)
{
MyEvent *myEvent = static_cast<MyEvent*>(ev);
qDebug()<<"customEvent"<<myEvent;
}
}
bool Widget::event(QEvent *ev)
{
if(ev->type() == MyEvent::myType)
{
MyEvent *myEvent = static_cast<MyEvent*>(ev);
qDebug()<<"event"<<myEvent;
return true;
}
return QWidget::event(ev);
}
需要注意的是,重写事件处理器函数时,如果不实现任何功能,最好调用基类的实现。就像上面的那段代码,Qt 本身就已经写了一大堆的实现了,你要是不写上 QWidget::event(ev)这个代码,那你写的这个继承于 QWidget类的 Widget就不会对鼠标点击产生任何反应。正所谓“你要不会干这事,叫你爸爸来做吧”。
至此,一个完整的事件处理过程已经说完了。此时的你应该不仅了解了 Qt 自带的类是如何处理事件的,而且写个自定义事件也是应该是能下手了。接下来我们对事件处理再说说其他方便的功能:过滤、接收/忽略。
事件传播机制
1. 事件分发器
概述: 事件对象创建完毕后,Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
event()函数主要用于事件的分发
如果你希望在事件分发之前做一些操作,就可以重写这个event()函数了。
1、如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
2、在event()函数中,调用事件对象的accept()和ignore()函数是没有作用的,不会影响到事件的传播。
3、记得不关心的事件 记得用父类的事件分发器处理。
声明分发器事件:
virtual bool event(QEvent *event);
重写分发器事件:
bool Widget::event(QEvent *event)
{
if(event->type() == QEvent::KeyPress) //键盘按下处理,其他事件让事件处理器自己处理,不能返回false
{
QKeyEvent *e = static_cast<QKeyEvent *>(event);//把QEvent类型转为QKeyEvent
if(e->key() == Qt::Key_B) //如果是 键盘上的B键 将不会打印键值
{
return QWidget::event(event);
}
qDebug()<<(char)(e->key());
return true;
}
else if(event->type() == QEvent::Timer)//计数器事件
{
//此处添加处理定时器事件的内容,如果想干掉定时器就直接返回true 定时器事件就不会处理
QTimerEvent *ev = static_cast<QTimerEvent *>(event);//把QEvent类型转换为QTimerEvent
timerEvent(ev);
return true; //如果返回true,事件停止传播
}
else if(event->type() == QEvent::MouseMove)//鼠标移动事件 ,需要在构造函数中设置追踪鼠标
{
//鼠标移动处理事件,当返回true时就停止事件处理,所以在返回true前处理事件,打印坐标
QMouseEvent *ev = static_cast<QMouseEvent *>(event);//把QEvent类型转换为QMouseEvent
qDebug()<<QString("Mouse Move:(%1, %2)").arg(ev->x()).arg(ev->y());
return true; //如果返回true,事件停止传播
}
else
{
return QWidget::event(event);
}
}
2. 事件过滤器
概述: 通过前面的章节,我们已经知道,Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目的:事件过滤器
1.在mylabel.cpp的构造函数中加载 事件过滤器
#include "mylabel.h"
#include <QEvent>
#include <QMouseEvent>
#include <QDebu