Qt中使用gif动图加载大量数据

这篇文章的主要内容是:点击一个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动图加载大量数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值