From 133b3b58a70fcbd1cd440cb34e54c6a4b180f603 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 4 Jun 2021 11:40:35 +0200 Subject: [PATCH] Utils: Provide line based callback handling Change-Id: Ibb1df93b457d299c377472ff6c0fbd719554d7d8 Reviewed-by: hjk --- src/libs/utils/qtcprocess.cpp | 37 ++++++++++++++++++++++++++++++++++- src/libs/utils/qtcprocess.h | 2 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index f53a63d3b67..883c66c0b36 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -82,6 +82,7 @@ public: void clearForRun(); QString linesRead(); + Utils::optional takeFirstLine(); void append(const QByteArray &text); QByteArray rawData; @@ -90,6 +91,7 @@ public: std::unique_ptr codecState; int rawDataPos = 0; std::function outputCallback; + std::function outputLineCallback; }; class ProcessHelper : public QProcess @@ -739,10 +741,13 @@ QString QtcProcess::locateBinary(const QString &binary) event loop that blocks only user input events. Thus, it allows for the GUI to repaint and append output to log windows. - The callbacks set with setStdOutCallBack(), setStdErrCallback() are called + The callbacks set with setStdOutCallback(), setStdErrCallback() are called with complete lines based on the '\\n' marker. They would typically be used for log windows. + Alternatively you can used setStdOutLineCallback() and setStdErrLineCallback() + to process the output line by line. + There is a timeout handling that takes effect after the last data have been read from stdout/stdin (as opposed to waitForFinished(), which measures time since it was invoked). It is thus also suitable for slow processes that @@ -866,6 +871,18 @@ QString ChannelBuffer::linesRead() return lines; } +// Check for first complete line inside the rawData and return it, removing the line from the buffer +Utils::optional ChannelBuffer::takeFirstLine() +{ + const int firstLineEnd = qMax(rawData.indexOf('\n'), rawData.indexOf('r')); + if (firstLineEnd == -1) + return Utils::nullopt; + + const QString line = QString::fromUtf8(rawData.left(firstLineEnd)); + rawData.remove(0, firstLineEnd + 1); + return Utils::make_optional(line); +} + void ChannelBuffer::append(const QByteArray &text) { if (text.isEmpty()) @@ -878,6 +895,14 @@ void ChannelBuffer::append(const QByteArray &text) if (!lines.isEmpty()) outputCallback(lines); } + if (outputLineCallback) { + do { + const Utils::optional line = takeFirstLine(); + if (!line.has_value()) + break; + outputLineCallback(line.value()); + } while (true); + } } @@ -1022,12 +1047,22 @@ void QtcProcess::setStdOutCallback(const std::function & d->m_stdOut.outputCallback = callback; } +void QtcProcess::setStdOutLineCallback(const std::function &callback) +{ + d->m_stdOut.outputLineCallback = callback; +} + void QtcProcess::setStdErrCallback(const std::function &callback) { QTC_CHECK(d->m_isSynchronousProcess); d->m_stdErr.outputCallback = callback; } +void QtcProcess::setStdErrLineCallback(const std::function &callback) +{ + d->m_stdErr.outputLineCallback = callback; +} + void QtcProcessPrivate::slotTimeout() { if (!m_waitingForUser && (++m_hangTimerCount > m_maxHangTimerCount)) { diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index e67fb62ab73..37ac0ee250e 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -108,7 +108,9 @@ public: void setWriteData(const QByteArray &writeData); void setStdOutCallback(const std::function &callback); + void setStdOutLineCallback(const std::function &callback); void setStdErrCallback(const std::function &callback); + void setStdErrLineCallback(const std::function &callback); static void setRemoteProcessHooks(const DeviceProcessHooks &hooks);