feat: Propogated subtask progress
Oh boy this is big. > TaskStepProgress struct is now QMetaObject compatabile and can be sent through signals > Task now has a method to propogates sub task progress it must be signal bound by each task containing a task wishing to report progress of it's children. > Downloads report speed > Tasks now have UUIDS to track them - use when reporting - use when logging - use when storeing them or objects related to them Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
		| @@ -98,6 +98,7 @@ void InstanceImportTask::executeTask() | ||||
|  | ||||
|         connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded); | ||||
|         connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged); | ||||
|         connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress); | ||||
|         connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed); | ||||
|         connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted); | ||||
|  | ||||
| @@ -291,6 +292,7 @@ void InstanceImportTask::processFlame() | ||||
|     }); | ||||
|     connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); | ||||
|     connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); | ||||
|     connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); | ||||
|     connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); | ||||
|     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); | ||||
|  | ||||
| @@ -382,6 +384,7 @@ void InstanceImportTask::processModrinth() | ||||
|     }); | ||||
|     connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed); | ||||
|     connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress); | ||||
|     connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress); | ||||
|     connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus); | ||||
|     connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater); | ||||
|  | ||||
|   | ||||
| @@ -788,6 +788,7 @@ class InstanceStaging : public Task { | ||||
|         connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable); | ||||
|         connect(child, &Task::status, this, &InstanceStaging::setStatus); | ||||
|         connect(child, &Task::progress, this, &InstanceStaging::setProgress); | ||||
|         connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress); | ||||
|         connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -53,6 +53,7 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack pack, | ||||
|     m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename()))); | ||||
|     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded); | ||||
|     connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged); | ||||
|     connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress); | ||||
|     connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed); | ||||
|  | ||||
|     addTask(m_filesNetJob); | ||||
|   | ||||
| @@ -27,8 +27,9 @@ void Update::executeTask() | ||||
|     if(m_updateTask) | ||||
|     { | ||||
|         connect(m_updateTask.get(), SIGNAL(finished()), this, SLOT(updateFinished())); | ||||
|         connect(m_updateTask.get(), &Task::progress, this, &Task::setProgress); | ||||
|         connect(m_updateTask.get(), &Task::status, this, &Task::setStatus); | ||||
|         connect(m_updateTask.get(), &Task::progress, this, &Update::setProgress); | ||||
|         connect(m_updateTask.get(), &Task::stepProgress, this, &Update::propogateStepProgress); | ||||
|         connect(m_updateTask.get(), &Task::status, this, &Update::setStatus); | ||||
|         emit progressReportingRequest(); | ||||
|         return; | ||||
|     } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ void MinecraftLoadAndCheck::executeTask() | ||||
|     connect(m_task.get(), &Task::failed, this, &MinecraftLoadAndCheck::subtaskFailed); | ||||
|     connect(m_task.get(), &Task::aborted, this, [this]{ subtaskFailed(tr("Aborted")); }); | ||||
|     connect(m_task.get(), &Task::progress, this, &MinecraftLoadAndCheck::progress); | ||||
|     connect(m_task.get(), &Task::stepProgress, this, &MinecraftLoadAndCheck::propogateStepProgress); | ||||
|     connect(m_task.get(), &Task::status, this, &MinecraftLoadAndCheck::setStatus); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -100,6 +100,7 @@ void MinecraftUpdate::next() | ||||
|         disconnect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); | ||||
|         disconnect(task.get(), &Task::aborted, this, &Task::abort); | ||||
|         disconnect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); | ||||
|         disconnect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); | ||||
|         disconnect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); | ||||
|     } | ||||
|     if(m_currentTask == m_tasks.size()) | ||||
| @@ -118,6 +119,7 @@ void MinecraftUpdate::next() | ||||
|     connect(task.get(), &Task::failed, this, &MinecraftUpdate::subtaskFailed); | ||||
|     connect(task.get(), &Task::aborted, this, &Task::abort); | ||||
|     connect(task.get(), &Task::progress, this, &MinecraftUpdate::progress); | ||||
|     connect(task.get(), &Task::stepProgress, this, &MinecraftUpdate::propogateStepProgress); | ||||
|     connect(task.get(), &Task::status, this, &MinecraftUpdate::setStatus); | ||||
|     // if the task is already running, do not start it again | ||||
|     if(!task->isRunning()) | ||||
|   | ||||
| @@ -45,6 +45,7 @@ void AssetUpdateTask::executeTask() | ||||
|     connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetIndexFailed); | ||||
|     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||
|     connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); | ||||
|     connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); | ||||
|  | ||||
|     qDebug() << m_inst->name() << ": Starting asset index download"; | ||||
|     downloadJob->start(); | ||||
| @@ -83,6 +84,7 @@ void AssetUpdateTask::assetIndexFinished() | ||||
|         connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed); | ||||
|         connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||
|         connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress); | ||||
|         connect(downloadJob.get(), &NetJob::stepProgress, this, &AssetUpdateTask::propogateStepProgress); | ||||
|         downloadJob->start(); | ||||
|         return; | ||||
|     } | ||||
|   | ||||
| @@ -75,6 +75,7 @@ void FMLLibrariesTask::executeTask() | ||||
|     connect(dljob.get(), &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed); | ||||
|     connect(dljob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||
|     connect(dljob.get(), &NetJob::progress, this, &FMLLibrariesTask::progress); | ||||
|     connect(dljob.get(), &NetJob::stepProgress, this, &FMLLibrariesTask::propogateStepProgress); | ||||
|     downloadJob.reset(dljob); | ||||
|     downloadJob->start(); | ||||
| } | ||||
|   | ||||
| @@ -70,6 +70,8 @@ void LibrariesTask::executeTask() | ||||
|     connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed); | ||||
|     connect(downloadJob.get(), &NetJob::aborted, this, [this]{ emitFailed(tr("Aborted")); }); | ||||
|     connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress); | ||||
|     connect(downloadJob.get(), &NetJob::stepProgress, this, &LibrariesTask::propogateStepProgress); | ||||
|  | ||||
|     downloadJob->start(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -683,6 +683,7 @@ void PackInstallTask::installConfigs() | ||||
|         abortable = true; | ||||
|         setProgress(current, total); | ||||
|     }); | ||||
|     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); | ||||
|     connect(jobPtr.get(), &NetJob::aborted, [&]{ | ||||
|         abortable = false; | ||||
|         jobPtr.reset(); | ||||
| @@ -849,6 +850,7 @@ void PackInstallTask::downloadMods() | ||||
|         abortable = true; | ||||
|         setProgress(current, total); | ||||
|     }); | ||||
|     connect(jobPtr.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); | ||||
|     connect(jobPtr.get(), &NetJob::aborted, [&] | ||||
|     { | ||||
|         abortable = false; | ||||
|   | ||||
| @@ -35,6 +35,7 @@ | ||||
|  | ||||
| #include "FlameInstanceCreationTask.h" | ||||
|  | ||||
| #include "modplatform/flame/FileResolvingTask.h" | ||||
| #include "modplatform/flame/FlameAPI.h" | ||||
| #include "modplatform/flame/PackManifest.h" | ||||
|  | ||||
| @@ -382,7 +383,7 @@ bool FlameCreationTask::createInstance() | ||||
|     }); | ||||
|     connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::progress, this, &FlameCreationTask::setProgress); | ||||
|     connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::status, this, &FlameCreationTask::setStatus); | ||||
|  | ||||
|     connect(m_mod_id_resolver.get(), &Flame::FileResolvingTask::stepProgress, this, &FlameCreationTask::propogateStepProgress); | ||||
|     m_mod_id_resolver->start(); | ||||
|  | ||||
|     loop.exec(); | ||||
| @@ -497,6 +498,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop) | ||||
|         setError(reason); | ||||
|     }); | ||||
|     connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress); | ||||
|     connect(m_files_job.get(), &NetJob::stepProgress, this, &FlameCreationTask::propogateStepProgress); | ||||
|     connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); | ||||
|  | ||||
|     setStatus(tr("Downloading mods...")); | ||||
|   | ||||
| @@ -81,6 +81,7 @@ void PackInstallTask::downloadPack() | ||||
|     connect(netJobContainer.get(), &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded); | ||||
|     connect(netJobContainer.get(), &NetJob::failed, this, &PackInstallTask::onDownloadFailed); | ||||
|     connect(netJobContainer.get(), &NetJob::progress, this, &PackInstallTask::onDownloadProgress); | ||||
|     connect(netJobContainer.get(), &NetJob::stepProgress, this, &PackInstallTask::propogateStepProgress); | ||||
|     connect(netJobContainer.get(), &NetJob::aborted, this, &PackInstallTask::onDownloadAborted); | ||||
|  | ||||
|     netJobContainer->start(); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "net/ChecksumValidator.h" | ||||
|  | ||||
| #include "net/NetJob.h" | ||||
| #include "settings/INISettingsObject.h" | ||||
|  | ||||
| #include "ui/dialogs/CustomMessageBox.h" | ||||
| @@ -263,6 +264,7 @@ bool ModrinthCreationTask::createInstance() | ||||
|     }); | ||||
|     connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit); | ||||
|     connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); }); | ||||
|     connect(m_files_job.get(), &NetJob::stepProgress, this, &ModrinthCreationTask::propogateStepProgress); | ||||
|  | ||||
|     setStatus(tr("Downloading mods...")); | ||||
|     m_files_job->start(); | ||||
|   | ||||
| @@ -50,6 +50,7 @@ void Technic::SingleZipPackInstallTask::executeTask() | ||||
|     auto job = m_filesNetJob.get(); | ||||
|     connect(job, &NetJob::succeeded, this, &Technic::SingleZipPackInstallTask::downloadSucceeded); | ||||
|     connect(job, &NetJob::progress, this, &Technic::SingleZipPackInstallTask::downloadProgressChanged); | ||||
|     connect(job, &NetJob::stepProgress, this, &Technic::SingleZipPackInstallTask::propogateStepProgress); | ||||
|     connect(job, &NetJob::failed, this, &Technic::SingleZipPackInstallTask::downloadFailed); | ||||
|     m_filesNetJob->start(); | ||||
| } | ||||
|   | ||||
| @@ -127,6 +127,7 @@ void Technic::SolderPackInstallTask::fileListSucceeded() | ||||
|  | ||||
|     connect(m_filesNetJob.get(), &NetJob::succeeded, this, &Technic::SolderPackInstallTask::downloadSucceeded); | ||||
|     connect(m_filesNetJob.get(), &NetJob::progress, this, &Technic::SolderPackInstallTask::downloadProgressChanged); | ||||
|     connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &Technic::SolderPackInstallTask::propogateStepProgress); | ||||
|     connect(m_filesNetJob.get(), &NetJob::failed, this, &Technic::SolderPackInstallTask::downloadFailed); | ||||
|     connect(m_filesNetJob.get(), &NetJob::aborted, this, &Technic::SolderPackInstallTask::downloadAborted); | ||||
|     m_filesNetJob->start(); | ||||
|   | ||||
| @@ -36,6 +36,8 @@ | ||||
|  */ | ||||
|  | ||||
| #include "Download.h" | ||||
| #include <QRegularExpression> | ||||
| #include <QUrl> | ||||
|  | ||||
| #include <QDateTime> | ||||
| #include <QFileInfo> | ||||
| @@ -52,6 +54,33 @@ Q_LOGGING_CATEGORY(DownloadLogC, "Task.Net.Download") | ||||
|  | ||||
| namespace Net { | ||||
|  | ||||
| QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false) | ||||
| {    | ||||
|     auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments; | ||||
|     auto str_url = url.toDisplayString(display_options); | ||||
|     if (str_url.length() <= max_len) | ||||
|         return str_url; | ||||
|  | ||||
|     QRegularExpression re(R"(^([\w]+:\/\/)([\w._-]+\/)([\w._-]+\/).*(\/[^]+[^]+)$)"); | ||||
|      | ||||
|     auto url_compact = QString(str_url); | ||||
|     url_compact.replace(re, "\\1\\2\\3...\\4"); | ||||
|     if (url_compact.length() >= max_len) { | ||||
|         auto url_compact = QString(str_url); | ||||
|         url_compact.replace(re, "\\1\\2...\\4"); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if ((url_compact.length() >= max_len) && hard_limit) { | ||||
|         auto to_remove = url_compact.length() - max_len + 3; | ||||
|         url_compact.remove(url_compact.length() - to_remove - 1, to_remove); | ||||
|         url_compact.append("..."); | ||||
|     } | ||||
|  | ||||
|     return url_compact; | ||||
|  | ||||
| } | ||||
|  | ||||
| auto Download::makeCached(QUrl url, MetaEntryPtr entry, Options options) -> Download::Ptr | ||||
| { | ||||
|     auto dl = makeShared<Download>(); | ||||
| @@ -91,7 +120,7 @@ void Download::addValidator(Validator* v) | ||||
|  | ||||
| void Download::executeTask() | ||||
| { | ||||
|     setStatus(tr("Downloading %1").arg(m_url.toString())); | ||||
|     setStatus(tr("Downloading %1").arg(truncateUrlHumanFriendly(m_url, 60))); | ||||
|  | ||||
|     if (getState() == Task::State::AbortedByUser) { | ||||
|         qCWarning(DownloadLogC) << getUid().toString() << "Attempt to start an aborted Download:" << m_url.toString(); | ||||
| @@ -152,9 +181,11 @@ void Download::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) | ||||
|     auto elapsed = now - m_last_progress_time; | ||||
|     auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count(); | ||||
|     auto bytes_recived_since = bytesReceived - m_last_progress_bytes; | ||||
|  | ||||
|     auto speed = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s"; | ||||
|     m_details = speed;     | ||||
|     if (elapsed_ms > 0) { | ||||
|         m_details = humanReadableFileSize(bytes_recived_since / elapsed_ms * 1000) + "/s"; | ||||
|     } else { | ||||
|         m_details = "0 b/s"; | ||||
|     }   | ||||
|  | ||||
|     setProgress(bytesReceived, bytesTotal); | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| #include <QDebug> | ||||
| #include <QCoreApplication> | ||||
| #include "tasks/Task.h" | ||||
|  | ||||
| ConcurrentTask::ConcurrentTask(QObject* parent, QString task_name, int max_concurrent) | ||||
|     : Task(parent), m_name(task_name), m_total_max_size(max_concurrent) | ||||
| @@ -15,13 +16,9 @@ ConcurrentTask::~ConcurrentTask() | ||||
|     } | ||||
| } | ||||
|  | ||||
| auto ConcurrentTask::getStepProgress() const -> QList<TaskStepProgress> | ||||
| auto ConcurrentTask::getStepProgress() const -> TaskStepProgressList | ||||
| { | ||||
|     QList<TaskStepProgress> task_progress; | ||||
|     for (auto progress : task_progress) { | ||||
|         task_progress.append(task_progress); | ||||
|     } | ||||
|     return task_progress; | ||||
|     return m_task_progress.values(); | ||||
| } | ||||
|  | ||||
| void ConcurrentTask::addTask(Task::Ptr task) | ||||
| @@ -103,7 +100,7 @@ void ConcurrentTask::startNext() | ||||
|     connect(next.get(), &Task::failed, this, [this, next](QString msg) { subTaskFailed(next, msg); }); | ||||
|  | ||||
|     connect(next.get(), &Task::status, this, [this, next](QString msg){ subTaskStatus(next, msg); }); | ||||
|     connect(next.get(), &Task::stepProgress, this, [this, next](QList<TaskStepProgress> tp){ subTaskStepProgress(next, tp); }); | ||||
|     connect(next.get(), &Task::stepProgress, this, [this, next](TaskStepProgressList tp){ subTaskStepProgress(next, tp); }); | ||||
|  | ||||
|     connect(next.get(), &Task::progress, this, [this, next](qint64 current, qint64 total){ subTaskProgress(next, current, total); }); | ||||
|  | ||||
| @@ -112,6 +109,7 @@ void ConcurrentTask::startNext() | ||||
|  | ||||
|  | ||||
|     updateState(); | ||||
|     updateStepProgress(); | ||||
|  | ||||
|     QCoreApplication::processEvents(); | ||||
|  | ||||
| @@ -129,12 +127,12 @@ void ConcurrentTask::subTaskSucceeded(Task::Ptr task) | ||||
|     m_succeeded.insert(task.get(), task); | ||||
|  | ||||
|     m_doing.remove(task.get()); | ||||
|     m_task_progress.value(task->getUid())->state = TaskState::Succeeded; | ||||
|     m_task_progress.value(task->getUid())->state = TaskStepState::Succeeded; | ||||
|  | ||||
|     disconnect(task.get(), 0, this, 0); | ||||
|  | ||||
|     updateState(); | ||||
|  | ||||
|     updateStepProgress(); | ||||
|     startNext(); | ||||
| } | ||||
|  | ||||
| @@ -144,20 +142,22 @@ void ConcurrentTask::subTaskFailed(Task::Ptr task, const QString& msg) | ||||
|     m_failed.insert(task.get(), task); | ||||
|  | ||||
|     m_doing.remove(task.get()); | ||||
|     m_task_progress.value(task->getUid())->state = TaskState::Failed; | ||||
|     m_task_progress.value(task->getUid())->state = TaskStepState::Failed; | ||||
|  | ||||
|     disconnect(task.get(), 0, this, 0); | ||||
|  | ||||
|     updateState(); | ||||
|  | ||||
|     updateStepProgress(); | ||||
|     startNext(); | ||||
| } | ||||
|  | ||||
| void ConcurrentTask::subTaskStatus(Task::Ptr task, const QString& msg) | ||||
| { | ||||
|     auto taskProgress = m_task_progress.value(task->getUid()); | ||||
|     taskProgress->status = msg; | ||||
|     taskProgress->status = msg;  | ||||
|     taskProgress->state = TaskStepState::Running; | ||||
|     updateState(); | ||||
|     updateStepProgress(); | ||||
| } | ||||
|  | ||||
| void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 total) | ||||
| @@ -166,21 +166,28 @@ void ConcurrentTask::subTaskProgress(Task::Ptr task, qint64 current, qint64 tota | ||||
|      | ||||
|     taskProgress->current = current; | ||||
|     taskProgress->total = total; | ||||
|  | ||||
|     taskProgress->state = TaskStepState::Running; | ||||
|     taskProgress->details = task->getDetails();  | ||||
|  | ||||
|     updateStepProgress(); | ||||
|     updateState(); | ||||
| } | ||||
|  | ||||
| void ConcurrentTask::subTaskStepProgress(Task::Ptr task, QList<TaskStepProgress> task_step_progress) | ||||
| void ConcurrentTask::subTaskStepProgress(Task::Ptr task, TaskStepProgressList task_step_progress) | ||||
| { | ||||
|     for (auto progress : task_step_progress) { | ||||
|         if (!m_task_progress.contains(progress.uid)) | ||||
|             m_task_progress.insert(progress.uid, std::make_shared<TaskStepProgress>(progress)); | ||||
|  | ||||
|  | ||||
|         if (!m_task_progress.contains(progress->uid)) { | ||||
|             m_task_progress.insert(progress->uid, progress); | ||||
|         } else { | ||||
|             auto tp = m_task_progress.value(progress->uid); | ||||
|             tp->current = progress->current; | ||||
|             tp->total = progress->total; | ||||
|             tp->status = progress->status; | ||||
|             tp->details = progress->details; | ||||
|         }            | ||||
|     } | ||||
|  | ||||
|     updateStepProgress(); | ||||
|      | ||||
| } | ||||
|  | ||||
| @@ -194,6 +201,7 @@ void ConcurrentTask::updateStepProgress() | ||||
|  | ||||
|    m_stepProgress = current; | ||||
|    m_stepTotalProgress = total; | ||||
|    emit stepProgress(m_task_progress.values()); | ||||
| } | ||||
|  | ||||
| void ConcurrentTask::updateState() | ||||
|   | ||||
| @@ -18,8 +18,8 @@ public: | ||||
|  | ||||
|     bool canAbort() const override { return true; } | ||||
|  | ||||
|     inline auto isMultiStep() const -> bool override { return m_queue.size() > 1; }; | ||||
|     auto getStepProgress() const -> QList<TaskStepProgress> override; | ||||
|     inline auto isMultiStep() const -> bool override { return totalSize() > 1; }; | ||||
|     auto getStepProgress() const -> TaskStepProgressList override; | ||||
|  | ||||
|     void addTask(Task::Ptr task); | ||||
|  | ||||
| @@ -41,7 +41,7 @@ slots: | ||||
|     void subTaskFailed(Task::Ptr, const QString &msg); | ||||
|     void subTaskStatus(Task::Ptr task, const QString &msg); | ||||
|     void subTaskProgress(Task::Ptr task, qint64 current, qint64 total); | ||||
|     void subTaskStepProgress(Task::Ptr task, QList<TaskStepProgress> task_step_progress); | ||||
|     void subTaskStepProgress(Task::Ptr task, TaskStepProgressList task_step_progress); | ||||
|  | ||||
| protected: | ||||
|     // NOTE: This is not thread-safe. | ||||
|   | ||||
| @@ -148,6 +148,11 @@ void Task::emitSucceeded() | ||||
|     emit finished(); | ||||
| } | ||||
|  | ||||
| void Task::propogateStepProgress(TaskStepProgressList task_progress) | ||||
| { | ||||
|     emit stepProgress(task_progress); | ||||
| } | ||||
|  | ||||
| QString Task::describe() | ||||
| { | ||||
|     QString outStr; | ||||
|   | ||||
| @@ -42,7 +42,7 @@ | ||||
|  | ||||
| #include "QObjectPtr.h" | ||||
|  | ||||
| enum class TaskState { | ||||
| enum class TaskStepState { | ||||
|     Waiting, | ||||
|     Running, | ||||
|     Failed, | ||||
| @@ -50,16 +50,22 @@ enum class TaskState { | ||||
|     Finished | ||||
| }; | ||||
|  | ||||
| Q_DECLARE_METATYPE(TaskStepState) | ||||
|  | ||||
| struct TaskStepProgress { | ||||
|     QUuid uid;  | ||||
|     qint64 current; | ||||
|     qint64 total; | ||||
|     QString status; | ||||
|     QString details; | ||||
|     TaskState state = TaskState::Waiting; | ||||
|     bool isDone() { return (state == TaskState::Failed) || (state == TaskState::Succeeded) || (state == TaskState::Finished); } | ||||
|     qint64 current = 0; | ||||
|     qint64 total = -1; | ||||
|     QString status = ""; | ||||
|     QString details = ""; | ||||
|     TaskStepState state = TaskStepState::Waiting; | ||||
|     bool isDone() { return (state == TaskStepState::Failed) || (state == TaskStepState::Succeeded) || (state == TaskStepState::Finished); } | ||||
| }; | ||||
|  | ||||
| Q_DECLARE_METATYPE(TaskStepProgress) | ||||
|  | ||||
| typedef QList<std::shared_ptr<TaskStepProgress>> TaskStepProgressList; | ||||
|  | ||||
| class Task : public QObject, public QRunnable { | ||||
|     Q_OBJECT | ||||
|    public: | ||||
| @@ -97,7 +103,7 @@ class Task : public QObject, public QRunnable { | ||||
|  | ||||
|     qint64 getProgress() { return m_progress; } | ||||
|     qint64 getTotalProgress() { return m_progressTotal; } | ||||
|     virtual auto getStepProgress() const -> QList<TaskStepProgress> { return {}; } | ||||
|     virtual auto getStepProgress() const -> TaskStepProgressList { return {}; } | ||||
|  | ||||
|     virtual auto getDetails() const -> QString { return ""; }  | ||||
|  | ||||
| @@ -117,7 +123,7 @@ class Task : public QObject, public QRunnable { | ||||
|     void aborted(); | ||||
|     void failed(QString reason); | ||||
|     void status(QString status); | ||||
|     void stepProgress(QList<TaskStepProgress> task_progress); //  | ||||
|     void stepProgress(TaskStepProgressList task_progress); //  | ||||
|  | ||||
|     /** Emitted when the canAbort() status has changed. | ||||
|      */ | ||||
| @@ -140,6 +146,8 @@ class Task : public QObject, public QRunnable { | ||||
|     virtual void emitAborted(); | ||||
|     virtual void emitFailed(QString reason = ""); | ||||
|  | ||||
|     virtual void propogateStepProgress(TaskStepProgressList task_progress); | ||||
|  | ||||
|    public slots: | ||||
|     void setStatus(const QString& status); | ||||
|     void setProgress(qint64 current, qint64 total); | ||||
|   | ||||
| @@ -48,10 +48,12 @@ | ||||
| template<typename T>  | ||||
| int map_int_range(T value) | ||||
| { | ||||
|     auto type_min = std::numeric_limits<T>::min(); | ||||
|     // auto type_min = std::numeric_limits<T>::min(); | ||||
|     auto type_min = 0; | ||||
|     auto type_max = std::numeric_limits<T>::max(); | ||||
|  | ||||
|     auto int_min = std::numeric_limits<int>::min(); | ||||
|     // auto int_min = std::numeric_limits<int>::min(); | ||||
|     auto int_min = 0; | ||||
|     auto int_max = std::numeric_limits<int>::max(); | ||||
|  | ||||
|     auto type_range = type_max - type_min; | ||||
| @@ -64,6 +66,7 @@ int map_int_range(T value) | ||||
| ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog) | ||||
| { | ||||
|     ui->setupUi(this); | ||||
|     ui->taskProgressScrollArea->setHidden(true); | ||||
|     this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||||
|     setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true); | ||||
|     setSkipButton(false); | ||||
| @@ -94,10 +97,17 @@ ProgressDialog::~ProgressDialog() | ||||
| } | ||||
|  | ||||
| void ProgressDialog::updateSize() | ||||
| { | ||||
| {    | ||||
|     QSize lastSize = this->size(); | ||||
|     QSize qSize = QSize(480, minimumSizeHint().height()); | ||||
|     resize(qSize); | ||||
|     setFixedSize(qSize); | ||||
|     // keep the dialog in the center after a resize | ||||
|     if (lastSize != qSize) | ||||
|         this->move( | ||||
|             this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2, | ||||
|             this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2 | ||||
|         ); | ||||
| } | ||||
|  | ||||
| int ProgressDialog::execWithTask(Task* task) | ||||
| @@ -126,10 +136,8 @@ int ProgressDialog::execWithTask(Task* task) | ||||
|     connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled); | ||||
|  | ||||
|     m_is_multi_step = task->isMultiStep(); | ||||
|     if (!m_is_multi_step) { | ||||
|         ui->globalStatusLabel->setHidden(true); | ||||
|         ui->globalProgressBar->setHidden(true); | ||||
|     } | ||||
|     ui->taskProgressScrollArea->setHidden(!m_is_multi_step); | ||||
|     updateSize(); | ||||
|  | ||||
|     // It's a good idea to start the task after we entered the dialog's event loop :^) | ||||
|     if (!task->isRunning()) { | ||||
| @@ -139,6 +147,9 @@ int ProgressDialog::execWithTask(Task* task) | ||||
|         changeProgress(task->getProgress(), task->getTotalProgress()); | ||||
|     } | ||||
|  | ||||
|     // auto size_hint = ui->verticalLayout->sizeHint(); | ||||
|     // resize(size_hint.width(), size_hint.height()); | ||||
|  | ||||
|     return QDialog::exec(); | ||||
| } | ||||
|  | ||||
| @@ -189,40 +200,45 @@ void ProgressDialog::onTaskSucceeded() | ||||
| void ProgressDialog::changeStatus(const QString& status) | ||||
| { | ||||
|     ui->globalStatusLabel->setText(task->getStatus()); | ||||
|     // ui->statusLabel->setText(task->getStepStatus()); | ||||
|     ui->globalStatusDetailsLabel->setText(task->getDetails()); | ||||
|  | ||||
|     updateSize(); | ||||
| } | ||||
|  | ||||
| void ProgressDialog::addTaskProgress(TaskStepProgress progress) | ||||
| void ProgressDialog::addTaskProgress(TaskStepProgress* progress) | ||||
| { | ||||
|     SubTaskProgressBar* task_bar = new SubTaskProgressBar(this); | ||||
|     taskProgress.insert(progress.uid, task_bar); | ||||
|     ui->taskProgressLayout->addWidget(task_bar); | ||||
|     taskProgress.insert(progress->uid, task_bar); | ||||
|     ui->taskProgressLayout->insertWidget(0, task_bar); | ||||
| } | ||||
|  | ||||
| void ProgressDialog::changeStepProgress(QList<TaskStepProgress> task_progress) | ||||
| void ProgressDialog::changeStepProgress(TaskStepProgressList task_progress) | ||||
| { | ||||
|     m_is_multi_step = true; | ||||
|     ui->taskProgressScrollArea->setHidden(false); | ||||
|      | ||||
|     for (auto tp : task_progress) { | ||||
|         if (!taskProgress.contains(tp.uid)) | ||||
|             addTaskProgress(tp); | ||||
|         auto task_bar = taskProgress.value(tp.uid); | ||||
|         if (!taskProgress.contains(tp->uid)) | ||||
|             addTaskProgress(tp.get()); | ||||
|         auto task_bar = taskProgress.value(tp->uid); | ||||
|  | ||||
|         if (tp.total < 0) { | ||||
|         if (tp->total < 0) { | ||||
|             task_bar->setRange(0, 0); | ||||
|         } else { | ||||
|             task_bar->setRange(0, map_int_range<qint64>(tp.total)); | ||||
|             task_bar->setRange(0, map_int_range<qint64>(tp->total)); | ||||
|         } | ||||
|  | ||||
|         task_bar->setValue(map_int_range<qint64>(tp.current)); | ||||
|         task_bar->setStatus(tp.status); | ||||
|         task_bar->setDetails(tp.details); | ||||
|         task_bar->setValue(map_int_range<qint64>(tp->current)); | ||||
|         task_bar->setStatus(tp->status); | ||||
|         task_bar->setDetails(tp->details); | ||||
|  | ||||
|         if (tp.isDone()) { | ||||
|         if (tp->isDone()) { | ||||
|             task_bar->setVisible(false); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     updateSize(); | ||||
| } | ||||
|  | ||||
| void ProgressDialog::changeProgress(qint64 current, qint64 total) | ||||
| @@ -230,13 +246,6 @@ void ProgressDialog::changeProgress(qint64 current, qint64 total) | ||||
|     ui->globalProgressBar->setMaximum(total); | ||||
|     ui->globalProgressBar->setValue(current); | ||||
|  | ||||
|     // if (!m_is_multi_step) { | ||||
|     //     ui->taskProgressBar->setMaximum(total); | ||||
|     //     ui->taskProgressBar->setValue(current); | ||||
|     // } else { | ||||
|     //     ui->taskProgressBar->setMaximum(task->getStepProgress()); | ||||
|     //     ui->taskProgressBar->setValue(task->getStepTotalProgress()); | ||||
|     // } | ||||
| } | ||||
|  | ||||
| void ProgressDialog::keyPressEvent(QKeyEvent* e) | ||||
|   | ||||
| @@ -80,7 +80,7 @@ slots: | ||||
|  | ||||
|     void changeStatus(const QString &status); | ||||
|     void changeProgress(qint64 current, qint64 total); | ||||
|     void changeStepProgress(QList<TaskStepProgress> task_progress); | ||||
|     void changeStepProgress(TaskStepProgressList task_progress); | ||||
|  | ||||
|  | ||||
| private | ||||
| @@ -93,7 +93,7 @@ protected: | ||||
|  | ||||
| private: | ||||
|     bool handleImmediateResult(QDialog::DialogCode &result); | ||||
|     void addTaskProgress(TaskStepProgress progress); | ||||
|     void addTaskProgress(TaskStepProgress* progress); | ||||
|  | ||||
| private: | ||||
|     Ui::ProgressDialog *ui; | ||||
|   | ||||
| @@ -6,20 +6,20 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>400</width> | ||||
|     <height>109</height> | ||||
|     <width>600</width> | ||||
|     <height>260</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
|    <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> | ||||
|     <horstretch>0</horstretch> | ||||
|     <verstretch>0</verstretch> | ||||
|    <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|     <horstretch>1</horstretch> | ||||
|     <verstretch>1</verstretch> | ||||
|    </sizepolicy> | ||||
|   </property> | ||||
|   <property name="minimumSize"> | ||||
|    <size> | ||||
|     <width>400</width> | ||||
|     <height>0</height> | ||||
|     <width>600</width> | ||||
|     <height>260</height> | ||||
|    </size> | ||||
|   </property> | ||||
|   <property name="maximumSize"> | ||||
| @@ -31,21 +31,103 @@ | ||||
|   <property name="windowTitle"> | ||||
|    <string>Please wait...</string> | ||||
|   </property> | ||||
|   <layout class="QGridLayout" name="gridLayout"> | ||||
|    <item row="2" column="0"> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0"> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="globalStatusLabel"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="minimumSize"> | ||||
|         <size> | ||||
|          <width>0</width> | ||||
|          <height>15</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Global Task Status...</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="globalStatusDetailsLabel"> | ||||
|        <property name="text"> | ||||
|         <string>Global Status Details...</string> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QProgressBar" name="globalProgressBar"> | ||||
|      <property name="enabled"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|      <property name="minimumSize"> | ||||
|       <size> | ||||
|        <width>0</width> | ||||
|        <height>24</height> | ||||
|       </size> | ||||
|      </property> | ||||
|      <property name="value"> | ||||
|       <number>24</number> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="4" column="0"> | ||||
|    <item> | ||||
|     <widget class="QScrollArea" name="taskProgressScrollArea"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="minimumSize"> | ||||
|       <size> | ||||
|        <width>0</width> | ||||
|        <height>100</height> | ||||
|       </size> | ||||
|      </property> | ||||
|      <property name="frameShape"> | ||||
|       <enum>QFrame::StyledPanel</enum> | ||||
|      </property> | ||||
|      <property name="horizontalScrollBarPolicy"> | ||||
|       <enum>Qt::ScrollBarAsNeeded</enum> | ||||
|      </property> | ||||
|      <property name="sizeAdjustPolicy"> | ||||
|       <enum>QAbstractScrollArea::AdjustToContents</enum> | ||||
|      </property> | ||||
|      <property name="widgetResizable"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|      <widget class="QWidget" name="taskProgressContainer"> | ||||
|       <property name="geometry"> | ||||
|        <rect> | ||||
|         <x>0</x> | ||||
|         <y>0</y> | ||||
|         <width>584</width> | ||||
|         <height>146</height> | ||||
|        </rect> | ||||
|       </property> | ||||
|       <layout class="QVBoxLayout" name="taskProgressLayout"> | ||||
|        <property name="spacing"> | ||||
|         <number>2</number> | ||||
|        </property> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QPushButton" name="skipButton"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||||
|       <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
| @@ -55,22 +137,6 @@ | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="3" column="0"> | ||||
|     <layout class="QVBoxLayout" name="taskProgressLayout"/> | ||||
|    </item> | ||||
|    <item row="1" column="0"> | ||||
|     <widget class="QLabel" name="globalStatusLabel"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string>Global Task Status...</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|   | ||||
| @@ -6,12 +6,12 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>265</width> | ||||
|     <height>65</height> | ||||
|     <width>597</width> | ||||
|     <height>61</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
|    <sizepolicy hsizetype="MinimumExpanding" vsizetype="Ignored"> | ||||
|    <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> | ||||
|     <horstretch>0</horstretch> | ||||
|     <verstretch>0</verstretch> | ||||
|    </sizepolicy> | ||||
| @@ -20,29 +20,45 @@ | ||||
|    <string>Form</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0"> | ||||
|    <property name="spacing"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="statusLabel"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="font"> | ||||
|         <font> | ||||
|          <pointsize>8</pointsize> | ||||
|         </font> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Sub Task Status...</string> | ||||
|        </property> | ||||
|        <property name="wordWrap"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="statusDetailsLabel"> | ||||
|        <property name="sizePolicy"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> | ||||
|         <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|          <horstretch>0</horstretch> | ||||
|          <verstretch>0</verstretch> | ||||
|         </sizepolicy> | ||||
|        </property> | ||||
|        <property name="font"> | ||||
|         <font> | ||||
|          <pointsize>8</pointsize> | ||||
|         </font> | ||||
|        </property> | ||||
|        <property name="text"> | ||||
|         <string>Status Details</string> | ||||
|        </property> | ||||
| @@ -55,6 +71,11 @@ | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QProgressBar" name="progressBar"> | ||||
|      <property name="font"> | ||||
|       <font> | ||||
|        <pointsize>8</pointsize> | ||||
|       </font> | ||||
|      </property> | ||||
|      <property name="value"> | ||||
|       <number>24</number> | ||||
|      </property> | ||||
|   | ||||
| @@ -69,8 +69,9 @@ class BigConcurrentTaskThread : public QThread { | ||||
|         auto sub_tasks = new BasicTask::Ptr[s_num_tasks]; | ||||
|  | ||||
|         for (unsigned i = 0; i < s_num_tasks; i++) { | ||||
|             sub_tasks[i] = makeShared<BasicTask>(false); | ||||
|             big_task.addTask(sub_tasks[i]); | ||||
|             auto sub_task = makeShared<BasicTask>(false); | ||||
|             sub_tasks[i] = sub_task; | ||||
|             big_task.addTask(sub_task); | ||||
|         } | ||||
|  | ||||
|         big_task.run(); | ||||
| @@ -99,7 +100,7 @@ class TaskTest : public QObject { | ||||
|         t.setStatus(status); | ||||
|  | ||||
|         QCOMPARE(t.getStatus(), status); | ||||
|         QCOMPARE(t.getStepProgress().isEmpty(), QList<TaskStepProgress>{}.isEmpty()); | ||||
|         QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     void test_SetStatus_MultiStep(){ | ||||
| @@ -111,7 +112,7 @@ class TaskTest : public QObject { | ||||
|         QCOMPARE(t.getStatus(), status); | ||||
|         // Even though it is multi step, it does not override the getStepStatus method, | ||||
|         // so it should remain the same. | ||||
|         QCOMPARE(t.getStepProgress().isEmpty(), QList<TaskStepProgress>{}.isEmpty()); | ||||
|         QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); | ||||
|     } | ||||
|  | ||||
|     void test_SetProgress(){ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user