高效Qt开发

您所在的位置:网站首页 qt创建多个循环线程 高效Qt开发

高效Qt开发

2024-04-18 00:12| 来源: 网络整理| 查看: 265

一般来说,Qt的界面类必须在GUI线程创建,也只能在GUI线程操作,这意味着我们的界面的创建和设置界面数据的方法必须在主线程被执行。而一旦遇上业务场景需要创建或者刷新大量界面数据时,当这些操作集中在主线程中执行时,往往界面会出现卡住的情况。

Qt界面的刷新有许多方法可以来防止界面卡顿卡住,以下我将以“在QLabel上设置QPixmap”的用例来说明。

*runTimer用于提供粗糙的计时功能,在start()方法中记录当前开始时的时间戳,在end()方法中使用最新的时间戳减去开始时的时间戳,取得时间间隔。

1.QLabel加载显示本地图片文件。

首先我们直接从本地加载一张规格为8504*4762的jpg图片,并将其缩放到和QLabel的一样的尺寸(使用保持比例缩放,并选择平滑缩放),调用QLabel::setPixmap()来设置渲染这个图片。

用例一 代码用例一 调式信息

结合用例一代码,分析以上的调试信息得出:

1)QPixmap打开本地图片,并加载到内存中花费了1250毫秒;

2)QPixmap的平滑缩放,消耗了768毫秒;

以上两个操作合计用时2018毫秒,意味着如果用这个方法加载一张这样大的图片并缩放显示,界面线程将会阻塞2秒,如果此时有界面操作,则看起来的效果是卡顿了2秒。

(使用QImageReader的方式来加载指定规格的图片,可以做到时间花销更小,但这个属于高效Qt开发-快速加载本地图片并缩放显示的技巧一文的内容)

所以我们可以将QPixmap的构造以及缩放封装到一个方法中,并调用此方法时处于异步刷新的环节,做到界面尽可能的不卡顿。

static QPixmap getPixmapFromLocalPicture(const QString & picturePath,const QSize & size) { auto pixmap = QPixmap(picturePath); pixmap = pixmap.scaled(size, Qt::AspectRatioMode::KeepAspectRatio,Qt::SmoothTransformation); return pixmap; } 2.直接加载显示本地图片用例二 代码

这与用例一是相同的,只不过在QLabel上加多了一个loading的动画效果,来展示界面的卡顿。

我们运行此代码,发现点击“加载图片”按钮后,代码在执行到getPixmapFromLocalPicture()这个方法后,界面卡顿了大约2秒。

用例二 运行效果3.运用QtConcurrent::run()加载显示本地图片

*使用QtConcurrent::run()需要在pro文件中添加“concurrent”模块,并引入头文件

用例三 代码

QtConcurrent::run()的使用方法有很多种,这里恰好是具体的操作内容是一个静态函数,我们可以使用函数指针的方式来调用这个静态函数,同时为了代码的阅读性,使用QFutureWatcher来观察并回调到主界面线程执行界面操作。

其代码效果如下:

用例三 运行效果

从运行效果可以发现,在点击了“加载图片”按钮之后,界面的loading动画仍然保持继续工作,并不会出现界面卡顿的情况,直到左边的调试信息显示加载完成时,图片被显示出来。

*但是用例三的代码中存在一定的问题,如果在点击“加载图片”按钮后,马上关闭窗口,此时界面程序已经退出,但是子线程中中还阻塞在文件IO阶段,并且创建的QPixmap由于失去了界面设备,无法正常的构造,此次会出现一个crash。如图:

用例三 存在的crash

(这个情况经常在使用QtConcurrentRun的场景中出现,但在这个加载QPixmap的场景下,真正触发的原因我还不确定。)

4.运用 QEventLoop+QtConcurrent::run()加载显示本地图片

QEventLoop可以使得函数被阻塞但界面仍然能够响应事件队列中的事件。

用例四 代码

用例四中,使用QEventLoop来关联线程执行状态观察者的完成信号,用完成信号来触发事件循环的退出。本质上整个函数的工作内容和顺序与用例三相差无几,但是在代码流程上实现了异步转同步的效果,函数仍然类似于顺序执行状态,阅读性更佳。

用例四 运行效果

其运行效果也与用例三相差不大,界面仍然比较丝滑的执行。

*用例四在重试用例三的crash场景操作时,却不会出现crash,我猜测是进入了事件循环而起到了保护,事件循环的退出必然等待子线程的退出才会退出,而主事件循环的退出又需要等待子事件循环的退出blablabla.....之类的吧

5.小结

在以往的业务场景中,如果涉及文件IO、图片缩放等操作的界面,一般都是要使用子线程执行耗时操作后回调到主界面上,以此避免界面加载时的长时间卡顿,而QEventLoop+QtConcurrentRun的组合实现的异步转同步的效果,也是比较有意思的用法。但是这个组合并非绝对安全,在没有做好指针保护的场景下,释放关闭程序控制权给用户,往往有时候会触发一些问题,这个只能在不断debug过程中去完善了。

但这个技巧有效的前提是在控件数量较小的范围之下,如果界面控件量级上千,可能还需要一些界面内存管理的手段来配合使用,否则在性能一般的设备之上,可能还无法做到很好的丝滑效果。

*用例中使用的图片素材出自:void_0 - 千钧一发之缘



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3