首页 > 其他分享 >Qt - 主界面卡死的解决方案

Qt - 主界面卡死的解决方案

时间:2023-10-25 11:36:14浏览次数:40  
标签:界面 Qt void TimerThread QObject 线程 connect 卡死 QThread

简介

我们在写UI文件的时候,有很多情况下,是需要界面来处理业务中某些耗时的操作,这时候如果不处理好界面相关的逻辑的话,主界面就会卡死,这时候就需要我们上多线程了

首先上业务上一个很简单的栗子

比如我们的代码中有这么一个耗时的操作

复制代码
1     // 第一种耗时的操作
2     auto fWhile1 = [] ()
3     {
4         for (int i = 0; i < 1000000; i++)
5         {
6             qDebug()<<i<<endl;
7         }
8     };
复制代码

把这个代码绑定到一个按钮事件上

connect(ui->pushButton1, &QPushButton::clicked, fWhile1);

然后点击。发现界面卡死了,很正常,必须得等到这段代码耗时完成之后才能继续操作界面,这段代码是太不友好了,不清真,所以我们要改一下。


方法1-QCoreApplication::processEvents

如何改动,可以看下这个函数

QCoreApplication::processEvents

来一起看下官网介绍

Processes all pending events for the calling thread according to the specified flags until there are no more events to process. You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file). In the event that you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop. Calling this function processes events only for the calling thread. Note: This function is thread-safe.
  • You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).
  • 当程序忙于执行长时间操作(例如复制文件)时,您可以偶尔调用此功能。

