Qt自定义事件的实现

今天在自定义软件蒙版的时候突然想到一件事,在做蒙版的过程中用到了Qt的事件过滤,结合以前面试的时候记得有人问我说一下什么是windows的消息机制,postMessage和sendMessage有什么区别,记得当时我听到这句话的时候心里无数的羊驼飞奔而过。

后来仔细想一想,Qt虽然集成了很多的控件,方便了很多的新人开发,但同时也慢慢地消耗了我们学习和思考的能力。使我们变得不再追求底层的技术实现。

突然想起,于是就去翻了翻Qt的事件,巩固巩固基础。

我们都知道,Qt的基础就是在不停的做事件循环,但是这个事件究竟是怎么循环的?底层究竟有什么东西,我们很多人都一知半解。

我们在Qt的main.cpp中,main函数的最后一行 return a.exec();有多少人对这个 exec()函数是产生了好奇的。会不会F1进去看一看这个函数的解释是什么。

1、看看a.exec()究竟干了什么

我们都知道,a 是 QApplication 的对象,如果你点进去看过的话,就会发现,Qt里面的 QApplication 是继承 QGuiApplication的,最终归根结底是继承自 QCoreApplication ,所以我们一层一层的递进,去看看exec() 函数的具体实现。

首先我们看下 QCoreApplication::exec()

int QCoreApplication::exec()
{
     threadData->quitNow = false;
     QEventLoop eventLoop;
     self->d_func()->in_exec = true;
     self->d_func()->aboutToQuitEmitted = false;
     int returnCode = eventLoop.exec();
     threadData->quitNow = false;
     if (self) {
         self->d_func()->in_exec = false;
         if (!self->d_func()->aboutToQuitEmitted)
             emit self->aboutToQuit(QPrivateSignal());
         self->d_func()->aboutToQuitEmitted = true;
         sendPostedEvents(0, QEvent::DeferredDelete);
     }

     return returnCode;
 }

从上面的函数中我们可以看到,整个执行了两个,一个是QEventLoop::exec()函数,还有一个是 sendPostedEvents,首先我们再点进去看一看QEventloop中做了什么事情。

while (!d->exit.loadAcquire())
	processEvents(flags | WaitForMoreEvents | EventLoopExec);

上面,在QEventloop::exec()函数中有一个循环,也就是说这个循环一直在处理与标志匹配的挂起事件,直到没有事件为止,这也是 processEvents 函数的职责。

调用 sendPostedEvents 函数会立即对前面已经通过 QCoreApplication::postEvent() 排队等待的所有事件立即进行派发。但是值得注意的是,窗口系统的事件不是有由这个函数派发的,而是由 processEvents进行派发的。

上面函数调用的receiver为0,也就意味着,会向所有对象都发送 QEvent::DeferredDelete 事件。

2、processEvents 函数什么时候可以用

Qt Assistant中对这个函数的说明是:

You can call this function occasionally when your program is busy performing a long operation。

也就是,如果我们的程序目前需要处理一个比较耗时的事情,同时,处理这件事的线程是在GUI线程,那么,我们就很明确的知道,这样做的后果是什么,界面无法响应,卡死。。。这个时候,程序没办法响应用户的任何操作,窗口也不会重新绘制。

处理这样的事情,我们通常的方法是,另起一个线程,线程如何选择请参考《Qt几种多线程的实现》,把这件事放在线程里面去做处理,这也是最简单能够想到的事情,但是如果不想用多线程怎么办?QApplication::processEvents函数提供比较友好的解决方案。

while(int i < 10000000)
{
    //to do something...

    QApplication::processEvents();
}

需要注意的一点是,因为这个函数是多窗口系统的事件进行派发,如果我们一直在不停的触发窗口事件,可能会一直在事件循环里面出不来。

3、Qt事件的流程

简单的说明一下Qt中事件处理的流程:

事件由button的鼠标点击(发送者)->QApplication(信使) ->receiver(接收者)

4、怎么实现自定义事件

Qt 的事件类型那么多,通常情况下是能够满足我们的需求的,但万一我们需要自定义事件,Qt也是支持的,QEvent type 将QEvent::User(1000)- QEvent::MaxUser(65535)之间的区间用来用户自定义事件。

1、子类化QEvent

其实子类化QEvent的过程,也就是我们自定义事件类型的过程。

QEvent::Type MyEvent ::eventType1 = (QEvent::Type)QEvent::registerEventType(QEvent::User + 1);
QEvent::Type MyEvent ::eventType2 = (QEvent::Type)QEvent::registerEventType(QEvent::User + 2);
class MyEvent : public QEvent
{
	MyEvent(Type type) : QEvent(Type(type)) { }
};

上面的实例中,我们注册了两个自定义的事件类型。

2、信使

Qt事件传输过程中我们可以看作信使的是 QCoreApplication,该类提供了方法 postEvent 和 sendEvent。我们可以通过调用这两个方法来实现将自定义事件推送给Qt的事件循环中。

我们通过下面的图来简单的说下这两种方法的区别。

在这里插入图片描述

这个例子中我们偷下懒,就不做 QCoreApplication 的重写了,如果有人想知道这个怎么写的话,参考 《Qt 程序中QApplication对象的重写》。

postEvent 是将事件追加到事件对列里面,并且立即返回,事件有没有被处理其实他是不关心的。
sendEvent 是将事件直接发送给 QCoreApplication 的 notify 函数,会等返回结果。

3、receiver

由上面的图我们可以看出,QCoreApplication 的 notify 函数之后,如果在接收者之前实现了事件的过滤,那么事件会先被观察者过滤,再发送给接收者的event()函数中,值得注意的是,如果在观察者过滤掉事件并把事件处理了,则接收者就收不到事件了。

bool Receiver::eventFilter(QObject *obj, QEvent *event)
{
    if(event->type() == Myevent::eventType)
    {
        qDebug() << "myevent is be filter...";
        MyEventHandler(event);
        Myevent *myEvent = dynamic_cast<Myevent*>(event);

        if(myEvent->isAccepted())
        {
            qDebug() << "the event has been handled...";
            return true;
        }

    }
    return QObject::eventFilter(obj, event);
}

上面这个例子是在过滤的时候顺便处理了这个事件,因此事件接收者的 event() 函数就收不到这个事件了。事件过滤一般都是需要注册的,注册的时候我们可以下注册的对象,不同的注册对象,则收到的事件也是不一样的。

Receiver::Receiver(QWidget *parent) : QWidget(parent), ui(new Ui::Receiver)
{
	ui->setupUi(this);
	qApp->installEventFilter(this);     //方法 1
	
	installEventFilter(this);        //方法 2
}

上面的两种注册方式会有不一样的结果,假设我们选择了第二种,但是我们的发送事件是这样的:

MyEvent event(MyEvent ::eventType);
Receiver receiver;
QApplication::sendEvent(qApp, &event);  //receiver 收不到事件

QApplication::sendEvent(&receiver, &event); //receiver 收到事件

为什么收不到呢?是因为接受对象的问题,qApp 是整个程序的对象,发给他的事件一般都在 QApplication :: event 中被处理了,或者其他地方如果有上面的 qApp->installEventFilter(this); 这样注册事件了过滤,则会发送给其他的过滤器。

同时,在测试的过程中发现,如果我们不用事件过滤,使用event() 函数来接受事件的时候,会出现同样的问题,所以在这个过程中一定要注意函数的用法。

5、自定义事件测试代码

测试代码

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值