Qt事件循环及QEventLoop的使用

这篇具有很好参考价值的文章主要介绍了Qt事件循环及QEventLoop的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、  Qt的事件循环

二、QCoreApplication 主事件循环

三、事件循环的开启

四、Qt中父子事件传递

1、例子

五、processEvents

六、QEventLoop类

七、事件循环的嵌套及QEventLoop模拟同步调用

1、同步获取数据

2、主线程等待

3、对话框弹出


一、  Qt的事件循环

        Qt作为一个跨平台的UI框架,其事件循环实现原理, 就是把不同平台的事件循环进行了封装,并提供统一的抽象接口。和Qt做了类似工作的,还有glfw、SDL等等很多开源库。

二、QCoreApplication 主事件循环

       一般的Qt程序,main函数中都有一个QCoreApplication/QGuiApplication/QApplication,并在末尾调用 exec。

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    //或者QGuiApplication, 或者 QApplication
    ...
    ...
    return app.exec();
}

         Application类中,除去启动参数、版本等相关东西后,关键就是维护了一个QEventLoop,Application的exec就是QEventLoop的exec。不过Application中的这个EventLoop,我们称作“主事件循环”Main EventLoop。所有的事件分发、事件处理都从这里开始。Application还提供了sendEvent和poseEvent两个函数,分别用来发送事件。sendEvent发出的事件会立即被处理,也就是“同步”执行。postEvent发送的事件会被加入事件队列,在下一轮事件循环时才处理,也就是“异步”执行。还有一个特殊的sendPostedEvents,是将已经加入队列中的准备异步执行的事件立即同步执行。

三、事件循环的开启

1、例1

        一般我们的事件循环都是由exec()来开启的,例如下面的例子:

 QCoreApplicaton::exec()
 QApplication::exec()
 QDialog::exec()
 QThread::exec()
 QDrag::exec()
 QMenu::exec()

  这些都开启了事件循环,事件循环首先是一个无限“循环”,程序在exec()里面无限循环,能让跟在exec()后面的代码得不到运行机会,直至程序从exec()跳出。从exec()跳出时,事件循环即被终止。QEventLoop::quit()能够终止事件循环。事件循环实际上类似于一个事件队列,对列入的事件依次的进行处理,当时间做完而时间循环没有结束的时候,其实际上比较类似于一个不占用CPU时间的的for(;;)循环。其本质实际上是以队列的方式来重新分配时间片。

2、例二

1 main()  
2 QApplication::exec()  
3 [...]  
4 QWidget::event(QEvent *)  
5 Button::mousePressEvent(QMouseEvent *)  
6 Button::clicked()  
7 [...]  
8 Worker::doWork()  

      在main()中,我们通过调用QApplication::exec() (如上段代码第2行所示)开启了事件循环。视窗管理者发送了鼠标点击事件,该事件被Qt内核捕获,并转换成QMouseEvent ,随后通过QApplication::notify() (notify并没有在上述代码里显示)发送到我们的widget的event()方法中(第4行)。因为Button并没有重载event(),它的基类QWidget方法得以调用。 QWidget::event() 检测出传入的事件是一个鼠标点击,并调用其专有的事件处理器,即Button::mousePressEvent() (第5行)。我们重载了 mousePressEvent方法,并发射了Button::clicked()信号(第6行),该信号激活了我们worker对象中十分耗时的Worker::doWork()槽(第8行)。

四、Qt中父子事件传递

     如果子widget没有accept或ignore该事件,则该事件会被传递给其父窗口。那么:对于一个继承而来的类,只要我们重写实现了其各个事件处理函数,则对应的事件肯定无法传递给其父widget!  哪怕重写的该事件处理函数的函数体为空!如果是标准的控件对象,则其肯定没重写各个事件处理函数。那消息能不能传递到父widget中,则取决于中途有没有使用事件过滤器等将该信号拦截下来了。

1、例子

      在一个QWidget上建了一个QLabel。而后实现父QWidget的mousePressEvent(), 然后跟一下发现:当我click这个label时:居然能进入到父QWidget的mousePressEvent()中,但是如果把子改成QPushButton则进入不了。

     (1)对于QLabel: 如果不重写mouse处理函数,也没有设置事件过滤器等操作的话,则相当于:其对mouse这个事件一直没有进行处理,那没有进行处理的话,相当于上边所说的情况,此时该事件会被传递给其parent。

     (2)而对于QPushButton而言:当click它时:其会发射clicked()信号,其实这就相当于它对该事件的一个operator过程。所以:这里它accept该事件并进行了对应处理。从而:无法传递给其父窗口。

五、processEvents

        我们的UI界面,要持续不断地刷新(对于QWidget就是触发paintEvent事件),以保证显示流畅、能及时响应用户输入。一般要有一个良好的帧率,比如每秒刷新60帧, 即经常说的FPS 60, 换算一下 1000 ms/ 60 ≈ 16 ms,也就是每隔16毫秒刷新一次。而我们有时候又需要做一些复杂的计算,这些计算的耗时远远超过了16毫秒。在没有计算完成之前,函数不会退出(相当于阻塞),事件循环得不到及时处理,就会发生UI卡住的现象。这种场景下,就可以使用Qt为我们提供的接口,立即处理一次事件循环,来保证UI的流畅。

//耗时操作
doWork1()
//适当的位置,插入一个processEvents,保证事件循环被处理
QCoreApplication::processEvents();

//耗时操作
doWork2()

六、QEventLoop类

QEventLoop即Qt中的事件循环类,主要接口如下:

