Qt开发-理解事件循环
简介
这次讨论事件循环相关的知识点。
【资料图】
事件与事件循环
Hello World
从Hello World说起吧
#include int main(int argc, char *argv[]) { printf(\"Hello World\"); return 0;}
这是一段大家都很熟悉的命令行程序,运行起来会在终端输出”Hello World”,之后程序就退出了。
循环处理
我们稍微加点需求: 程序能够一直运行,每次用户输入一些信息并按下回车时,打印出用户的输入。直到输入的内容为“quit”时才退出。
按照这个需求,代码实现如下:
#include #include int main(int argc, char* argv[]){ char input[1024]; //假设输入长度不超过1024 const char quitStr[] = \"quit\"; bool quit = false; while (false == quit) { scanf_s(\"%s\", input, sizeof input); printf(\"user input: %s
\", input); if (0 == memcmp(input, quitStr, sizeof quitStr)) { quit = true; } } return 0;}
我们使用了一个while循环。在这个循环体内,不停地处理用户的输入。当输入的内容为”quit”时,循环终止条件被设置为true,循环将终止。
类比事件循环的概念
在上面这个例子中,“用户输入并按下回车”这件事情,我们可以称作一个“事件”或者“用户输入事件”,不停的去处理“事件”的这段代码,
我们可以称作“事件循环”, 也可以叫做”消息循环”,是一回事。
一般对于带UI窗口的程序来说,“事件”是由操作系统或程序框架在不同的时刻发出的。
当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,计时器触发的时候,都会发出一个相应的事件。
我们把“事件循环”的代码 提炼/抽象 如下:
function loop() { initialize(); bool shouldQuit = false; while(false == shouldQuit) { var message = get_next_message(); process_message(message); if (message == QUIT) { shouldQuit = true; } }}
在事件循环中, 不停地去获取下一个事件,然后做出处理。直到quit事件发生,循环结束。
有“取事件”的过程,那么自然有“存储事件”的地方,要么是操作系统存储,要么是软件框架存储。
存储事件的地方,我们称作 “事件队列” Event Queue
处理事件,我们也称作 “事件分发” Event Dispatch
不同操作系统的事件循环
Windows
先来看一个Windows系统的事件循环示例(win32 API):
MSG msg = { 0 };bool done = false;bool result = false;while (!done){ if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (msg.message == WM_QUIT) { done = true; }}
思路和前面介绍的一致
Linux X11窗口
有些linux系统使用X11窗口系统,看看其窗口事件循环
Atom wmDeleteMessage = XInternAtom(mDisplay, \"WM_DELETE_WINDOW\", False);XSetWMProtocols(display, window, &wmDeleteMessage, 1);XEvent event;bool running = true;while (running){ XNextEvent(display, &event); switch (event.type) { case Expose: printf(\"Expose
\"); break; case ClientMessage: if (event.xclient.data.l[0] == wmDeleteMessage) running = false; break; default: break; }}
思路也是和前面一致的
MacOS Cocoa Application
在Cocoa Application中, 有一种获取事件的机制,叫做runloop(一个NSRunLoop对象,它允许进程接收窗口服务的各种事件)
一般的Cocoa Application运行流程是,从runloop的事件队列中获取一个事件(NSEvent)
派发事件(NSEvent)到合适的对象(Object)
事件被处理完成后,再取下一个事件(NSEvent),直到应用退出.
思路也是和前面一致的。
Qt的事件循环
Qt作为一个跨平台的UI框架,其事件循环实现原理, 就是把不同平台的事件循环进行了封装,并提供统一的抽象接口。
和Qt做了类似工作的,还有glfw、SDL等等很多开源库。
QEventLoop类
QEventLoop即Qt中的事件循环类,主要接口如下:
int exec(QEventLoop::ProcessEventsFlags flags = AllEvents)void exit(int returnCode = 0)bool isRunning() constbool processEvents(QEventLoop::ProcessEventsFlags flags = AllEvents)void processEvents(QEventLoop::ProcessEventsFlags flags, int maxTime)void wakeUp()
其中exec是启动事件循环,调用exec以后,调用exec的函数就会被“阻塞”,直到EventLoop里面的while循环结束。
这里画个简单的示意图:
exit是退出事件循环(将EventLoop中的退出标识设为true)
processEvents是及时处理队列中的事件(这个很有用,后面还会讲)。
这里有个问题,exec阻塞了当前函数,还怎么退出EventLoop呢?
答案是:在派发事件后,某个事件处理的函数中,达到事件退出条件时,调用exit函数,将EventLoop中的退出标识设为true。
这样的程序运行流程,我们叫做 “事件驱动”式的程序。
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,是将已经加入队列中的准备异步执行的事件立即同步执行。
Qt的事件分发和事件处理
以QWidget为例来说明。
QWidget是Widget框架中,大部分UI组件的基类。QWidget类拥有一些名字为xxxEvent的虚函数,比如:
virtual void keyPressEvent(QKeyEvent *event)virtual void keyReleaseEvent(QKeyEvent *event)
keyPressEvent就表示按键按下时的处理,keyReleaseEvent表示按键松开时的处理。
主事件循环中(注册过QWidget类之后),事件分发会在按键按下时调用QWidget的keyPressEvent函数,按键松开时调用QWidget的keyReleaseEvent函数。
重载事件
有了上面的事件处理机制,我们就可以在自己的QWidget子类中,通过重载keyPressEvent、keyReleaseEvent等等事件处理函数,做一些自定义的事件处理。
QEvent
每一个事件处理函数,都是带有参数的,这个参数是QEvent的子类,携带了各种事件的参数。比如
按键事件 void keyPressEvent(QKeyEvent *event) 中的QKeyEvent, 就包括了按下的按键值key、 count等等。
事件过滤器
Qt还提供了事件过滤机制,在事件分发之前先过滤一部分事件。
用法如下:
class KeyPressEater : public QObject{ Q_OBJECT ...protected: bool eventFilter(QObject *obj, QEvent *event) override;};bool KeyPressEater::eventFilter(QObject *obj, QEvent *event){ if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); qDebug(\"Ate key press %d\", keyEvent->key()); return true; } else { // standard event processing return QObject::eventFilter(obj, event); }}。。。 monitoredObj->installEventFilter(filterObj);
自定义一个QObject子类,重载eventFilter函数。之后在要过滤的QObject对象上,调用installEventFilter函数以安装过滤器上去。
过滤器函数的返回值为bool,true表示这个事件被过滤掉了,不用再往下分发了。false表示没有过滤。
事件循环的运用
processEvents不阻塞UI
我们的UI界面,要持续不断地刷新(对于QWidget就是触发paintEvent事件),以保证显示流畅、能及时响应用户输入。
一般要有一个良好的帧率,比如每秒刷新60帧, 即经常说的FPS 60, 换算一下 1000 ms/ 60 ≈ 16 ms,也就是每隔16毫秒刷新一次。
而我们有时候又需要做一些复杂的计算,这些计算的耗时远远超过了16毫秒。
在没有计算完成之前,函数不会退出(相当于阻塞),事件循环得不到及时处理,就会发生UI卡住的现象。
这种场景下,就可以使用Qt为我们提供的接口,立即处理一次事件循环,来保证UI的流畅
(后续再讨论多线程)
//耗时操作someWork1()//适当的位置,插入一个processEvents,保证事件循环被处理QCoreApplication::processEvents();//耗时操作someWork2()
QEventLoop模拟同步调用
经常会有这种场景: “触发 ”了某项操作,必须等该操作完成后才能进行“ 下一步 ”
比如:软件的登录界面,向服务器发起登录请求后,必须等收到服务器返回的登录数据,才知道登录结果并决定下一步如何执行。
这种场景,如果设计成异步调用,直接用Qt的信号/槽即可,如果要设计成同步调用,就可以使用本地QEventLoop
这里写段伪代码示例一下:
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;}
【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】
点击这里:
标签:
推荐文章
- Qt开发-理解事件循环
- 【报资讯】东吴证券:港股为何还有些脆弱? 何时才能强势反弹?
- 【成语漫话世界】自食其果
- 沅陵县沅陵镇鸳鸯山社区“微宣讲”之声 助推基层治理
- 世界快讯:奥密克戎新亚株横扫印度新加坡,我们的口罩还能摘下来吗?
- 每日速读!线上活动形式一般有哪些(线上活动形式有哪些)
- 即时:湖北:35条新举措“全周期”呵护个体户
- 聊城市茌平区委书记:拉满招商引资“弯弓” 激活高质量发展“一池春水”|环球今日报
- 华昌达:与华工科技签署战略合作框架协议
- 北京开展规范旅游市场价格秩序专项执法行动 播报
- 世界热门:带圆圈的数字怎么打ppt_带圆圈的数字怎么打11
- 两家美国公司签署激光聚变能源商业化合作备忘录
- 网师园夜游免票政策 当前要闻
- 实时:中国蛇类DNA条形码参考数据集发布
- 教育部:推开教职员工准入查询工作 对师德违规问题“零容忍” 全球通讯
- 4月20日连豆油早盘下跌
- 微头条丨刘家铎、尹国辉、张大旗被处分
- 中国电信:2023年第一季度净利润约79.84亿元 天天微头条
- VERTU手机威图手机5G新品碳纤维款直降2000+送皮套
- 工信部将强化中小企业在产业链供应链上的配套能力
- 魅族 20 Pro简评:当年的那个魅族,回来了! 当前看点
- 求贤若渴 引才有道 世界通讯
- 暗光长焦加持!OPPO Reno10 Pro+曝光_世界速读
- 【时快讯】美容院十大品牌排行_美容院十大品牌排行榜
- 信用卡协商停息挂账收费怎么收?停息挂账的好处和危害是什么? 环球讯息
- 天天微速讯:一位付费API工程师如何制造了推特今年第七次崩溃?
- 世界快报:教育部:正积极建设国家智慧教育平台 向全社会提供优质数字学习资源
- 农业农村部:预计未来5到10年农业农村投资需求近15万亿元|当前播报
- 焦点滚动:无人驾驶融入生产生活(新时代画卷)
- 杭叉集团(603298):盈利能力显着提升 锂电出海逻辑强化
- 千禧年是什么意思 千禧年是哪一年|天天热消息
- 康希诺跌6.1%创新低 2020年上市募52亿中信证券保荐
- 小摩减持长城汽车(02333)约945.85万股 每股作价约8.85港元
- 当KD正面对决小卡,谁是天下第一前锋?
- 湖北、浙江、广西,大消息来了!|今日播报
- 兰州潮宏基今日黄金价格查询(2023年4月19日)_环球信息
- 海鸥住工:公司截至4月10日股东总户数为:37,572户,请了解
- 西安长安十二时辰主题街区将于明日上午10时恢复对外开放
- 湖人输球5大罪人出炉!浓眉创耻辱纪录,拉塞尔难配高薪哈姆嘴硬
- 121家公司获机构调研(附名单)
- 每日看点!上汽通用汽车携34款展车盛装亮相2023上海车展
- 每日精选:农业农村部:生猪养殖有望在二季度末实现扭亏为盈
- 用好“新”钥匙开启产业振兴“致富门”|热议
- 科新机电:感谢对公司的关注;公司作为过程装备制造企业,先后参与了国内知名工程公司的有关灰氢项目建设_焦点报道
- 全球观热点:“国考”,县医院的“内卷时代”
- 天天热推荐:哈姆:季后赛不是NCAA 输一局不会被淘汰没什么大不了
- 任泽区气象台发布大风蓝色预警【Ⅳ级/一般】
- 坐月子不能吃哪些调料_坐月子不能吃哪些食物呢_每日消息
- 华福证券:给予湖南黄金买入评级,目标价位20.0元
- 当前资讯!小米智能空气炸锅Pro4L刚刚到货
- 亲子对话:从操控指责到信息引导
- 大运新能源来了,全层级爆品惊艳上海车展
- 盒马据悉正筹备上市事宜 准备明年在香港上市 世界看点
- 环球热资讯!李岩正式履职方正证券董事会秘书
X 关闭
最新资讯
- “中国鸽子树”珙桐迎来开花季 当地破解野生植物保护多个技术难题
- 【当前独家】智能语音下载(智能语音下载APP)
- 平安夜挂袜子还是圣诞节挂袜子?平安夜晚上在床头挂袜子还是圣诞节
- 淄博要将方舱改旅舍接待游客?是什么情况 淄博要将方舱改旅舍接待游客?具体来龙去脉是怎么样_环球短讯
- 浩云科技: 关于2022年度利润分配预案的公告|天天热推荐
- 每日看点!终极斗罗:最新出现的事物,可能是神界构思,蓝轩宇走在最前面
- 全球通讯!山东:三预警齐发!还有断崖式降温
- 一季度新疆7个地州市外贸进出口倍增
- 前沿资讯!东威科技:光伏镀铜设备第二代设备已经客户验收确认收入,第三代设备处于制造中,也与客户签订了协议,预计于今年7月底左右交货
- 记者勇士对追梦被禁赛极其惊讶 勇士对格林禁赛感到特别吃惊本以为最多追加罚款
- 东屿岛的零碳探索(美丽中国)
- 2023年4月19日,一起早读云南!
- 长峰医院发生火情的东楼主要收治危重症患者 东楼建筑面积5298平方米
- 通用汽车一季度在华销量为46万辆,同比下滑24.6%
- 2023年山西二级建造师准考证打印入口
- 「南方500」雨润重整方案获通过 创始人直言感恩 股价一度涨超30%
- 世界实时:巴菲特带飞市场情绪 日股创一年多来最长连涨
- 宁德时代(300750.SZ)再发新品!凝聚态电池实现高比能与高安全兼得 年内具备量产能力|全球关注
- 金地22.14亿元竞得东莞松山湖商住地 溢价率15%
- 双面创作 纸织成画(工匠绝活)
- ST曙光4月19日快速回调-环球快看点
- 世界微头条丨义乌**兴安盟物流专线专线**-义乌到克孜勒货运公司货到付款
- 环境工程系_关于环境工程系介绍
- 【世界新视野】北京长峰医院发生火灾,北京市:当务之急是全力以赴救治伤员
- 4月19日生意社片碱基准价为3500.00元/吨
- 国家发展改革委:后期铁矿石市场供需趋于宽松 价格可能总体趋于回落_当前焦点
- 焦点速看:分析人士:LNG库存目前维持高位
- 全球速讯:实用!收好这份五一假期防坑指南
- 2023年横琴新区龙头股有哪些?(2023/4/19)
- *ST腾信4月19日盘中涨幅达5% 热头条
- transboundary音标_transboundary
- 接连发生手机被盗案,集市“黑手”落网!
- 2022网络硬盘十大品牌排行榜_网络硬盘哪个牌子好
- 栀子花放在哪里合适(栀子花应该放在什么地方) 当前消息
- 天道文化属性的四个层次_文化属性的四个层次 环球即时看
- 沪锡昨日盘中触及23万元/吨关口 国泰君安等17席位逆向减持多头头寸
- 焦点!中老铁路国际旅客列车为跨境游注入新活力
- 4月18日基金净值:华安聚优精选混合最新净值0.7873,跌0.3%
- 百货零售上市公司去年业绩两极分化,“数字化转型”成行业破局共识 世界快播报
- 环球今日报丨福建福鼎:非遗提线木偶泡“非遗”茶
- 现代起亚集团_现代起亚_全球短讯
- 射阳法院情法融合圆满执结腾房纠纷
- 每日快报!关于中秋的诗句古诗_描写中秋的诗句古诗
- 市群艺馆20日起免费发放演出票|全球快资讯
- 热点评!friendly比较级是more friendly吗_friendly比较级
- 环球观天下!天空体育:巴萨正在加紧努力给梅西开出一份合同
- 交警:骑电动自行车不要驾照!但要注意5点,避免被罚款,明确了 视讯
- 【世界聚看点】江苏3月工业用电量同比增长7.5% 电力大数据透视工业平稳复苏
- 今日播报!《Collar×Malice -deep cover-》剧场版公布!五月开播!
- 新动态:幼儿园入园需要准备哪些东西_幼儿园入园准备材料
X 关闭