Show update download progress
This commit is contained in:
parent
8aa10754e2
commit
45e12265c1
@ -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)
|
||||||
|
@ -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_;
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user