int exec(QEventLoop::ProcessEventsFlags flags = AllEvents)
void exit(int returnCode = 0)
bool isRunning() const
bool processEvents(QEventLoop::ProcessEventsFlags flags = AllEvents)
void processEvents(QEventLoop::ProcessEventsFlags flags, int maxTime)
void wakeUp()

      其中exec是启动事件循环,调用exec以后,调用exec的函数就会被“阻塞”,直到EventLoop里面的while循环结束。

七、事件循环的嵌套及QEventLoop模拟同步调用

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

1、同步获取数据

         经常会有这种场景: “触发 ”了某项操作,必须等该操作完成后才能进行“ 下一步 ”。比如:数据获取,向服务器发起登录请求后,必须等收到服务器返回的数据,才决定下一步如何执行。这种场景,如果设计成异步调用,直接用Qt的信号/槽即可,如果要设计成同步调用,就可以使用本地QEventLoop。


void A::onFinish(bool r, const QString &info)
{
   m_result = r;
   qDebug() << info;
 //槽中退出事件循环 
  loop.quit(); 
}​

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

​

2、主线程等待

      比如说如果想要将主线程等待100ms,总不能使用sleep吧,那样会导致GUI界面停止响应的,但是用事件循环就可以避免这一点:文章来源地址https://www.toymoban.com/news/detail-403660.html

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

3、对话框弹出

void A::Show()
{
     QDialog dlg;
     dlg.show();
     QEventLoop loop;
     connect(&dlg, SIGNAL(finished(int)), &loop, SLOT(quit()));
     loop.exec(QEventLoop::ExcludeUserInputEvents);
}

到了这里,关于Qt事件循环及QEventLoop的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt 事件过滤器使用QPainter绘制温度

    Qt的 eventFilter 是一个事件过滤器,可以用来捕获和处理Qt对象的事件。事件过滤器可以被安装到一个对象上,以便在该对象上拦截和处理包含特定类型和内容的事件。下面是 eventFilter 的简单使用介绍: 创建一个类,并继承自 QObject 。这个类将作为事件过滤器的实现。 在该类

    2024年02月12日
    浏览(36)
  • DAY4,Qt(事件处理机制的使用,Qt中实现服务器的原理)

    ---chatser.h---头文件 ---chatser.cpp---函数实现文件 ---main.cpp---测试文件 结果展示---     

    2024年02月15日
    浏览(55)
  • JavaScript中的事件循环机制,包括事件循环的原理、宏任务和微任务、事件队列和调用栈、以及如何优化事件循环

    JavaScript中的事件循环机制是JavaScript运行引擎的核心之一,它决定了代码的执行方式和效率。本文将从几个方面介绍JavaScript中的事件循环机制,包括事件循环的原理、宏任务和微任务、事件队列和调用栈、以及如何优化事件循环。 一、事件循环的原理 事件循环是JavaScript中实

    2024年02月05日
    浏览(43)
  • 浏览器事件循环(事件轮询)

    浏览器事件循环(Browser Event Loop)是浏览器用于处理用户输入、网络请求、渲染和其他异步事件的机制。这个循环确保了 JavaScript 代码的执行是非阻塞的,允许浏览器同时处理多个任务,从而提高用户体验。以下是浏览器事件循环的详细说明: 调用栈: 当一个 JavaScript 脚本

    2024年01月19日
    浏览(50)
  • 【Qt】定时器处理——定时器事件类QTimerEvent和定时器类QTimer使用

    Qt的定时器只能通过纯代码实现,定时器顾名思义,主要作用是定时特定的时间。 Qt提供了定时器事件类 QQTimerEvent 和定时器类 QTimer 实现定时器操作。 Qt提供了更高层次的定时器编程接口** QTimer **类,可以使用信号和槽,还可以设置定时一次。比较常用的方法有: QTimer::set

    2024年02月05日
    浏览(45)
  • Node.js 事件循环和事件派发器

    目录 1、process.nextTick() 介绍 2、setTimeout() 3、零延迟 4、setInterval() 5、递归setTimeout 6、setImmediate() 7、Node.js 事件派发器 Node.js中  process.nextTick 函数以一种特殊的方式与事件循环交互。 当你试图理解Node.js事件循环时,它的一个重要部分是process.nextTick()。每次事件循环进行一次完

    2024年02月06日
    浏览(40)
  • 事件循环

    事件循环与浏览器有关,需要先了解其进程模型。 程序运行需要其专属的内存空间,用于存储变量、执行函数等操作,可以将这块内存空间简单地理解为进程。 每个应用 至少有一个进程 ,进程之间 相互独立 ,即使要通信,也需要双方同意。 有了进程后,就可以运行程序的

    2024年02月08日
    浏览(21)
  • 事件循环原理

    何为进程? 程序运行需要有它自己专属的内存空间,可以把这块内存空间简单的理解为进程 每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意。 何为线程? 有了进程后,就可以运行程序的代码了。 运行代码的「人」称之为「线程」。 一个进程至

    2024年02月14日
    浏览(32)
  • 事件循环机制

    聊一下事件循环机制,在开始这篇文章之前,先明确一个概念,js本身是没有事件循环这个定义的。是js被嵌入相应的执行环境(浏览器 / Nodejs),配合当前事件循环的组成部分,具体来说分下面两部分: 在浏览器环境中,事件循环是HTML标准中定义的,用于协调浏览器端的多

    2024年02月08日
    浏览(55)
  • js事件循环详解

    JavaScript的事件循环是一种处理异步事件和回调函数的机制,它是在浏览器或Node.js环境中运行,用于管理任务队列和调用栈,以及在适当的时候执行回调函数。 事件循环的基本原理是,JavaScript引擎在空闲时等待事件的到来,然后将事件添加到事件队列中。事件循环会不断地检

    2024年02月07日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包