Qt使用QListWidget结合QScrollBar实现图像动态无限加载

任何事物,量变都会引起质变,在Qt界面开发中无不体现着这个道理,特别是在界面上加载数据量巨大的图片的时候,会显得非常的力不从心,比如:我想加载100000张图片到列表中,每一个列表项是一个自定义控件,自定义控件中包含了图像缩略图,图片名称等信息.我做过一个实验,按照普通的加载方式,一次性加载5000个自定义列表项,界面会卡住大概10秒左右,这样的体验相当差.
为此,我们需要动态的方式来加载列表项,由于界面空间有限,所以我们只需要显示部分的列表项即可,当用户需要显示更多的列表项的时候,可以通过鼠标的滚轮滚动显示,或者直接拖动右侧的滚动条实现快速定位显示.
为了实现动态无限加载的功能,我需要做两个工作,一个是使用QListWidgetQScrollBar结合,可能你会说,QListWidget不是内置了QScrollBar吗?你这样做不是多此一举吗?如果你真觉得是多此一举,那么你可以试一试那样的方式,至少我是试过的,行不通,所以我选择通过这样的方式来实现.
另外一个是通过鼠标的滚轮滚动来实现慢速的加载列表项,通常这个功能的目的是为了让用户在快速定位到感兴趣区域后,慢下来仔细查看前后列表项.
这里,我还做了一个小的改动,由于鼠标的滚轮往外滚(或者叫做往上滚)是增,往内滚(或者叫做往下滚)是减,显然这个在列表控件中是违反直觉的,通常我们希望鼠标滚轮往下滚,就显示更多的列表项,往上滚就是显示之前的列表项,为此,我在前面加了一个-负号,具体的在源码注释中有体现.
其实,这个实现的思路不仅仅只能用在QListWidget中,在QTableWidget上需要加载大量的数据的时候也可以使用这样的思路,道理是相通的.
正所谓:源码面前,了无秘密.
接下来,我就直接贴出我的源码,大家一看便知:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QString>
#include <QVector>
#include <QWheelEvent>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void wheelEvent(QWheelEvent *event);
private slots:
    void on_pushButton_clicked();
    void showImageList();

private:
    Ui::Widget *ui;
    QVector<QString> m_VectorOfImageFullName;
    int m_ImageSum;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QFileInfo>
#include <QFileInfoList>
#include <QFileDialog>
#include <QDir>
#include <iostream>
#include <QStringList>
#include <QListWidgetItem>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    connect(ui->verticalScrollBar, SIGNAL(sliderReleased()), this, SLOT(showImageList()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::wheelEvent(QWheelEvent *event)
{
    int numDegrees = event->angleDelta().y() / 8; //获取滚轮的垂直方向的值
    int numSteps = - numDegrees / 15; //这里取负号,目的是为了实现鼠标往下滚动时是数值增大,往上滚动时是数值减小
    std::cout << "numSteps: " << numSteps << std::endl;
    if(event->orientation() == Qt::Vertical) {
        int index = ui->verticalScrollBar->value() + numSteps;
        std::cout << "current index: " << index << std::endl;
        if(index < m_ImageSum) {
            ui->verticalScrollBar->setValue(index);
            ui->listWidget->clear();
            if(index < 20) {
                for(int i = 0; i < index; ++i) {
                    QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
                    ui->listWidget->addItem(item);
                }
            }
            else {
                for(int i = index - 19; i < index + 1; ++i) {
                    QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
                    ui->listWidget->addItem(item);
                }
            }
        }
    }
    event->accept(); //表示此处已经处理了鼠标的滚动事件,不需要传给父部件处理
}

void Widget::on_pushButton_clicked()
{
    QString dirFullName = QFileDialog::getExistingDirectory(this,
                                                            tr("选择图片所在目录"),
                                                            "/home",
                                                            QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    QDir dir(dirFullName);
    if(!dir.exists()) {
        std::cerr << "dir does not exists!" << std::endl;
        return;
    }
    dir.setFilter(QDir::Files | QDir::NoSymLinks);
    QStringList filters;
    filters << tr("*.png") << tr("*.jpg");
    dir.setNameFilters(filters);
    QFileInfoList infoListOfImages = dir.entryInfoList();
    ui->verticalScrollBar->setRange(0, infoListOfImages.size() - 1);
    ui->verticalScrollBar->setPageStep(10);
    ui->verticalScrollBar->setSingleStep(1);
    for(auto &el : infoListOfImages) {
        m_VectorOfImageFullName.push_back(el.absoluteFilePath());
        std::cout << "image absolute file path: " << el.absoluteFilePath().toStdString() <<std::endl;
    }
    m_ImageSum = ui->verticalScrollBar->maximum() + 1;
    if(m_ImageSum < 20) {
        ui->verticalScrollBar->setValue(m_ImageSum - 1);
        for(int i = 0; i < m_ImageSum; ++i) {
            QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
            ui->listWidget->addItem(item);
        }
    }
    else {
        for(int i = 0; i < 20; ++i) {
            ui->verticalScrollBar->setValue(19);
            QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
            ui->listWidget->addItem(item);
        }
    }
}

void Widget::showImageList()
{
    int index = ui->verticalScrollBar->value();
    if(index >= 20) {
        ui->listWidget->clear();
        for(int i = index - 19; i < index + 1; ++i) {
            QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
            ui->listWidget->addItem(item);
        }
    }
    else {
        ui->listWidget->clear();
        for(int i = 0; i < 20; ++i) {
            QListWidgetItem *item = new QListWidgetItem(m_VectorOfImageFullName.at(i), ui->listWidget);
            ui->listWidget->addItem(item);
        }
    }
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

运行结果:
在这里插入图片描述
运行流畅,轻松显示948张图片列表,当然,这里我只是显示了简单的字符串,并没有显示图片,但是原理上走通了,加载图片也不会有问题.

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页