From 3c6dfd23b405f77c2e8558aec29c7db35750051a Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 20 Mar 2026 16:57:13 +0000 Subject: [PATCH] Add a drop-down for the script output panel to switch context * By default we still display all messages, but this allows users to switch to just seeing the output from one extension if desired. --- qrenderdoc/Code/pyrenderdoc/PythonContext.cpp | 6 +- qrenderdoc/Code/pyrenderdoc/PythonContext.h | 2 + qrenderdoc/Windows/PythonShell.cpp | 102 +++++++++++++++--- qrenderdoc/Windows/PythonShell.h | 14 +++ qrenderdoc/Windows/PythonShell.ui | 48 +++++++++ 5 files changed, 158 insertions(+), 14 deletions(-) diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index ed970e60b..932d7caed 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -1311,7 +1311,11 @@ QString PythonContext::LoadExtension(ICaptureContext &ctx, const rdcstr &extensi ext = NULL; } - if(!ext) + if(ext) + { + emit m_ExtensionContext->extensionLoaded(extension); + } + else { if(typeStr.isEmpty()) FetchException(typeStr, valueStr, finalLine, frames); diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index 3f7f4876c..27af1f8b5 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -124,6 +124,8 @@ signals: QList frames); void textOutput(const QString &extension, bool isStdError, const QString &output); + void extensionLoaded(const QString &extension); + public slots: void executeString(const QString &source); void executeString(const QString &filename, const QString &source, bool debugging); diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 540921337..23b990669 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -34,6 +34,13 @@ #include "scintilla/include/qt/ScintillaEdit.h" #include "ui_PythonShell.h" +enum +{ + AllOutputFilter, + ScriptOutputFilter, + FirstExtensionOutputFilter, +}; + // a forwarder that invokes onto the UI thread wherever necessary. // Note this does NOT make CaptureContext thread safe. We just invoke for any potentially UI // operations. All invokes are blocking, so there can't be any times when the UI thread waits @@ -1052,10 +1059,27 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent) enableButtons(true); + Q_ASSERT(ui->outputContext->count() == AllOutputFilter); + ui->outputContext->addItem(tr("All")); + Q_ASSERT(ui->outputContext->count() == ScriptOutputFilter); + ui->outputContext->addItem(tr("Script")); + Q_ASSERT(ui->outputContext->count() == FirstExtensionOutputFilter); + + for(const ExtensionMetadata &e : m_Ctx.Extensions().GetInstalledExtensions()) + { + if(m_Ctx.Extensions().IsExtensionLoaded(e.package)) + { + ui->outputContext->addItem(tr("Extension %1").arg(e.package)); + loadedExtensions.push_back(e.package); + } + } + QObject::connect(PythonContext::GetExtensionContext(), &PythonContext::textOutput, this, &PythonShell::textOutput); QObject::connect(PythonContext::GetExtensionContext(), &PythonContext::exception, this, &PythonShell::exception); + QObject::connect(PythonContext::GetExtensionContext(), &PythonContext::extensionLoaded, this, + &PythonShell::extensionLoaded); // reset output to default on_clear_clicked(); @@ -1125,7 +1149,9 @@ void PythonShell::runScript(bool debugging) ToolWindowManager::raiseToolWindow(ui->outputGroup); - ui->scriptOutput->clear(); + scriptOutputLines.removeIf([](const ScriptOutputLine &l) { return l.extension.isEmpty(); }); + + updateScriptOutput(true); QString script = QString::fromUtf8(scriptEditor->getText(scriptEditor->textLength() + 1)); @@ -1290,17 +1316,11 @@ void PythonShell::traceLine(const QString &file, int line) void PythonShell::exception(const QString &extension, const QString &type, const QString &value, int finalLine, QList frames) { - QTextEdit *out = ui->scriptOutput; - if(QObject::sender() == (QObject *)interactiveContext) - out = ui->interactiveOutput; - - QString exString; - if(finalLine >= 0) traceLine(QString(), finalLine); - if(!out->toPlainText().endsWith(QLatin1Char('\n'))) - exString = lit("\n"); + QString exString; + if(!frames.isEmpty()) { exString += tr("Traceback (most recent call last):\n"); @@ -1309,16 +1329,72 @@ void PythonShell::exception(const QString &extension, const QString &type, const } exString += QFormatStr("%1: %2\n").arg(type).arg(value); - appendText(out, exString); + if(QObject::sender() == (QObject *)interactiveContext) + { + appendText(ui->interactiveOutput, exString); + return; + } + + exString.insert(0, QLatin1Char('\n')); + + scriptOutputLines.push_back({extension, exString}); + + updateScriptOutput(false); } void PythonShell::textOutput(const QString &extension, bool isStdError, const QString &output) { - QTextEdit *out = ui->scriptOutput; if(QObject::sender() == (QObject *)interactiveContext) - out = ui->interactiveOutput; + { + appendText(ui->interactiveOutput, output); + return; + } - appendText(out, output); + scriptOutputLines.push_back({extension, output}); + updateScriptOutput(false); +} + +void PythonShell::on_outputContext_currentIndexChanged(int idx) +{ + updateScriptOutput(true); +} + +void PythonShell::updateScriptOutput(bool fullRefresh) +{ + if(fullRefresh) + { + ui->scriptOutput->clear(); + lastDisplayedLine = 0; + } + + for(size_t i = lastDisplayedLine; i < scriptOutputLines.size(); i++) + { + bool display = false; + + if(ui->outputContext->currentIndex() == AllOutputFilter) + display = true; + + // Script + else if(ui->outputContext->currentIndex() == ScriptOutputFilter) + display = scriptOutputLines[i].extension.isEmpty(); + + else if(loadedExtensions.indexOf(scriptOutputLines[i].extension) + FirstExtensionOutputFilter == + ui->outputContext->currentIndex()) + display = true; + + if(display) + { + appendText(ui->scriptOutput, scriptOutputLines[i].text); + } + } + + lastDisplayedLine = scriptOutputLines.size(); +} + +void PythonShell::extensionLoaded(const QString &extension) +{ + ui->outputContext->addItem(tr("Extension %1").arg(extension)); + loadedExtensions.push_back(extension); } void PythonShell::editor_contextMenu(const QPoint &pos) diff --git a/qrenderdoc/Windows/PythonShell.h b/qrenderdoc/Windows/PythonShell.h index 51ccc5fe2..a73961b60 100644 --- a/qrenderdoc/Windows/PythonShell.h +++ b/qrenderdoc/Windows/PythonShell.h @@ -73,6 +73,7 @@ private slots: void on_runScript_clicked(); void on_debugScript_clicked(); void on_abortRun_clicked(); + void on_outputContext_currentIndexChanged(int idx); // manual slots void interactive_keypress(QKeyEvent *e); @@ -81,6 +82,7 @@ private slots: void exception(const QString &extension, const QString &type, const QString &value, int finalLine, QList frames); void textOutput(const QString &extension, bool isStdError, const QString &output); + void extensionLoaded(const QString &extension); void editor_contextMenu(const QPoint &pos); private: @@ -99,6 +101,18 @@ private: QString m_storedLines; + struct ScriptOutputLine + { + QString extension; + QString text; + }; + + rdcarray loadedExtensions; + rdcarray scriptOutputLines; + size_t lastDisplayedLine = 0; + + void updateScriptOutput(bool fullRefresh); + QString getDottedWordAtPoint(int scintillaPos); PythonContext *newContext(); diff --git a/qrenderdoc/Windows/PythonShell.ui b/qrenderdoc/Windows/PythonShell.ui index 7ead69759..2cd282e91 100644 --- a/qrenderdoc/Windows/PythonShell.ui +++ b/qrenderdoc/Windows/PythonShell.ui @@ -344,6 +344,54 @@ 4 + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Output from: + + + + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::ScrollBarAlwaysOn