这篇文章的主要内容是:点击一个widget页面时,当加载的数据量过大时,会造成假死状态。此时使用gif动态加载图表示当前加载的状态。
目前我使用的开发环境是:VS2017+QT5.14.2
肯定有人觉得怎么用这么个怪异的开发环境,使用QT语言为啥不直接使用QtCreator呢?
我只能说工作需要,哈哈。接下来我们主要讲解我需要实现的功能
实现的功能是:点击父窗口的按钮,进入子窗口Dlg1中,并在子窗口Dlg1中加载数据,在加载数据的同时显示gif等待图片
这个功能看着很简单,其实存在了以下几个难点:
1:点击父窗口的按钮,呼出子窗口Dlg1时,如何保证是在子窗口Dlg1显示之后再动态加载数据?
2:加载数据时,如何保证一遍加载数据一遍显示动态gif加载图?
接下来,我对这个功能进行代码讲解。
在父窗口中创建需要显示的子窗口。假设父窗口是 QtParentDlg、子窗口是QtChildDlg。这里只是对实现方法介绍,保证可行,具体的业务实现每个人的功能不同,不做详细解释。
1:在父窗口的点击按钮中触发子窗口QtChildDlg的显示
void QtParentDlg::onBnClickedShowChildDlg
{
if(m_pChildDlg)
{
/*
可以实现具体的业务处理,对子窗口进行数据传递
*/
m_pChildDlg->show(); //显示子窗口
}
}
2:在子窗口中,重写show函数,在这里进行处理
因为是要先显示子窗口页面,再进行数据加载。重写show函数方法
virtual void show();
void QtChildDlg::show()
{
QWidget::show(); //显示当前窗口,必须写
}
上面的代码是对子窗口的show函数的重写。
有人就觉得可以直接在QWidget::show()后面直接写需要加载的数据。
大家可以尝试一下,当加载小数据时,是可以做到不卡顿,当加载一个大数据时,子窗口会等当前函数处理完之后才会进行页面显示。
既然如此,是不是可以用开线程的方法呢?答案是当前可以,但是开线程有点麻烦,此时我选用的是使用定时器。
3:子窗口中,定时器使用
需要的头文件是:
#include <QTimer> //头文件添加
创建定时器,并设置响应槽函数
QTimer* m_TimerPtr; //定时器
//创建定时器
m_TimerPtr = new QTimer(this);
//槽函数
connect(m_TimerPtr, &QTimer::timeout, this,&QtChildDlg::OnTimerEvent);
在OnTimerEvent中处理大量数据。
void QtChildDlg::OnTimerEvent
{
/*
在这里加载需要处理的数据
*/
//在处理数据之后,一定要关闭定时器,否则会一直处理!!!
m_TimerPtr->stop();
}
定时器中的处理操作实现了,那么需要在哪里开启定时器呢?
答案是,要在重写的show函数中,开启定时器,并且是一显示页面就需要加载数据。
实现方法如下:
void QtChildDlg::show()
{
//必须要写的
QWidget::show();
//开启定时器
if (m_TimerPtr->isActive() == false)
{
m_TimerPtr->start(0);
}
}
此时需要注意的是:在使用定时器时,我们需要判断定时器是否处于激活状态,再开启定时器,为了安全起见。
start(0)表示:一显示页面就需要进行数据加载。
大家看到了这里,就可以使用代码尝试一下,这样的方法是否可行?
尝试以后,会发现这样也会导致页面假死,那么我们该怎么解决这个假死的问题呢?
4:解决页面死锁问题
可以使用等待页面,一遍加载数据一遍显示加载动画。
一般的处理方法是开一个线程,在显示加载动画页面时,同时加载数据。一般这是C++程序员的惯性思维,开线程的方式可以做到同步。
但是,今天不使用开线程的方式。今天使用一个新的方法,也是我最近常用的方法,可以替代开线程,方便快捷。
这是今天的重中之重,大家可以收藏下来,以后不想用线程处理的时候,可以使用这种方法。
直接使用代码显示更加直观
//用到的头文件
#include <QtConcurrent/QtConcurrentRun>
//函数中的具体实现
QFuture<bool> futureResult = QtConcurrent::run(this, &QtChildDlg::ThreadLoadData);
while (!futureResult.isFinished())
{
QApplication::processEvents(QEventLoop::AllEvents);
}
使用这种方法代替了开线程。如果有人想了解这个功能是做什么的,可以具体去查,在这里只是实现功能,不做API的详细介绍。
我们需要将加载的大数据放到ThreadLoadData函数中,需要注意的是该函数必须有返回值,且为true!
具体的函数实现:
bool QtChildDlg::ThreadLoadData()
{
/*
做实际的数据加载处理,纯数据,不能加载窗口等与页面有关的内容!!
*/
return true;
}
有的人就想我需要传参数的怎么办?
具体实现:
//假设该函数有两个参数
bool QtChildDlg::ThreadLoadData(int n, bool b)
{
/*实际的数据处理*/
return true;
}
在调用该函数的地方
QFuture<bool> futureResult = QtConcurrent::run(this, &QtChildDlg::ThreadLoadData,int(1), bool(true));
实现的函数中有多少参数,只需要在后面追加就行。
5:整体实现
下面,我贴出整体额实现代码
void QtChildDlg::show()
{
QWidget::show();
//在开启定时器之前,加载动画页面,此处不做详细解释
m_pActionDlg->show();
//开启定时器
if (m_TimerPtr->isActive() == false)
{
m_TimerPtr->start(0);
}
}
void QtChildDlg::OnTimerEvent()
{
//模拟开线程的方式加载数据
QFuture<bool> futureResult = QtConcurrent::run(this, &QtChildDlg::ThreadLoadData);
while (!futureResult.isFinished())
{
QApplication::processEvents(QEventLoop::AllEvents);
}
//加载完数据之后,隐藏动画页面
m_pActionDlg->hide();
//处理完数据之后,停止定时器!!
m_TimerPtr->stop();
}
注意的是,在QtChildDlg中,析构的时候记得要删除定时器
QtChildDlg::~QtChildDlg()
{
if(m_TimerPtr)
{
//判断定时器是否在工作?
if (m_TimerPtr->isActive() == true)
{
m_TimerPtr->stop(); //关闭定时器
}
delete m_TimerPtr;
m_TimerPtr= nullptr;
}
}
贴出原地址:Qt中使用gif动图加载大量数据