Qt 之 Eventloop 事件循环

文章目录

  • 需求
  • QEventLoop
  • Demo1- 登录
  • Demo2- 延时
  • Demo3- 在程序中等待服务器返回
  • 理解QT事件循环
    • 事件循环抽象
    • Qt是事件驱动的
    • Qt常见事件
    • Qt事件从哪里来
    • Qt事件队列
    • Qt事件接收
    • Qt事件循环
    • Qt事件同步和异步分发
    • processEvents不阻塞UI
    • Qt 事件循环嵌套

需求

  • 登录时,等待服务器返回,才能知道下一步结果
  • 希望某线程等待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,是将已经加入队列中的准备异步执行的事件立即同步执行。

  • 事件和信号对比
    Qt 之 Eventloop 事件循环_第1张图片

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来循环是不一样的。

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

你可能感兴趣的:(Qt,qt,开发语言)