我们就暂时就这个(滑稽。 接下来可以把代码搞成这种了。

复制代码
1     auto fWhile2 = [] ()
2     {
3         for (int i = 0; i < 1000000; i++)
4         {
5             qDebug()<<i<<endl;
6             QApplication::processEvents();
7         }
8     };
9     connect(ui->pushButton2, &QPushButton::clicked, fWhile2);
复制代码

这种代码在配置不好的机器上实际上还是有点小问题,比如我的小破本子。还是会有点卡的。我觉得用户一般是可以接受这种情况的。


方法2-QtConcurrent::run

实际上这个逻辑还有一个问题,就是如果我的业务代码不是循环该怎么办呢,这时候我们可以用新的类接口

QtConcurrent::run

这个类。这个类是可以将一个函数放到新的线程里来执行。再加上

QFuture<T>

这个类,可以控制这个新的线程函数开始,控制,结束。 具体可以查看官方文档,我这里就上个简单的栗子

复制代码
 1 //耗时的操作
 2 static bool function_needmoretime()
 3 {
 4     for (int i = 0; i < 1000000; i++)
 5     {
 6         qDebug()<<i<<endl;
 7     }
 8     return true;
 9 }
10 
11     // three
12     auto fWhile3 = [] () -> void
13     {
14         QFuture<bool> future = QtConcurrent::run(function_needmoretime);
15         while(!future.isFinished())
16         {
17             QApplication::processEvents(QEventLoop::AllEvents, 100);
18         }
19     };
20     connect(ui->pushButton3, &QPushButton::clicked, fWhile3);
复制代码

QFuture + QtConcurrent这个框架非常强大,可以将线程同步异步状态抽象出来,让程序员不用太关心这些。这只是一个最简单的栗子。我的小破本子来运行这个是一点都不卡的。界面依旧如丝滑般流畅。


方法3-线程

线程基础那种废话我就不多说了。道理大家都懂,我直接上wiki。

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。 线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。 在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。

线程的创建有两种方式,第一种是继承QThread的方式,然后重写run,但是这种方式官方已经不推荐了。官方不推荐的我们就不要这样写了,我们这里讨论的是第二种方式。

继承QObject ,move到新的线程中。

重写 QObject

复制代码
 1 // 头文件
 2 class workThread : public QObject
 3 {
 4     Q_OBJECT
 5 public:
 6     workThread(QObject* parent = nullptr);
 7     ~workThread();
 8 public slots:
 9     void start1();
10     void doWork();
11 signals:
12     void workFinished();
13     void workStart();
14 };
15 
16 //cpp
17 workThread::workThread(QObject* parent) : QObject (parent)
18 {
19 }
20 workThread::~workThread()
21 {
22 }
23 void workThread::start1()
24 {
25     emit workStart();
26     doWork();
27 }
28 void workThread::doWork()
29 {
30     for (int i = 0; i < 1000000; i++)
31     {
32         qDebug()<<i<<endl;
33     }
34     emit workFinished();
35 }
36 使用方法
37 
38 QThread* m_workerThread = new QThread();
39     workThread* worker = new workThread();
40     worker->moveToThread(m_workerThread);
41 
42     connect(m_workerThread, &QThread::started, worker, &workThread::start1);
43     connect(worker, &WorkThread::workFinished, worker, &WorkThread::deleteLater);
44     connect(worker, &workThread::workFinished, m_workerThread, &QThread::quit);
45     connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
46 
47     //也可以退出释放资源
48 //    connect(qApp, &QApplication::aboutToQuit, worker, &QObject::deleteLater);
49 //    connect(worker, &QObject::destroyed, m_workerThread, &QThread::quit);
50 //    connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
复制代码

总结下这样的操作界面是一点都不卡的,因为延迟的操作我们放到新的线程中了。 如果需要传递数据的话,可以将数据通过信号槽的方式传递。

  • 之所以官方不推荐重写QThread也是因为无法使用信号槽
  • 想继承QThread的话也可以,这个继承QThread的类也需要moveToThread,这种做法不清真,所以不希望大家用。

方法4-线程 + 定时器

实际上,就是逻辑4的进阶版本,再加个定时器,每隔两秒输出当前时间

复制代码
 1 class TimerThread : public QObject
 2 {
 3     Q_OBJECT
 4 public:
 5     TimerThread(QObject* parent = nullptr);
 6     ~TimerThread();
 7 public:
 8     void run();
 9     void doWork();
10 signals:
11     void workStart();
12     void workFinished();
13 };
14 
15 static int timerCount = 0;
16 TimerThread::TimerThread(QObject* parent): QObject (parent)
17 {
18 }
19 TimerThread::~TimerThread()
20 {
21 }
22 void TimerThread::run()
23 {
24     emit workStart();
25     QTimer *timer = new QTimer(this);
26     connect(timer, &QTimer::timeout, this, &TimerThread::doWork);
27     timer->start(2000);
28 }
29 void TimerThread::doWork()
30 {
31     timerCount ++;
32     if (timerCount > 100)
33         emit workFinished();
34     qDebug()<<QTime::currentTime()<<endl;
35 }
36 业务代码在这里
37 
38 auto fTimerThreadStart = [=] () -> void
39     {
40         fiveThread->start();
41     };
42     connect(ui->threadButton2, &QPushButton::clicked, fTimerThreadStart);
43     connect(fiveThread, &QThread::started, timerObject, &TimerThread::run);
44     connect(timerObject, &TimerThread::workFinished, fiveThread, &QThread::quit);
45     connect(fiveThread, &QThread::finished, fiveThread, &QThread::deleteLater);
复制代码

界面也是灰常丝滑般流畅的。具体的业务逻辑需求可以再想。

----------------------------------------------------------------------

原文转自:https://www.cnblogs.com/ybqjymy/p/13446589.html

标签:界面,Qt,void,TimerThread,QObject,线程,connect,卡死,QThread
From: https://www.cnblogs.com/zhuchunlin/p/17786727.html

相关文章

  • 界面控件开发包DevExpress v23.1.6全新发布|附高速下载
    DevExpressUniversal拥有.NET开发需要的所有平台控件,包含600多个UI控件、报表平台、DevExpressDashboardeXpressApp框架、适用于VisualStudio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress今年第一个重要版本v23.1正式发布,该版本拥有众多新产品和数十个具......
  • 【CentOS7】启动 CentOS7 系统时卡在开机界面 7 解决办法
    【CentOS7】启动CentOS7系统时卡在开机界面7解决办法本文参考:https://www.51c51.com/danpianji/xinxi/89/73120.html目录一、问题如题二、解决步骤1.进入grub界面2.修改BIOSLegacy或UEFIBIOS模式启动的3.挂载系统根&RW读写权限4.修改/etc/selinux/config配置......
  • Qt CustomDashLine会对范围外Path自动裁剪问题
    在使用QPainter进行绘制时发现问题。当直接使用QPen进行绘制自定义虚线时会出现一个问题:当绘制的Path遇到界面进行裁剪时,此时虚线线型将会省略裁剪的那一部分,导致自定义虚线在移动以及放大时会自动修改位置。解决办法:直接使用QPainterPathSkroke。问题描述......
  • 安防视频监控平台EasyCVR新版(3.4)平台界面更新2.0
    视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,在视频监控播放上,TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放,可同时播放多路视频流,也能支持视频定时轮播。视频监控汇聚平台EasyCVR支持多种播放......
  • js实现在报表参数界面获取body中控件的值
     要在报表参数界面获取body中控件的值,你可以使用JavaScript来实现。下面是一个详细的介绍:1.DOM(文档对象模型):  -DOM是用于操作HTML文档的API,它允许你通过JavaScript访问和操作文档中的元素。  -在报表参数界面,你可以使用DOM来获取页面上的控件元素。2.获取控件元......
  • Qt - QDateTime类的使用
    介绍QDateTime类是Qt框架中用于处理日期和时间的类,在Qt中拥有广泛的应用。它能够精确地表示某个事件或时间点,并且支持对日期和时间进行各种操作和转换,比如计算两个时间之间的差值、设置时区、格式化输出等。使用QDateTime类,我们能够轻松地完成各种日期和时间的转换和处理,从而方......
  • m基于深度学习网络的智能垃圾分类系统matlab仿真,带GUI界面
    1.算法仿真效果matlab2022a仿真结果如下:    2.算法涉及理论知识概要       垃圾数量的急剧增加和垃圾中物质的复杂多样性带来了严重的环境污染和资源浪费问题。回收可以减少废物,但手工管道垃圾分拣工作环境恶劣,劳动强度大,分拣效率低。智能垃圾分类系统是......
  • m基于深度学习网络的智能垃圾分类系统matlab仿真,带GUI界面
    1.算法仿真效果matlab2022a仿真结果如下:2.算法涉及理论知识概要垃圾数量的急剧增加和垃圾中物质的复杂多样性带来了严重的环境污染和资源浪费问题。回收可以减少废物,但手工管道垃圾分拣工作环境恶劣,劳动强度大,分拣效率低。智能垃圾分类系统是基于深度学习网络的一种应用,它可以通......
  • 算法笔记(2)FHQtreap
    原发布于我的个人博客前言FHQtreap绝对是平衡树里最好写,最实用的,他几乎能做所有splay或其它平衡树能做的事,还能可持久化!这篇文章将会介绍FHQtreap的基本操作和维护区间的操作,并附上例题。基本操作FHQtreap的基本操作只有两个,分裂和合并。有些读者可能会问,分裂和合并和平衡树......
  • Qt - Label标签显示特殊字符
    1.创建一个带Ui界面的测试工程,把特殊字符拷贝到label标签里,点击保存。 2.打开qt安装目录,找到designer.exe文件双击打开 3.把刚刚创建的ui界面拖进来 4.点击窗口-》ViewC++Code...5.会弹出一个窗口里面有ui文件的源码,还有一串特殊数字,这串特殊数字就是那个特......