運行效果
主要代碼
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
namespace Ui {
class MainWidget;
}
class MainWidget : public QWidget
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
~MainWidget();
private slots:
private:
Ui::MainWidget *ui;
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include "imagelistviewmodel.h"
#include "imagelistviewitemdelegate.h"
#include "imagemanager.h"
#include <QDir>
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWidget)
{
ui->setupUi(this);
ImageListViewModel *listViewModel=new ImageListViewModel(this);
ui->listImage->setModel(listViewModel);
ImageManager *imageManager=new ImageManager(this);
connect(imageManager, SIGNAL(imageLoaded(int,QString)), listViewModel, SLOT(imageLoaded(int,QString)));
connect(imageManager, SIGNAL(imageDownloadProgressChanged(int,QString)), listViewModel, SLOT(imageDownloadProgressChanged(int,QString)));
ImageListViewItemDelegate *delegate=new ImageListViewItemDelegate(imageManager, this);
ui->listImage->setItemDelegate(delegate);
ui->listImage->setViewMode(QListView::IconMode);
ui->listImage->setResizeMode(QListView::Adjust);
ui->listImage->setSelectionMode(QListView::SingleSelection);
ui->listImage->setSpacing(10);
ui->listImage->setWordWrap(true);
ui->listImage->setUpdatesEnabled(true);
}
MainWidget::~MainWidget()
{
delete ui;
}
imagemanager.h
#ifndef IMAGEMANAGER_H
#define IMAGEMANAGER_H
#include <QObject>
#include <QMap>
#include <QCache>
#include <QSet>
#include <QQueue>
#include <QPair>
class ImageManager : public QObject
{
Q_OBJECT
public:
typedef struct{
QImage *image;
int row;
QString imageName;
}ImageResult;
explicit ImageManager(QObject *parent = 0);
QImage *getImage(int row, const QString &imageName);
int getImageDownloadProgress(const QString &imageName);
private:
QCache<QString, QImage> m_imageCache;
void asyncGetImage(int row, const QString &imageName);
QMap<QString, int> m_imageDownloadProgressMap;
QQueue<QPair<int, QString>*> m_asyncGetImageQueue;
void pushAsyncGetImageTask(int row, const QString &imageName);
void pullAsyncGetImageTask();
signals:
void imageLoaded(int row, const QString &imageName);
void imageDownloadProgressChanged(int row, const QString &imageName);
public slots:
void doAsyncGetImageFinished();
void doDownloadImageProgress(int row, const QString &imageName, int percent);
};
#endif // IMAGEMANAGER_H
imagemanager.cpp
#include "imagemanager.h"
#include <QFutureWatcher>
#include <QFuture>
#include <QFile>
#include <QApplication>
#include <QDir>
#include <QImage>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QDebug>
#include <QtConcurrent>
#include "downloadimagewrapper.h"
/**************異步加載圖片到緩存的步驟**********************************************************
* (1)先到文件系統中查找是否有緩存的圖片,如果存在則讀取該圖片,創建並返回一個QImage對象
* (2)如果文件系統中沒有緩存的圖片,則下載雲端的圖片,下載成功後保存到文件系統中,創建並返回一個QImage對象
* (3)如果文件系統和雲端都沒有找到該圖片,則返回空指針
****************************************************************************************/
ImageManager::ImageResult* asyncGetImageFromFileSystemAndNetwork(int row, const QString &imageName, ImageManager *imageManager)
{
ImageManager::ImageResult *imageResult=new ImageManager::ImageResult;
imageResult->image=0;
imageResult->row=row;
imageResult->imageName=imageName;
QString fileSystemCachePath=QString("%1/%2").arg(QDir::tempPath()).arg(imageName);
QImage *image=new QImage(fileSystemCachePath);
if(!image->isNull())
{
imageResult->image=image;
qDebug()<<"Load from file system cache : "<<imageName;
return imageResult;
}
QEventLoop eventLoop;
DownloadImageWrapper wrapper(row, imageName);
QObject::connect(&wrapper, SIGNAL(finished()), &eventLoop, SLOT(quit()));
QObject::connect(&wrapper, SIGNAL(imageDownloadProgress(int,QString,int)), imageManager, SLOT(doDownloadImageProgress(int,QString,int)));
wrapper.startDownload();
eventLoop.exec();
if(!wrapper.hasError())
{
QImage *image=new QImage;
image->loadFromData(wrapper.imageData(), "jpg");
if(!image->isNull())
{
image->save(fileSystemCachePath);
imageResult->image=image;
qDebug()<<"Load from network : "<<imageName;
return imageResult;
}
delete image;
}
if(imageResult->image==0)
{
qDebug()<<"Load from file system and network failed : "<<imageName;
}
return imageResult;
}
ImageManager::ImageManager(QObject *parent) : QObject(parent)
{
m_imageCache.setMaxCost(10); //設置最大緩存對象數量爲10,便於測試
}
QImage *ImageManager::getImage(int row, const QString &imageName)
{
QImage *image=m_imageCache.object(imageName);
if(image==0)
{
if(!m_imageDownloadProgressMap.contains(imageName))
{
pushAsyncGetImageTask(row, imageName);
}
}
return image;
}
int ImageManager::getImageDownloadProgress(const QString &imageName)
{
return m_imageDownloadProgressMap.value(imageName, 0);
}
/***************************
* 異步讀取文件系統或雲端的圖片
**************************/
void ImageManager::asyncGetImage(int row, const QString &imageName)
{
m_imageDownloadProgressMap.insert(imageName, 0);
QFutureWatcher<ImageResult*> *watcher=new QFutureWatcher<ImageResult*>;
connect(watcher, SIGNAL(finished()), this, SLOT(doAsyncGetImageFinished()));
QFuture<ImageResult*> future=QtConcurrent::run(asyncGetImageFromFileSystemAndNetwork, row, imageName, this);
watcher->setFuture(future);
}
void ImageManager::pushAsyncGetImageTask(int row, const QString &imageName)
{
while(m_asyncGetImageQueue.size()>=QThreadPool::globalInstance()->maxThreadCount())
{
QPair<int, QString> *pair=m_asyncGetImageQueue.dequeue();
delete pair;
}
QPair<int, QString> *pair=new QPair<int, QString>;
pair->first=row;
pair->second=imageName;
m_asyncGetImageQueue.enqueue(pair);
pullAsyncGetImageTask();
}
void ImageManager::pullAsyncGetImageTask()
{
if(m_asyncGetImageQueue.isEmpty())
{
return;
}
if(m_imageDownloadProgressMap.size()>=QThreadPool::globalInstance()->maxThreadCount())
{
return;
}
QPair<int, QString> *pair=m_asyncGetImageQueue.dequeue();
asyncGetImage(pair->first, pair->second);
delete pair;
}
void ImageManager::doAsyncGetImageFinished()
{
QFutureWatcher<ImageResult*> *watcher=static_cast<QFutureWatcher<ImageResult*>*>(sender());
if(watcher!=NULL)
{
ImageResult *imageResult=watcher->result();
m_imageDownloadProgressMap.remove(imageResult->imageName);
if(imageResult->image!=0)
{
m_imageCache.insert(imageResult->imageName, imageResult->image);
emit imageLoaded(imageResult->row, imageResult->imageName);
}
watcher->deleteLater();
delete imageResult;
imageResult=0;
}
pullAsyncGetImageTask();
}
void ImageManager::doDownloadImageProgress(int row, const QString &imageName, int percent)
{
m_imageDownloadProgressMap.insert(imageName, percent);
emit imageDownloadProgressChanged(row, imageName);
}
imagelistviewmodel.h
#ifndef IMAGELISTVIEWMODEL_H
#define IMAGELISTVIEWMODEL_H
#include <QObject>
#include <QAbstractListModel>
#include <QList>
class ImageListViewModel : public QAbstractListModel
{
Q_OBJECT
public:
ImageListViewModel(QObject *parent=0);
~ImageListViewModel();
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QList<QString> m_dataList;
public slots:
void imageLoaded(int row, const QString &imageName);
void imageDownloadProgressChanged(int row, const QString &imageName);
};
#endif // IMAGELISTVIEWMODEL_H
imagelistview.cpp
#include "imagelistviewmodel.h"
#include <QSize>
#include <QDebug>
ImageListViewModel::ImageListViewModel(QObject *parent) : QAbstractListModel(parent)
{
for(int i=1; i<=28; i++)
{
m_dataList.push_back(QString("%1.jpg").arg(i));
}
}
ImageListViewModel::~ImageListViewModel()
{
}
int ImageListViewModel::rowCount(const QModelIndex &parent) const
{
return m_dataList.size();
}
int ImageListViewModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant ImageListViewModel::data(const QModelIndex &index, int role) const
{
switch (role) {
case Qt::SizeHintRole:
{
return QSize(600, 400);
break;
}
case Qt::UserRole:
{
return m_dataList.at(index.row());
break;
}
}
return QVariant();
}
void ImageListViewModel::imageLoaded(int row, const QString &imageName)
{
emit dataChanged(this->index(row), this->index(row)); //通知QListView重繪指定的row
}
void ImageListViewModel::imageDownloadProgressChanged(int row, const QString &imageName)
{
emit dataChanged(this->index(row), this->index(row)); //通知QListView重繪指定的row
}
imagelistviewitemdelegate.h
#ifndef IMAGELISTVIEWITEMDELEGATE_H
#define IMAGELISTVIEWITEMDELEGATE_H
#include <QObject>
#include <QStyledItemDelegate>
class ImageManager;
class ImageListViewItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ImageListViewItemDelegate(ImageManager *imageManager, QObject *parent = 0);
~ImageListViewItemDelegate();
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
ImageManager *m_imageManager;
signals:
public slots:
};
#endif // IMAGELISTVIEWITEMDELEGATE_H
imagelistviewitemdelegate.cpp
#include "imagelistviewitemdelegate.h"
#include <QPainter>
#include <QFontMetrics>
#include <QDebug>
#include "imagemanager.h"
ImageListViewItemDelegate::ImageListViewItemDelegate(ImageManager *imageManager, QObject *parent) : QStyledItemDelegate(parent)
{
this->m_imageManager=imageManager;
}
ImageListViewItemDelegate::~ImageListViewItemDelegate()
{
}
void ImageListViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
QImage *image=m_imageManager->getImage(index.row(), index.data(Qt::UserRole).toString());
if(image==0) //如果內存緩存中不存在要顯示的圖片,則繪製“加載中...”文字信息
{
QPen pen=painter->pen();
pen.setColor(QColor(200, 200, 200));
painter->setPen(pen);
QFont font=painter->font();
font.setPointSize(25);
painter->setFont(font);
QString txt=QString("圖片加載中···(%1%)").arg(m_imageManager->getImageDownloadProgress(index.data(Qt::UserRole).toString()));
QFontMetrics fm(font);
QRect rect;
rect.setWidth(fm.width(txt));
rect.setHeight(fm.capHeight());
painter->fillRect(option.rect, QColor(240, 240, 240));
painter->drawText(option.rect.left()+(option.rect.width()-rect.width())/2, option.rect.top()+(option.rect.height()-rect.height())/2+rect.height(), txt);
}
else //如果內存中存在要顯示的圖片,則繪製該圖片
{
painter->drawImage(option.rect, *image);
}
}
downloadimagewrapper.h
#ifndef DOWNLOADIMAGEWRAPPER_H
#define DOWNLOADIMAGEWRAPPER_H
#include <QObject>
#include <QTimer>
#include <QNetworkAccessManager>
class QNetworkReply;
class DownloadImageWrapper : public QObject
{
Q_OBJECT
public:
explicit DownloadImageWrapper(int row, const QString &imageName, QObject *parent = 0);
~DownloadImageWrapper();
void startDownload();
QByteArray imageData();
bool hasError();
private:
int m_row;
QString m_imageName;
QNetworkReply *m_reply;
QNetworkAccessManager m_manager;
QTimer m_timer;
signals:
void finished();
void imageDownloadProgress(int row, const QString &imageName, int percent);
public slots:
void doDownloadProgress(qint64, qint64);
};
#endif // DOWNLOADIMAGEWRAPPER_H
downloadimagewrapper.cpp
#include "downloadimagewrapper.h"
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QDebug>
DownloadImageWrapper::DownloadImageWrapper(int row, const QString &imageName, QObject *parent) : QObject(parent)
{
this->m_reply=0;
this->m_row=row;
this->m_imageName=imageName;
this->m_timer.setInterval(20*1000); //超時時間20秒
this->m_timer.setSingleShot(true);
}
DownloadImageWrapper::~DownloadImageWrapper()
{
if(this->m_reply!=0)
{
delete this->m_reply;
this->m_reply=0;
}
m_timer.stop();
}
void DownloadImageWrapper::startDownload()
{
QNetworkRequest request;
request.setUrl(QString("http://118.24.201.167:8080/test/image/%1").arg(m_imageName));
m_reply=m_manager.get(request);
connect(m_reply, SIGNAL(finished()), this, SIGNAL(finished()));
connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(doDownloadProgress(qint64, qint64)));
connect(&m_timer, SIGNAL(timeout()), this, SIGNAL(finished()));
m_timer.start();
}
QByteArray DownloadImageWrapper::imageData()
{
return m_reply->readAll();
}
bool DownloadImageWrapper::hasError()
{
return m_reply->error()!=QNetworkReply::NoError;
}
void DownloadImageWrapper::doDownloadProgress(qint64 received, qint64 total)
{
if(total==0)
{
return;
}
int percent=(int)(100*(received/(total*1.0)));
emit imageDownloadProgress(m_row, m_imageName, percent);
}
main.cpp
#include "mainwidget.h"
#include <QApplication>
#include <QThreadPool>
#include <QDir>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
/***** 設置線程池最大線程數爲4 ******/
QThreadPool::globalInstance()->setMaxThreadCount(4);
/***** 清除緩存文件,便於測試 ******/
QDir dir=QDir::temp();
dir.setFilter(QDir::Files);
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QFileInfo fileInfo = list.at(i);
QFile::remove(fileInfo.absoluteFilePath());
}
MainWidget w;
w.show();
return a.exec();
}
(-----------------完-------------------)