Add a helper class to load icons asynchronously and set them on QStandardItems

This commit is contained in:
David Sansome 2012-03-06 21:24:41 +00:00
parent f2885c0319
commit 884080684a
7 changed files with 196 additions and 31 deletions

View File

@ -305,6 +305,7 @@ set(SOURCES
ui/screensaver.cpp
ui/settingsdialog.cpp
ui/settingspage.cpp
ui/standarditemiconloader.cpp
ui/systemtrayicon.cpp
ui/trackselectiondialog.cpp
ui/windows7thumbbar.cpp
@ -541,6 +542,7 @@ set(HEADERS
ui/qtsystemtrayicon.h
ui/settingsdialog.h
ui/settingspage.h
ui/standarditemiconloader.h
ui/systemtrayicon.h
ui/trackselectiondialog.h
ui/windows7thumbbar.h

View File

@ -18,8 +18,8 @@
#include "podcast.h"
#include "podcastdiscoverymodel.h"
#include "core/application.h"
#include "covers/albumcoverloader.h"
#include "ui/iconloader.h"
#include "ui/standarditemiconloader.h"
#include <QIcon>
#include <QSet>
@ -27,14 +27,11 @@
PodcastDiscoveryModel::PodcastDiscoveryModel(Application* app, QObject* parent)
: QStandardItemModel(parent),
app_(app),
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
is_tree_(false),
default_icon_(":providers/podcast32.png")
default_icon_(":providers/podcast16.png")
{
cover_options_.desired_height_ = 32;
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64,QImage)),
SLOT(ImageLoaded(quint64,QImage)));
connect(this, SIGNAL(modelAboutToBeReset()), SLOT(CancelPendingImages()));
icon_loader_->SetModel(this);
}
QVariant PodcastDiscoveryModel::data(const QModelIndex& index, int role) const {
@ -76,27 +73,10 @@ void PodcastDiscoveryModel::LazyLoadImage(const QModelIndex& index) {
Podcast podcast = index.data(Role_Podcast).value<Podcast>();
if (podcast.image_url().isValid()) {
quint64 id = app_->album_cover_loader()->LoadImageAsync(
cover_options_, podcast.image_url().toString(), QString());
pending_covers_[id] = item;
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
}
}
void PodcastDiscoveryModel::ImageLoaded(quint64 id, const QImage& image) {
QStandardItem* item = pending_covers_.take(id);
if (!item)
return;
if (!image.isNull()) {
item->setIcon(QIcon(QPixmap::fromImage(image)));
}
}
void PodcastDiscoveryModel::CancelPendingImages() {
app_->album_cover_loader()->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
pending_covers_.clear();
}
QStandardItem* PodcastDiscoveryModel::CreateLoadingIndicator() {
QStandardItem* item = new QStandardItem;
item->setText(tr("Loading..."));

View File

@ -24,6 +24,7 @@
class Application;
class Podcast;
class StandardItemIconLoader;
class PodcastDiscoveryModel : public QStandardItemModel {
Q_OBJECT
@ -54,22 +55,17 @@ public:
QVariant data(const QModelIndex& index, int role) const;
private slots:
void CancelPendingImages();
void ImageLoaded(quint64 id, const QImage& image);
private:
void LazyLoadImage(const QModelIndex& index);
private:
Application* app_;
StandardItemIconLoader* icon_loader_;
bool is_tree_;
AlbumCoverLoaderOptions cover_options_;
QIcon default_icon_;
QIcon folder_icon_;
QMap<quint64, QStandardItem*> pending_covers_;
};
#endif // PODCASTDISCOVERYMODEL_H

View File

@ -19,8 +19,11 @@
#include "podcastbackend.h"
#include "podcastservice.h"
#include "core/application.h"
#include "core/logging.h"
#include "internet/internetmodel.h"
#include "library/libraryview.h"
#include "ui/iconloader.h"
#include "ui/standarditemiconloader.h"
#include <QMenu>
@ -29,10 +32,13 @@ const char* PodcastService::kSettingsGroup = "Podcasts";
PodcastService::PodcastService(Application* app, InternetModel* parent)
: InternetService(kServiceName, app, parent, parent),
use_pretty_covers_(true),
icon_loader_(new StandardItemIconLoader(app->album_cover_loader(), this)),
context_menu_(NULL),
root_(NULL),
backend_(app->podcast_backend())
{
icon_loader_->SetModel(model());
}
PodcastService::~PodcastService() {
@ -53,6 +59,10 @@ void PodcastService::LazyPopulate(QStandardItem* parent) {
}
void PodcastService::PopulatePodcastList(QStandardItem* parent) {
if (default_icon_.isNull()) {
default_icon_ = QIcon(":providers/podcast16.png");
}
foreach (const Podcast& podcast, backend_->GetAllSubscriptions()) {
const int unlistened_count = podcast.extra("db:unlistened_count").toInt();
QString title = podcast.title();
@ -70,6 +80,12 @@ void PodcastService::PopulatePodcastList(QStandardItem* parent) {
}
item->setText(podcast.title());
item->setIcon(default_icon_);
// Load the podcast's image if it has one
if (podcast.image_url().isValid()) {
icon_loader_->LoadIcon(podcast.image_url().toString(), QString(), item);
}
parent->appendRow(item);
}
@ -86,6 +102,14 @@ void PodcastService::ShowContextMenu(const QModelIndex& index,
context_menu_->popup(global_pos);
}
void PodcastService::ReloadSettings() {
QSettings s;
s.beginGroup(LibraryView::kSettingsGroup);
use_pretty_covers_ = s.value("pretty_covers", true).toBool();
// TODO: reload the podcast icons that are already loaded?
}
QModelIndex PodcastService::GetCurrentIndex() {
return QModelIndex();
}

View File

@ -25,6 +25,7 @@
class AddPodcastDialog;
class PodcastBackend;
class StandardItemIconLoader;
class PodcastService : public InternetService {
Q_OBJECT
@ -46,6 +47,7 @@ public:
void LazyPopulate(QStandardItem* parent);
void ShowContextMenu(const QModelIndex& index, const QPoint& global_pos);
void ReloadSettings();
protected:
QModelIndex GetCurrentIndex();
@ -57,6 +59,10 @@ private:
void PopulatePodcastList(QStandardItem* parent);
private:
bool use_pretty_covers_;
QIcon default_icon_;
StandardItemIconLoader* icon_loader_;
QMenu* context_menu_;
QStandardItem* root_;

View File

@ -0,0 +1,94 @@
/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "standarditemiconloader.h"
#include "covers/albumcoverloader.h"
#include <QAbstractItemModel>
#include <QSet>
#include <QStandardItem>
StandardItemIconLoader::StandardItemIconLoader(AlbumCoverLoader* cover_loader,
QObject* parent)
: QObject(parent),
cover_loader_(cover_loader),
model_(NULL)
{
cover_options_.desired_height_ = 16;
connect(cover_loader_, SIGNAL(ImageLoaded(quint64,QImage)),
SLOT(ImageLoaded(quint64,QImage)));
}
void StandardItemIconLoader::SetModel(QAbstractItemModel* model) {
if (model_) {
disconnect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
}
model_ = model;
connect(model_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
SLOT(RowsAboutToBeRemoved(QModelIndex,int,int)));
connect(model_, SIGNAL(modelAboutToBeReset()),
SLOT(ModelReset()));
}
void StandardItemIconLoader::LoadIcon(const QString& art_automatic,
const QString& art_manual,
QStandardItem* for_item) {
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, art_automatic, art_manual);
pending_covers_[id] = for_item;
}
void StandardItemIconLoader::LoadIcon(const Song& song, QStandardItem* for_item) {
const quint64 id = cover_loader_->LoadImageAsync(cover_options_, song);
pending_covers_[id] = for_item;
}
void StandardItemIconLoader::RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end) {
for (QMap<quint64, QStandardItem*>::iterator it = pending_covers_.begin() ;
it != pending_covers_.end() ; ) {
const QStandardItem* item = it.value();
const QStandardItem* item_parent = item->parent();
if (item_parent &&
item_parent->index() == parent &&
item->index().row() >= begin &&
item->index().row() <= end) {
cover_loader_->CancelTask(it.key());
it = pending_covers_.erase(it);
} else {
++ it;
}
}
}
void StandardItemIconLoader::ModelReset() {
cover_loader_->CancelTasks(QSet<quint64>::fromList(pending_covers_.keys()));
pending_covers_.clear();
}
void StandardItemIconLoader::ImageLoaded(quint64 id, const QImage& image) {
QStandardItem* item = pending_covers_.take(id);
if (!item)
return;
if (!image.isNull()) {
item->setIcon(QIcon(QPixmap::fromImage(image)));
}
}

View File

@ -0,0 +1,63 @@
/* This file is part of Clementine.
Copyright 2012, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef STANDARDITEMICONLOADER_H
#define STANDARDITEMICONLOADER_H
#include "covers/albumcoverloaderoptions.h"
#include <QMap>
#include <QObject>
class AlbumCoverLoader;
class Song;
class QAbstractItemModel;
class QModelIndex;
class QStandardItem;
// Uses an AlbumCoverLoader to asynchronously load and set an icon on a
// QStandardItem.
class StandardItemIconLoader : public QObject {
Q_OBJECT
public:
StandardItemIconLoader(AlbumCoverLoader* cover_loader, QObject* parent = 0);
AlbumCoverLoaderOptions* options() { return &cover_options_; }
void SetModel(QAbstractItemModel* model);
void LoadIcon(const QString& art_automatic, const QString& art_manual,
QStandardItem* for_item);
void LoadIcon(const Song& song, QStandardItem* for_item);
private slots:
void ImageLoaded(quint64 id, const QImage& image);
void RowsAboutToBeRemoved(const QModelIndex& parent, int begin, int end);
void ModelReset();
private:
AlbumCoverLoader* cover_loader_;
AlbumCoverLoaderOptions cover_options_;
QAbstractItemModel* model_;
QMap<quint64, QStandardItem*> pending_covers_;
};
#endif // STANDARDITEMICONLOADER_H