From 07ca7f7b990ca5343fb100910ed9d2006f4dbe3d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 7 Oct 2022 14:59:41 +0200 Subject: [PATCH] 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 --- src/libs/utils/qtcprocess.cpp | 5 + src/libs/utils/qtcprocess.h | 1 + src/plugins/coreplugin/CMakeLists.txt | 1 + src/plugins/coreplugin/coreplugin.qbs | 1 + .../progressmanager/processprogress.cpp | 134 ++++++++++++++++++ .../progressmanager/processprogress.h | 35 +++++ 6 files changed, 177 insertions(+) create mode 100644 src/plugins/coreplugin/progressmanager/processprogress.cpp create mode 100644 src/plugins/coreplugin/progressmanager/processprogress.h diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index e2e67b7d8da..d04da95e923 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -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); diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 2754e407f5b..966288efa4e 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -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); diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index feade344647..29542994ce0 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -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 diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index 3b2e9e8dc5a..7bd810e1c47 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -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", diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp new file mode 100644 index 00000000000..af27b49e8ec --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp @@ -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 +#include + +#include + +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 m_watcher; + QFutureInterface 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::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(); + 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 diff --git a/src/plugins/coreplugin/progressmanager/processprogress.h b/src/plugins/coreplugin/progressmanager/processprogress.h new file mode 100644 index 00000000000..4d80a99bd80 --- /dev/null +++ b/src/plugins/coreplugin/progressmanager/processprogress.h @@ -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 + +#include + +QT_BEGIN_NAMESPACE +template +class QFutureInterface; +QT_END_NAMESPACE + +namespace Utils { class QtcProcess; } + +namespace Core { + +using ProgressParser = std::function &, 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