Qt 之 Eventloop 事件循环

需求

  • 登录时,等待服务器返回,才能知道下一步结果
  • 希望某线程等待100ms,但不会影响UI刷新
  • 在程序里等待服务器返回

QEventLoop

At any time, you can create a QEventLoop object and call exec() on it to start a local event loop. From within the event loop, calling exit() will force exec() to return.

  • QEventLoop类为我们提供了一种进入和退出一个事件循环的方法。在任何时候,你都可以创建一个QEventLoop实例,然后调用exec()来启动一个事件循环,在这个循环期间,可以调用exit()来强制使exct()返回。
  • exec( )之后的代码不会执行,直至从exec( )跳出。

Demo1- 登录

bool login(const QString &userName, const QString &passwdHash, const QString &slat)
{
    //声明本地EventLoop
    QEventLoop loop;
    bool result = false;
    //先连接好信号
    connect(&network, &Network::result, [&](bool r, const QString &info){
        result = r;
        qDebug() << info;
        //槽中退出事件循环
        loop.quit();
    });
    //发起登录请求
    sendLoginRequest(userName, passwdHash, slat);
    //启动事件循环。阻塞当前函数调用,但是事件循环还能运行。
    //这里不会再往下运行,直到前面的槽中,调用loop.quit之后,才会继续往下走
    loop.exec();
    //返回result。loop退出之前,result中的值已经被更新了。
    return result;
}

登录QEventLoop Demo

Demo2- 延时

1 QEventLoop loop;
2 QTimer::singleShot(100, &loop, SLOT(quit()));
3 loop.exec();

主线程延时Demo

Demo3- 在程序中等待服务器返回

  • 增加事件循环
//以上省略了部分代码,这是使用HTTP中的post来发送文件到服务器
QNetworkReply *postReply =  m_pNetManager->post(request, qbt);  //post方式到本地服务器
connect(postReply, SIGNAL(finished()), this, SLOT(postFileReplyFinished()));  //成功后会有返回响应
loop->exec();  //设置等待,若文件成功发送,则退出等待
  • 在服务器接收中退出事件循环

void Widget::postFileReplyFinished()
{
    QNetworkReply* reply = (QNetworkReply*)sender();
    QByteArray replyData = reply->readAll();
 
    //转为JSon格式,便于提取字段数据
    QJsonDocument jsonDoc= QJsonDocument::fromJson(replyData);
 
    if(!jsonDoc.isNull())
    {
        QJsonObject jsonObj = jsonDoc.object();  //转换格式
        if(jsonObj.contains("status"))
        {
            loop->exit();  //loop退出等待
        }
    }
}

在程序中等待服务器返回 Demo

理解QT事件循环

事件循环抽象

function loop() {initialize();
    bool shouldQuit = false;
    while(false == shouldQuit)
    {
        var message = get_next_message();
        process_message(message);
        if (message == QUIT) 
        {
            shouldQuit = true;
        }}
}

Qt是事件驱动的

  • Qt将系统产生的信号(软件中断)转换成Qt事件,并且将事件封装成类,所有的事件类都是由QEvent派生的,事件的产生和处理就是Qt程序的主轴,且伴随着整个程序的运行周期。因此我们说,Qt是事件驱动的。

Qt常见事件

  • 鼠标事件(QMouseEvent)
  • 键盘事件(QKeyEvent)
  • 绘制事件(QPaintEvent)
  • 窗口尺寸改变(QResizeEvent)
  • 滚动事件(QScrollEvent)
  • 控件显示(QShowEvent)
  • 控件隐藏(QHideEvent)
  • 定时器事件(QTimerEvent)

Qt事件从哪里来

  • Qt的官方手册说,事件有两个来源:程序外部和程序内部,多数情况下来自操作系统并且通过spontaneous()函数返回true来获知事件来自于程序外部,当spontaneous()返回false时说明事件来自于程序内部。

Qt事件队列

Qt事件(外部或内部)来了之后,进入事件队列,然后由派发器派发。

