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