Qt - QObject事件

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. 事件过滤器</

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值