Qt事件接收

  • QObject!它是所有Qt类的基类!是Qt对象模型的核心!QObject类的三大核心功能其中之一就是:事件处理。QObject通过event()函数调用获取事件。所有的需要处理事件的类都必须继承自Qobject,可以通过重定义event()函数实现自定义事件处理或者将事件交给父类。
    Qt事件接收

Qt事件循环

  • 事件是一个类对象具有特定的类型,事件多数情况下是被分发到一个队列中(事件队列),当队列中有事件时就不停的将队列中的事件发送给QObject对象,当队列为空时就阻塞地等待事件,这个过程就是事件循环!
  • QCoreApplication::exec()开启了这种循环,一直到QCoreApplication::exit()被调用才终止,所以说事件循环是伴随着Qt程序的整个运行周期!
App启动
Eventloop.exec
获取事件
派发事件
处理事件1
处理事件n
处理事件3:退出循环
Eventloop.quit

Qt事件同步和异步分发

  • sendEvent

sendEvent发出的事件会立即被处理,也就是“同步”执行。

  • postEvent

postEvent发送的事件会被加入事件队列,在下一轮事件循环时才处理,也就是“异步”执行。

  • sendPostedEvents

还有一个特殊的sendPostedEvents,是将已经加入队列中的准备异步执行的事件立即同步执行。

  • 事件和信号对比
    事件和信号对比

processEvents不阻塞UI

  • 60 FPS, 1s 60帧, 1000ms/60 , 约16ms刷新一次
  • 复杂计算> 16ms
  • 没有完成计算,不会退出, UI卡住
  • 使用postEvents
//耗时操作
someWork1()
//适当的位置,插入一个processEvents,保证事件循环被处理
QCoreApplication::processEvents();

//耗时操作
someWork2()

Qt 事件循环嵌套

事件循环是可以嵌套的,当在子事件循环中的时候,父事件循环中的事件实际上处于中断状态,当子循环跳出exec之后才可以执行父循环中的事件。当然,这不代表在执行子循环的时候,类似父循环中的界面响应会被中断,因为往往子循环中也会有父循环的大部分事件,执行QMessageBox::exec(),QEventLoop::exec()的时候,虽然这些exec()打断了main()中的QApplication::exec(),但是由于GUI界面的响应已经被包含到子循环中了,所以GUI界面依然能够得到响应。

子层事件循环具有父层事件循环的所有功能,所以当在主线程中启动各种exec()(比如QEventLoop::exec())时,虽然会打断main函数中的QApplication::exec(),但是Gui界面还是可以正常响应,不会出现卡住的现象。这与用while来循环是不一样的。

如果某个子事件循环仍然有效,但其父循环被强制跳出,此时父循环不会立即执行跳出,而是等待子事件循环跳出后,父循环才会跳出

  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt 中的事件处理机制是基于事件驱动的,主要分为以下几个部分: 1. 事件循环Event Loop):Qt 应用程序通过事件循环来等待事件的发生。事件循环是一个无限循环,等待事件的发生,当有事件发生时,将事件传递给相应的对象进行处理。 2. 事件(Event):事件是 Qt 应用程序中的基本单位,例如鼠标点击、键盘按键等。在 Qt 中,事件被封装成 QEvent 类,不同类型的事件对应不同的 QEvent 类型。 3. 事件过滤器(Event Filter):事件过滤器是一个对象,用于拦截和处理其他对象的事件。事件过滤器可以在事件到达目标对象之前对事件进行预处理,也可以在事件到达目标对象之后对事件进行后处理。 4. 事件处理器(Event Handler):事件处理器是一个对象,用于处理自己接收到的事件。每个对象都可以有自己的事件处理器,用于处理特定类型的事件。在 Qt 中,事件处理器通常是通过重写 QObject 类的事件处理函数来实现的。 5. 事件分发(Event Dispatch):在事件循环中,当事件到达目标对象时,Qt 将事件分发给目标对象的事件处理器进行处理。如果目标对象没有事件处理器,事件将被传递给目标对象的父对象进行处理,直到事件被处理或者到达应用程序的根对象为止。 总的来说,Qt 中的事件处理机制是一种高效、灵活和可扩展的事件驱动模型,可以帮助开发者轻松地处理各种事件,实现各种功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值