2022-11-10 16:57:17 +01:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2022-11-10 16:57:17 +01:00
|
|
|
|
|
|
|
|
#include "taskprogress.h"
|
|
|
|
|
|
2022-11-25 11:35:15 +01:00
|
|
|
#include "futureprogress.h"
|
2022-11-10 16:57:17 +01:00
|
|
|
#include "progressmanager.h"
|
|
|
|
|
|
2022-11-25 18:34:28 +01:00
|
|
|
#include <utils/mathutils.h>
|
2022-11-10 16:57:17 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
#include <utils/tasktree.h>
|
|
|
|
|
|
|
|
|
|
#include <QFutureWatcher>
|
2022-11-25 09:52:13 +01:00
|
|
|
#include <QTimer>
|
2022-11-10 16:57:17 +01:00
|
|
|
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Core {
|
|
|
|
|
|
2022-11-25 09:52:13 +01:00
|
|
|
static const int ProgressResolution = 100; // 100 discrete values
|
2022-12-04 08:10:55 +01:00
|
|
|
static const int TimerInterval = 20; // 20 ms = 50 Hz
|
2022-11-25 09:52:13 +01:00
|
|
|
|
2022-11-10 16:57:17 +01:00
|
|
|
class TaskProgressPrivate : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit TaskProgressPrivate(TaskProgress *progress, TaskTree *taskTree);
|
|
|
|
|
~TaskProgressPrivate();
|
|
|
|
|
|
2022-11-25 09:52:13 +01:00
|
|
|
void advanceTick();
|
|
|
|
|
void advanceProgress(int newValue);
|
|
|
|
|
void updateProgress();
|
|
|
|
|
|
|
|
|
|
int m_currentTick = 0;
|
|
|
|
|
|
|
|
|
|
int m_currentProgress = 0; // from TaskTree (max value = task count)
|
|
|
|
|
|
2022-11-10 16:57:17 +01:00
|
|
|
TaskTree *m_taskTree = nullptr;
|
2022-11-25 09:52:13 +01:00
|
|
|
QTimer m_timer;
|
2022-11-10 16:57:17 +01:00
|
|
|
QFutureWatcher<void> m_watcher;
|
|
|
|
|
QFutureInterface<void> m_futureInterface;
|
2022-11-25 11:35:15 +01:00
|
|
|
QPointer<FutureProgress> m_futureProgress;
|
2022-12-04 08:10:55 +01:00
|
|
|
int m_halfLifeTimePerTask = 1000; // 1000 ms
|
2022-11-10 16:57:17 +01:00
|
|
|
QString m_displayName;
|
2022-11-25 11:35:15 +01:00
|
|
|
FutureProgress::KeepOnFinishType m_keep = FutureProgress::HideOnFinish;
|
|
|
|
|
bool m_isSubtitleVisibleInStatusBar = false;
|
|
|
|
|
QString m_subtitle;
|
2022-11-10 16:57:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TaskProgressPrivate::TaskProgressPrivate(TaskProgress *progress, TaskTree *taskTree)
|
|
|
|
|
: QObject(progress)
|
|
|
|
|
, m_taskTree(taskTree)
|
2022-11-25 09:52:13 +01:00
|
|
|
, m_timer(progress)
|
2022-11-10 16:57:17 +01:00
|
|
|
{
|
2022-11-25 09:52:13 +01:00
|
|
|
m_timer.setInterval(TimerInterval);
|
|
|
|
|
connect(&m_timer, &QTimer::timeout, &m_timer, [this] { advanceTick(); });
|
2022-11-10 16:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskProgressPrivate::~TaskProgressPrivate()
|
|
|
|
|
{
|
|
|
|
|
if (m_futureInterface.isRunning()) {
|
|
|
|
|
m_futureInterface.reportCanceled();
|
|
|
|
|
m_futureInterface.reportFinished();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-25 09:52:13 +01:00
|
|
|
void TaskProgressPrivate::advanceTick()
|
|
|
|
|
{
|
|
|
|
|
++m_currentTick;
|
|
|
|
|
updateProgress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskProgressPrivate::advanceProgress(int newValue)
|
|
|
|
|
{
|
|
|
|
|
m_currentProgress = newValue;
|
|
|
|
|
m_currentTick = 0;
|
|
|
|
|
updateProgress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskProgressPrivate::updateProgress()
|
|
|
|
|
{
|
2022-12-04 08:10:55 +01:00
|
|
|
const int halfLife = qRound(double(m_halfLifeTimePerTask) / TimerInterval);
|
2022-11-25 18:34:28 +01:00
|
|
|
const int pMin = ProgressResolution * m_currentProgress;
|
|
|
|
|
const int pMax = ProgressResolution * (m_currentProgress + 1);
|
2022-12-04 08:10:55 +01:00
|
|
|
const int newValue = MathUtils::interpolateExponential(m_currentTick, halfLife, pMin, pMax);
|
2022-11-25 18:34:28 +01:00
|
|
|
m_futureInterface.setProgressValue(newValue);
|
2022-11-25 09:52:13 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 16:57:17 +01:00
|
|
|
/*!
|
|
|
|
|
\class Core::TaskProgress
|
|
|
|
|
|
|
|
|
|
\brief The TaskProgress class is responsible for showing progress of the running task tree.
|
|
|
|
|
|
|
|
|
|
It's possible to cancel the running task tree automatically after pressing a small 'x'
|
|
|
|
|
indicator on progress panel. In this case TaskTree::stop() method is being called.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TaskProgress::TaskProgress(TaskTree *taskTree)
|
|
|
|
|
: QObject(taskTree)
|
|
|
|
|
, d(new TaskProgressPrivate(this, taskTree))
|
|
|
|
|
{
|
|
|
|
|
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, [this] {
|
|
|
|
|
d->m_taskTree->stop(); // TODO: should we have different cancel policies?
|
|
|
|
|
});
|
|
|
|
|
connect(d->m_taskTree, &TaskTree::started, this, [this] {
|
|
|
|
|
d->m_futureInterface = QFutureInterface<void>();
|
2022-11-25 09:52:13 +01:00
|
|
|
d->m_futureInterface.setProgressRange(
|
|
|
|
|
0, d->m_taskTree->progressMaximum() * ProgressResolution);
|
2022-11-10 16:57:17 +01:00
|
|
|
d->m_watcher.setFuture(d->m_futureInterface.future());
|
|
|
|
|
d->m_futureInterface.reportStarted();
|
2022-11-25 09:52:13 +01:00
|
|
|
d->advanceProgress(0);
|
2022-11-10 16:57:17 +01:00
|
|
|
|
|
|
|
|
const auto id = Id::fromString(d->m_displayName + ".action");
|
2022-11-25 11:35:15 +01:00
|
|
|
d->m_futureProgress = ProgressManager::addTask(d->m_futureInterface.future(),
|
|
|
|
|
d->m_displayName, id);
|
|
|
|
|
d->m_futureProgress->setKeepOnFinish(d->m_keep);
|
|
|
|
|
d->m_futureProgress->setSubtitleVisibleInStatusBar(d->m_isSubtitleVisibleInStatusBar);
|
|
|
|
|
d->m_futureProgress->setSubtitle(d->m_subtitle);
|
2022-11-25 09:52:13 +01:00
|
|
|
d->m_timer.start();
|
2022-11-10 16:57:17 +01:00
|
|
|
});
|
|
|
|
|
connect(d->m_taskTree, &TaskTree::progressValueChanged, this, [this](int value) {
|
2022-11-25 09:52:13 +01:00
|
|
|
d->advanceProgress(value);
|
2022-11-10 16:57:17 +01:00
|
|
|
});
|
|
|
|
|
connect(d->m_taskTree, &TaskTree::done, this, [this] {
|
2022-11-25 09:52:13 +01:00
|
|
|
d->m_timer.stop();
|
2022-11-10 16:57:17 +01:00
|
|
|
d->m_futureInterface.reportFinished();
|
|
|
|
|
});
|
|
|
|
|
connect(d->m_taskTree, &TaskTree::errorOccurred, this, [this] {
|
2022-11-25 09:52:13 +01:00
|
|
|
d->m_timer.stop();
|
2022-11-10 16:57:17 +01:00
|
|
|
d->m_futureInterface.reportCanceled();
|
|
|
|
|
d->m_futureInterface.reportFinished();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 16:12:55 +01:00
|
|
|
TaskProgress::~TaskProgress() = default;
|
|
|
|
|
|
2022-12-04 08:10:55 +01:00
|
|
|
void TaskProgress::setHalfLifeTimePerTask(int msecs)
|
|
|
|
|
{
|
|
|
|
|
d->m_halfLifeTimePerTask = msecs;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 16:57:17 +01:00
|
|
|
void TaskProgress::setDisplayName(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
d->m_displayName = name;
|
2022-11-25 11:35:15 +01:00
|
|
|
if (d->m_futureProgress)
|
|
|
|
|
d->m_futureProgress->setTitle(d->m_displayName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskProgress::setKeepOnFinish(FutureProgress::KeepOnFinishType keepType)
|
|
|
|
|
{
|
|
|
|
|
d->m_keep = keepType;
|
|
|
|
|
if (d->m_futureProgress)
|
|
|
|
|
d->m_futureProgress->setKeepOnFinish(d->m_keep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskProgress::setSubtitleVisibleInStatusBar(bool visible)
|
|
|
|
|
{
|
|
|
|
|
d->m_isSubtitleVisibleInStatusBar = visible;
|
|
|
|
|
if (d->m_futureProgress)
|
|
|
|
|
d->m_futureProgress->setSubtitleVisibleInStatusBar(d->m_isSubtitleVisibleInStatusBar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskProgress::setSubtitle(const QString &subtitle)
|
|
|
|
|
{
|
|
|
|
|
d->m_subtitle = subtitle;
|
|
|
|
|
if (d->m_futureProgress)
|
|
|
|
|
d->m_futureProgress->setSubtitle(d->m_subtitle);
|
2022-11-10 16:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Core
|