任何事物,量变都会引起质变,在Qt界面开发中无不体现着这个道理,特别是在界面上加载数据量巨大的图片的时候,会显得非常的力不从心,比如:我想加载100000张图片到列表中,每一个列表项是一个自定义控件,自定义控件中包含了图像缩略图,图片名称等信息.我做过一个实验,按照普通的加载方式,一次性加载5000个自定义列表项,界面会卡住大概10秒左右,这样的体验相当差.
为此,我们需要动态的方式来加载列表项,由于界面空间有限,所以我们只需要显示部分的列表项即可,当用户需要显示更多的列表项的时候,可以通过鼠标的滚轮滚动显示,或者直接拖动右侧的滚动条实现快速定位显示.
为了实现动态无限加载的功能,我需要做两个工作,一个是使用QListWidget
和QScrollBar
结合,可能你会说,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张图片列表,当然,这里我只是显示了简单的字符串,并没有显示图片,但是原理上走通了,加载图片也不会有问题.