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

View File

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

View File

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