Introduce ProcessProgress

This is extracted separate object out of VcsCommand.
It's responsible for showing progress of the running
process. It's able to cancel the running process
automatically after pressing a small 'x' indicator on
progress panel. In this case the QtcProcess::stop()
method is being called.

Change-Id: I9fa94fd047638f76909356ae4023852349be3a06
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2022-10-07 14:59:41 +02:00
parent 907c36217d
commit 07ca7f7b99
6 changed files with 177 additions and 0 deletions

View File

@@ -1750,6 +1750,11 @@ void QtcProcess::setTimeoutS(int timeoutS)
d->m_maxHangTimerCount = INT_MAX / 1000;
}
int QtcProcess::timeoutS() const
{
return d->m_maxHangTimerCount;
}
void QtcProcess::setCodec(QTextCodec *c)
{
QTC_ASSERT(c, return);

View File

@@ -142,6 +142,7 @@ public:
/* Timeout for hanging processes (triggers after no more output
* occurs on stderr/stdout). */
void setTimeoutS(int timeoutS);
int timeoutS() const;
// TODO: We should specify the purpose of the codec, e.g. setCodecForStandardChannel()
void setCodec(QTextCodec *c);

View File

@@ -140,6 +140,7 @@ add_qtc_plugin(Core
plugindialog.cpp plugindialog.h
plugininstallwizard.cpp plugininstallwizard.h
progressmanager/futureprogress.cpp progressmanager/futureprogress.h
progressmanager/processprogress.cpp progressmanager/processprogress.h
progressmanager/progressbar.cpp progressmanager/progressbar.h
progressmanager/progressmanager.cpp progressmanager/progressmanager.h progressmanager/progressmanager_p.h
progressmanager/progressview.cpp progressmanager/progressview.h

View File

@@ -239,6 +239,7 @@ Project {
prefix: "progressmanager/"
files: [
"futureprogress.cpp", "futureprogress.h",
"processprogress.cpp", "processprogress.h",
"progressbar.cpp", "progressbar.h",
"progressmanager.cpp", "progressmanager.h", "progressmanager_p.h",
"progressview.cpp", "progressview.h",

View File

@@ -0,0 +1,134 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "processprogress.h"
#include "coreplugintr.h"
#include "progressmanager.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QFutureWatcher>
using namespace Utils;
namespace Core {
class ProcessProgressPrivate : public QObject
{
public:
explicit ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process);
~ProcessProgressPrivate();
QString displayName() const;
void parseProgress(const QString &inputText);
QtcProcess *m_process = nullptr;
ProgressParser m_parser = {};
QFutureWatcher<void> m_watcher;
QFutureInterface<void> m_futureInterface;
QString m_displayName;
};
ProcessProgressPrivate::ProcessProgressPrivate(ProcessProgress *progress, QtcProcess *process)
: QObject(progress)
, m_process(process)
{
}
ProcessProgressPrivate::~ProcessProgressPrivate()
{
if (m_futureInterface.isRunning()) {
m_futureInterface.reportCanceled();
m_futureInterface.reportFinished();
// TODO: should we stop the process? Or just mark the process canceled?
// What happens to task in progress manager?
}
}
QString ProcessProgressPrivate::displayName() const
{
if (!m_displayName.isEmpty())
return m_displayName;
const CommandLine commandLine = m_process->commandLine();
QString result = commandLine.executable().baseName();
QTC_ASSERT(!result.isEmpty(), result = Tr::tr("Unknown"));
result[0] = result[0].toTitleCase();
if (!commandLine.arguments().isEmpty())
result += ' ' + commandLine.splitArguments().at(0);
return result;
}
void ProcessProgressPrivate::parseProgress(const QString &inputText)
{
QTC_ASSERT(m_parser, return);
m_parser(m_futureInterface, inputText);
}
/*!
\class Core::ProcessProgress
\brief The ProcessProgress class is responsible for showing progress of the running process.
It's able to cancel the running process automatically after pressing a small 'x' indicator on
progress panel. In this case the QtcProcess::stop() method is being called.
*/
ProcessProgress::ProcessProgress(QtcProcess *process)
: QObject(process)
, d(new ProcessProgressPrivate(this, process))
{
connect(&d->m_watcher, &QFutureWatcher<void>::canceled, this, [this] {
d->m_process->stop(); // TODO: should we have different cancel policies?
});
connect(d->m_process, &QtcProcess::starting, this, [this] {
d->m_futureInterface = QFutureInterface<void>();
d->m_futureInterface.setProgressRange(0, 1);
d->m_watcher.setFuture(d->m_futureInterface.future());
d->m_futureInterface.reportStarted();
const QString name = d->displayName();
const auto id = Id::fromString(name + ".action");
if (d->m_parser) {
ProgressManager::addTask(d->m_futureInterface.future(), name, id);
} else {
ProgressManager::addTimedTask(d->m_futureInterface, name, id,
qMax(2, d->m_process->timeoutS() / 5));
}
});
connect(d->m_process, &QtcProcess::done, this, [this] {
if (d->m_process->result() != ProcessResult::FinishedWithSuccess)
d->m_futureInterface.reportCanceled();
d->m_futureInterface.reportFinished();
});
}
void ProcessProgress::setDisplayName(const QString &name)
{
d->m_displayName = name;
}
void ProcessProgress::setProgressParser(const ProgressParser &parser)
{
if (d->m_parser) {
disconnect(d->m_process, &QtcProcess::textOnStandardOutput,
d, &ProcessProgressPrivate::parseProgress);
disconnect(d->m_process, &QtcProcess::textOnStandardError,
d, &ProcessProgressPrivate::parseProgress);
}
d->m_parser = parser;
if (!d->m_parser)
return;
QTC_ASSERT(d->m_process->textChannelMode(Channel::Output) != TextChannelMode::Off,
qWarning() << "Setting progress parser on a process without changing process' "
"text channel mode is no-op.");
connect(d->m_process, &QtcProcess::textOnStandardOutput,
d, &ProcessProgressPrivate::parseProgress);
connect(d->m_process, &QtcProcess::textOnStandardError,
d, &ProcessProgressPrivate::parseProgress);
}
} // namespace Core

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <coreplugin/core_global.h>
#include <QObject>
QT_BEGIN_NAMESPACE
template <typename T>
class QFutureInterface;
QT_END_NAMESPACE
namespace Utils { class QtcProcess; }
namespace Core {
using ProgressParser = std::function<void(QFutureInterface<void> &, const QString &)>;
class ProcessProgressPrivate;
class CORE_EXPORT ProcessProgress : public QObject
{
public:
ProcessProgress(Utils::QtcProcess *process); // Makes ProcessProgress a child of process
void setDisplayName(const QString &name);
void setProgressParser(const ProgressParser &parser);
private:
ProcessProgressPrivate *d;
};
} // namespace Core