Show update download progress

This commit is contained in:
Gres 2020-04-02 21:00:20 +03:00
parent 8aa10754e2
commit 45e12265c1
3 changed files with 131 additions and 25 deletions

View File

@ -1,6 +1,7 @@
#include "updates.h" #include "updates.h"
#include "debug.h" #include "debug.h"
#include <QApplication>
#include <QComboBox> #include <QComboBox>
#include <QDir> #include <QDir>
#include <QJsonArray> #include <QJsonArray>
@ -95,6 +96,8 @@ void Loader::applyUserActions()
break; break;
} }
connect(reply, &QNetworkReply::downloadProgress, //
this, &Loader::updateProgress);
file.downloadPath = downloadPath_ + '/' + file.rawPath; file.downloadPath = downloadPath_ + '/' + file.rawPath;
componentReplyToPath_.emplace(reply, file.downloadPath); componentReplyToPath_.emplace(reply, file.downloadPath);
} }
@ -169,6 +172,7 @@ void Loader::commitUpdate()
{ {
SOFT_ASSERT(installer_, return ); SOFT_ASSERT(installer_, return );
if (installer_->commit()) { if (installer_->commit()) {
model_->resetProgress();
emit updated(); emit updated();
} else { } else {
emit error(tr("Update failed: %1").arg(installer_->errorString())); emit error(tr("Update failed: %1").arg(installer_->errorString()));
@ -176,6 +180,16 @@ void Loader::commitUpdate()
finishUpdate(); finishUpdate();
} }
void Loader::updateProgress(qint64 bytesSent, qint64 bytesTotal)
{
if (bytesTotal < 1)
return;
const auto reply = qobject_cast<QNetworkReply *>(sender());
SOFT_ASSERT(reply, return );
const auto progress = int(100.0 * bytesSent / bytesTotal);
model_->updateProgress(reply->url(), progress);
}
Model *Loader::model() const Model *Loader::model() const
{ {
return model_; return model_;
@ -273,6 +287,36 @@ std::unique_ptr<Model::Component> Model::parse(const QJsonObject &json) const
return result; return result;
} }
void Model::updateProgress(Model::Component &component, const QUrl &url,
int progress)
{
if (!component.files.empty()) {
for (auto &file : component.files) {
if (!url.isEmpty() && file.url != url)
continue;
file.progress = progress;
component.progress = progress;
for (const auto &file : component.files)
component.progress = std::max(file.progress, component.progress);
const auto index = toIndex(component, int(Column::Progress));
emit dataChanged(index, index, {Qt::DisplayRole});
if (!url.isEmpty())
break;
}
return;
}
if (!component.children.empty()) {
for (auto &child : component.children)
updateProgress(*child, url, progress);
return;
}
}
void Model::setExpansions(const std::map<QString, QString> &expansions) void Model::setExpansions(const std::map<QString, QString> &expansions)
{ {
expansions_ = expansions; expansions_ = expansions;
@ -321,6 +365,22 @@ bool Model::hasUpdates() const
return hasUpdates(*root_); return hasUpdates(*root_);
} }
void Model::updateProgress(const QUrl &url, int progress)
{
if (!root_)
return;
updateProgress(*root_, url, progress);
}
void Model::resetProgress()
{
if (!root_)
return;
updateProgress(*root_, {}, 0);
}
bool Model::hasUpdates(const Model::Component &component) const bool Model::hasUpdates(const Model::Component &component) const
{ {
for (const auto &i : component.children) { for (const auto &i : component.children) {
@ -465,9 +525,9 @@ QVariant Model::headerData(int section, Qt::Orientation orientation,
return section + 1; return section + 1;
const QMap<Column, QString> names{ const QMap<Column, QString> names{
{Column::Name, tr("Name")}, {Column::State, tr("State")}, {Column::Name, tr("Name")}, {Column::State, tr("State")},
{Column::Action, tr("Action")}, {Column::Version, tr("Version")}, {Column::Action, tr("Action")}, {Column::Version, tr("Version")},
{Column::Files, tr("Files")}, {Column::Progress, tr("Progress")}, {Column::Files, tr("Files")},
}; };
return names.value(Column(section)); return names.value(Column(section));
} }
@ -485,6 +545,8 @@ QVariant Model::data(const QModelIndex &index, int role) const
case int(Column::State): return toString(ptr->state); case int(Column::State): return toString(ptr->state);
case int(Column::Action): return toString(ptr->action); case int(Column::Action): return toString(ptr->action);
case int(Column::Version): return ptr->version; case int(Column::Version): return ptr->version;
case int(Column::Progress):
return ptr->progress > 0 ? ptr->progress : QVariant();
case int(Column::Files): { case int(Column::Files): {
QStringList files; QStringList files;
files.reserve(ptr->files.size()); files.reserve(ptr->files.size());
@ -536,36 +598,73 @@ Qt::ItemFlags Model::flags(const QModelIndex &index) const
return result; return result;
} }
ActionDelegate::ActionDelegate(QObject *parent) UpdateDelegate::UpdateDelegate(QObject *parent)
: QStyledItemDelegate(parent) : QStyledItemDelegate(parent)
{ {
} }
QWidget *ActionDelegate::createEditor(QWidget *parent, void UpdateDelegate::paint(QPainter *painter,
const QStyleOptionViewItem & /*option*/, const QStyleOptionViewItem &option,
const QModelIndex & /*index*/) const const QModelIndex &index) const
{ {
auto combo = new QComboBox(parent); if (index.column() == int(Model::Column::Progress) &&
combo->setEditable(false); !index.data().isNull()) {
combo->addItems({toString(Action::NoAction), toString(Action::Remove), QStyleOptionProgressBar progressBarOption;
toString(Action::Install)}); progressBarOption.rect = option.rect;
return combo; progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
const auto progress = index.data().toInt();
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,
&progressBarOption, painter);
return;
}
QStyledItemDelegate::paint(painter, option, index);
} }
void ActionDelegate::setEditorData(QWidget *editor, QWidget *UpdateDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == int(Model::Column::Action)) {
auto combo = new QComboBox(parent);
combo->setEditable(false);
combo->addItems({toString(Action::NoAction), toString(Action::Remove),
toString(Action::Install)});
return combo;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void UpdateDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const const QModelIndex &index) const
{ {
auto combo = qobject_cast<QComboBox *>(editor); if (index.column() == int(Model::Column::Action)) {
SOFT_ASSERT(combo, return ); auto combo = qobject_cast<QComboBox *>(editor);
combo->setCurrentText(index.data(Qt::EditRole).toString()); SOFT_ASSERT(combo, return );
combo->setCurrentText(index.data(Qt::EditRole).toString());
return;
}
return QStyledItemDelegate::setEditorData(editor, index);
} }
void ActionDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, void UpdateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const const QModelIndex &index) const
{ {
auto combo = qobject_cast<QComboBox *>(editor); if (index.column() == int(Model::Column::Action)) {
SOFT_ASSERT(combo, return ); auto combo = qobject_cast<QComboBox *>(editor);
model->setData(index, combo->currentIndex()); SOFT_ASSERT(combo, return );
model->setData(index, combo->currentIndex());
return;
}
return QStyledItemDelegate::setModelData(editor, model, index);
} }
Installer::Installer(const UserActions &actions) Installer::Installer(const UserActions &actions)

View File

@ -19,15 +19,18 @@ struct File {
QString downloadPath; QString downloadPath;
QString md5; QString md5;
QDateTime versionDate; QDateTime versionDate;
int progress{0};
}; };
using UserActions = std::multimap<Action, File>; using UserActions = std::multimap<Action, File>;
class ActionDelegate : public QStyledItemDelegate class UpdateDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ActionDelegate(QObject* parent = nullptr); explicit UpdateDelegate(QObject* parent = nullptr);
void paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
const QModelIndex& index) const override; const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override; void setEditorData(QWidget* editor, const QModelIndex& index) const override;
@ -39,7 +42,7 @@ class Model : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
enum class Column { Name, State, Action, Version, Files, Count }; enum class Column { Name, State, Action, Version, Progress, Files, Count };
explicit Model(QObject* parent = nullptr); explicit Model(QObject* parent = nullptr);
@ -48,6 +51,8 @@ public:
UserActions userActions() const; UserActions userActions() const;
void updateStates(); void updateStates();
bool hasUpdates() const; bool hasUpdates() const;
void updateProgress(const QUrl& url, int progress);
void resetProgress();
QModelIndex index(int row, int column, QModelIndex index(int row, int column,
const QModelIndex& parent) const override; const QModelIndex& parent) const override;
@ -72,9 +77,11 @@ private:
std::vector<std::unique_ptr<Component>> children; std::vector<std::unique_ptr<Component>> children;
Component* parent{nullptr}; Component* parent{nullptr};
int index{-1}; int index{-1};
int progress{0};
}; };
std::unique_ptr<Component> parse(const QJsonObject& json) const; std::unique_ptr<Component> parse(const QJsonObject& json) const;
void updateProgress(Component& component, const QUrl& url, int progress);
void updateState(Component& component); void updateState(Component& component);
bool hasUpdates(const Component& component) const; bool hasUpdates(const Component& component) const;
void fillUserActions(UserActions& actions, Component& component) const; void fillUserActions(UserActions& actions, Component& component) const;
@ -126,6 +133,7 @@ private:
QString toError(QNetworkReply& reply) const; QString toError(QNetworkReply& reply) const;
void finishUpdate(const QString& error = {}); void finishUpdate(const QString& error = {});
void commitUpdate(); void commitUpdate();
void updateProgress(qint64 bytesSent, qint64 bytesTotal);
QNetworkAccessManager* network_; QNetworkAccessManager* network_;
Model* model_; Model* model_;

View File

@ -89,8 +89,7 @@ SettingsEditor::SettingsEditor(Manager &manager, update::Loader &updater)
auto updatesProxy = new QSortFilterProxyModel(this); auto updatesProxy = new QSortFilterProxyModel(this);
updatesProxy->setSourceModel(updater_.model()); updatesProxy->setSourceModel(updater_.model());
ui->updatesView->setModel(updatesProxy); ui->updatesView->setModel(updatesProxy);
ui->updatesView->setItemDelegateForColumn(int(update::Model::Column::Action), ui->updatesView->setItemDelegate(new update::UpdateDelegate(this));
new update::ActionDelegate(this));
#ifndef DEVELOP #ifndef DEVELOP
ui->updatesView->hideColumn(int(update::Model::Column::Files)); ui->updatesView->hideColumn(int(update::Model::Column::Files));
#endif #endif