diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index e10a6206c..d479deec9 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -43,6 +43,7 @@ PyTypeObject **SbkPySide2_QtWidgetsTypes = NULL; #include #include #include +#include #include "PythonContext.h" #include "renderdoc_replay.h" @@ -382,6 +383,20 @@ PythonContext::PythonContext(QObject *parent) : QObject(parent) // release the GIL again PyGILState_Release(gil); + + // every 100ms while running, check for new output + outputTicker = new QTimer(this); + outputTicker->setInterval(100); + QObject::connect(outputTicker, &QTimer::timeout, this, &PythonContext::outputTick); + + // we have to start it here, because we can't start on another thread. + outputTicker->start(); +} + +PythonContext::~PythonContext() +{ + // do a final tick to gather any remaining output + outputTick(); } void PythonContext::Finish() @@ -450,6 +465,9 @@ void PythonContext::executeString(const QString &filename, const QString &source m_State = NULL; + // catch any output + outputTick(); + Py_XDECREF(thisobj); Py_XDECREF(traceContext); } @@ -587,6 +605,35 @@ PyObject *PythonContext::QtObjectToPython(const char *typeName, QObject *object) #endif } +// callback to flush output every so often (not constantly, to avoid spamming signals) +void PythonContext::outputTick() +{ + QMutexLocker lock(&outputMutex); + + if(!outstr.isEmpty()) + { + emit textOutput(false, outstr); + } + + if(!errstr.isEmpty()) + { + emit textOutput(true, errstr); + } + + outstr.clear(); + errstr.clear(); +} + +void PythonContext::addText(bool isStdError, const QString &output) +{ + QMutexLocker lock(&outputMutex); + + if(isStdError) + errstr += output; + else + outstr += output; +} + void PythonContext::setPyGlobal(const char *varName, PyObject *obj) { if(!initialised()) @@ -662,7 +709,7 @@ PyObject *PythonContext::outstream_write(PyObject *self, PyObject *args) if(context) { - emit context->textOutput(redirector->isStdError ? true : false, QString::fromUtf8(text)); + context->addText(redirector->isStdError ? true : false, QString::fromUtf8(text)); } } diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index 6a0c802e1..66b03ef76 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -44,7 +45,8 @@ private: // don't allow destruction from outside, you must heap-allocate the context and let it delete // itself when all references are done. This handles the case where e.g. some Async work is going // on and needs to finish executing after the external code is done with the context - ~PythonContext() {} + ~PythonContext(); + public: explicit PythonContext(QObject *parent = NULL); void Finish(); @@ -121,6 +123,13 @@ private: static PyObject *QtObjectToPython(const char *typeName, QObject *object); + QTimer *outputTicker; + QMutex outputMutex; + QString outstr, errstr; + + void outputTick(); + void addText(bool isStdError, const QString &output); + // Python callbacks static void outstream_del(PyObject *self); static PyObject *outstream_write(PyObject *self, PyObject *args);