diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index b07a123f9..adac1ad41 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -42,6 +42,7 @@ #include "Windows/MainWindow.h" #include "Windows/PipelineState/PipelineStateViewer.h" #include "Windows/PixelHistoryView.h" +#include "Windows/PythonShell.h" #include "Windows/ShaderViewer.h" #include "Windows/StatisticsViewer.h" #include "Windows/TextureViewer.h" @@ -732,6 +733,18 @@ IStatisticsViewer *CaptureContext::GetStatisticsViewer() return m_StatisticsViewer; } +IPythonShell *CaptureContext::GetPythonShell() +{ + if(m_PythonShell) + return m_PythonShell; + + m_PythonShell = new PythonShell(*this, m_MainWindow); + m_PythonShell->setObjectName("pythonShell"); + setupDockWindow(m_PythonShell); + + return m_PythonShell; +} + void CaptureContext::ShowEventBrowser() { m_MainWindow->showEventBrowser(); @@ -772,6 +785,11 @@ void CaptureContext::ShowStatisticsViewer() m_MainWindow->showStatisticsViewer(); } +void CaptureContext::ShowPythonShell() +{ + m_MainWindow->showPythonShell(); +} + IShaderViewer *CaptureContext::EditShader(bool customShader, const QString &entryPoint, const QStringMap &files, IShaderViewer::SaveCallback saveCallback, @@ -865,6 +883,10 @@ QWidget *CaptureContext::CreateBuiltinWindow(const QString &objectName) { return GetStatisticsViewer()->Widget(); } + else if(objectName == "pythonShell") + { + return GetPythonShell()->Widget(); + } return NULL; } @@ -887,6 +909,8 @@ void CaptureContext::BuiltinWindowClosed(QWidget *window) m_DebugMessageView = NULL; else if(m_StatisticsViewer && m_StatisticsViewer->Widget() == window) m_StatisticsViewer = NULL; + else if(m_PythonShell && m_PythonShell->Widget() == window) + m_PythonShell = NULL; else qCritical() << "Unrecognised window being closed: " << window; } diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 4ded8ae3c..ebd2596ad 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -46,6 +46,7 @@ class TextureViewer; class CaptureDialog; class DebugMessageView; class StatisticsViewer; +class PythonShell; QString ConfigFilePath(const QString &filename); @@ -128,6 +129,7 @@ public: ICaptureDialog *GetCaptureDialog() override; IDebugMessageView *GetDebugMessageView() override; IStatisticsViewer *GetStatisticsViewer() override; + IPythonShell *GetPythonShell() override; bool HasEventBrowser() override { return m_EventBrowser != NULL; } bool HasAPIInspector() override { return m_APIInspector != NULL; } @@ -137,6 +139,7 @@ public: bool HasCaptureDialog() override { return m_CaptureDialog != NULL; } bool HasDebugMessageView() override { return m_DebugMessageView != NULL; } bool HasStatisticsViewer() override { return m_StatisticsViewer != NULL; } + bool HasPythonShell() override { return m_PythonShell != NULL; } void ShowEventBrowser() override; void ShowAPIInspector() override; void ShowTextureViewer() override; @@ -145,6 +148,7 @@ public: void ShowCaptureDialog() override; void ShowDebugMessageView() override; void ShowStatisticsViewer() override; + void ShowPythonShell() override; IShaderViewer *EditShader(bool customShader, const QString &entryPoint, const QStringMap &files, IShaderViewer::SaveCallback saveCallback, @@ -265,4 +269,5 @@ private: CaptureDialog *m_CaptureDialog = NULL; DebugMessageView *m_DebugMessageView = NULL; StatisticsViewer *m_StatisticsViewer = NULL; + PythonShell *m_PythonShell = NULL; }; diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index a55dee86d..5eb9d3ee3 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -318,6 +318,21 @@ protected: DECLARE_REFLECTION_STRUCT(IStatisticsViewer); +DOCUMENT("The interactive python shell."); +struct IPythonShell +{ + DOCUMENT( + "Retrieves the QWidget for this :class:`PythonShell` if PySide2 is available, or " + "``None``."); + virtual QWidget *Widget() = 0; + +protected: + IPythonShell() = default; + ~IPythonShell() = default; +}; + +DECLARE_REFLECTION_STRUCT(IPythonShell); + DOCUMENT(R"(A shader window used for viewing, editing, or debugging. .. function:: SaveCallback(context, viewer, files) @@ -990,6 +1005,13 @@ as well as messages generated during replay and analysis. )"); virtual IStatisticsViewer *GetStatisticsViewer() = 0; + DOCUMENT(R"(Retrieve the current singleton :class:`PythonShell`. + +:return: The current window, which is created (but not shown) it there wasn't one open. +:rtype: PythonShell +)"); + virtual IPythonShell *GetPythonShell() = 0; + DOCUMENT(R"(Check if there is a current :class:`EventBrowser` open. :return: ``True`` if there is a window open. @@ -1046,6 +1068,13 @@ as well as messages generated during replay and analysis. )"); virtual bool HasStatisticsViewer() = 0; + DOCUMENT(R"(Check if there is a current :class:`PythonShell` open. + +:return: ``True`` if there is a window open. +:rtype: ``bool`` +)"); + virtual bool HasPythonShell() = 0; + DOCUMENT("Raise the current :class:`EventBrowser`, showing it in the default place if needed."); virtual void ShowEventBrowser() = 0; DOCUMENT("Raise the current :class:`APIInspector`, showing it in the default place if needed."); @@ -1064,6 +1093,8 @@ as well as messages generated during replay and analysis. DOCUMENT( "Raise the current :class:`StatisticsViewer`, showing it in the default place if needed."); virtual void ShowStatisticsViewer() = 0; + DOCUMENT("Raise the current :class:`PythonShell`, showing it in the default place if needed."); + virtual void ShowPythonShell() = 0; DOCUMENT(R"(Show a new :class:`ShaderViewer` window, showing an editable view of a given shader. diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp index d479deec9..5f1013fed 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.cpp @@ -424,6 +424,11 @@ void PythonContext::GlobalShutdown() Py_Finalize(); } +QString PythonContext::versionString() +{ + return QString("%1.%2.%3").arg(PY_MAJOR_VERSION).arg(PY_MINOR_VERSION).arg(PY_MICRO_VERSION); +} + void PythonContext::executeString(const QString &filename, const QString &source, bool interactive) { if(!initialised()) diff --git a/qrenderdoc/Code/pyrenderdoc/PythonContext.h b/qrenderdoc/Code/pyrenderdoc/PythonContext.h index 66b03ef76..17cbed5f6 100644 --- a/qrenderdoc/Code/pyrenderdoc/PythonContext.h +++ b/qrenderdoc/Code/pyrenderdoc/PythonContext.h @@ -55,6 +55,8 @@ public: static void GlobalInit(); static void GlobalShutdown(); + QString versionString(); + template void setGlobal(const char *varName, T *object) { diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index f357fbfdc..ac6828a91 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -1399,6 +1399,16 @@ void MainWindow::on_action_Statistics_Viewer_triggered() ui->toolWindowManager->addToolWindow(stats, mainToolArea()); } +void MainWindow::on_action_Python_Shell_triggered() +{ + QWidget *py = m_Ctx.GetPythonShell()->Widget(); + + if(ui->toolWindowManager->toolWindows().contains(py)) + ToolWindowManager::raiseToolWindow(py); + else + ui->toolWindowManager->addToolWindow(py, mainToolArea()); +} + void MainWindow::on_action_Resolve_Symbols_triggered() { m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { r->InitResolver(); }); diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index 53d89a12f..6999152e8 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -89,6 +89,7 @@ public: void showCaptureDialog() { on_action_Capture_Log_triggered(); } void showDebugMessageView() { on_action_Errors_and_Warnings_triggered(); } void showStatisticsViewer() { on_action_Statistics_Viewer_triggered(); } + void showPythonShell() { on_action_Python_Shell_triggered(); } private slots: // automatic slots void on_action_Exit_triggered(); @@ -104,6 +105,7 @@ private slots: void on_action_Capture_Log_triggered(); void on_action_Errors_and_Warnings_triggered(); void on_action_Statistics_Viewer_triggered(); + void on_action_Python_Shell_triggered(); void on_action_Inject_into_Process_triggered(); void on_action_Resolve_Symbols_triggered(); void on_action_Attach_to_Running_Instance_triggered(); diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp new file mode 100644 index 000000000..68ccb61fb --- /dev/null +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -0,0 +1,343 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "PythonShell.h" +#include +#include +#include +#include "3rdparty/scintilla/include/SciLexer.h" +#include "3rdparty/scintilla/include/qt/ScintillaEdit.h" +#include "Code/ScintillaSyntax.h" +#include "Code/pyrenderdoc/PythonContext.h" +#include "ui_PythonShell.h" + +PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent) + : QFrame(parent), ui(new Ui::PythonShell), m_Ctx(ctx) +{ + ui->setupUi(this); + + QObject::connect(ui->lineInput, &RDLineEdit::keyPress, this, &PythonShell::interactive_keypress); + + ui->lineInput->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + ui->interactiveOutput->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + ui->scriptOutput->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + + scriptEditor = new ScintillaEdit(this); + + scriptEditor->setMarginLeft(4); + scriptEditor->setMarginWidthN(0, 32); + scriptEditor->setMarginWidthN(1, 0); + scriptEditor->setMarginWidthN(2, 16); + scriptEditor->setObjectName("scriptEditor"); + + scriptEditor->markerSetBack(CURRENT_MARKER, SCINTILLA_COLOUR(240, 128, 128)); + scriptEditor->markerSetBack(CURRENT_MARKER + 1, SCINTILLA_COLOUR(240, 128, 128)); + scriptEditor->markerDefine(CURRENT_MARKER, SC_MARK_SHORTARROW); + scriptEditor->markerDefine(CURRENT_MARKER + 1, SC_MARK_BACKGROUND); + + ConfigureSyntax(scriptEditor, SCLEX_PYTHON); + + scriptEditor->setTabWidth(4); + + scriptEditor->setScrollWidth(1); + scriptEditor->setScrollWidthTracking(true); + + scriptEditor->colourise(0, -1); + + QObject::connect(scriptEditor, &ScintillaEdit::modified, [this](int type, int, int, int, + const QByteArray &, int, int, int) { + if(type & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT | SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) + { + scriptEditor->markerDeleteAll(CURRENT_MARKER); + scriptEditor->markerDeleteAll(CURRENT_MARKER + 1); + } + }); + + ui->scriptSplitter->insertWidget(0, scriptEditor); + int w = ui->scriptSplitter->rect().width(); + ui->scriptSplitter->setSizes({w * 2 / 3, w / 3}); + + ui->tabWidget->setCurrentIndex(0); + + interactiveContext = NULL; + + enableButtons(true); + + // reset output to default + on_clear_clicked(); + on_newScript_clicked(); +} + +PythonShell::~PythonShell() +{ + m_Ctx.BuiltinWindowClosed(this); + + interactiveContext->Finish(); + + delete ui; +} + +void PythonShell::on_execute_clicked() +{ + QString command = ui->lineInput->text(); + + appendText(ui->interactiveOutput, command + "\n"); + + if(command.trimmed().length() > 0) + interactiveContext->executeString(command, true); + + history.push_front(command); + historyidx = -1; + + ui->lineInput->clear(); + + appendText(ui->interactiveOutput, ">> "); +} + +void PythonShell::on_clear_clicked() +{ + QString minidocHeader = scriptHeader(); + + minidocHeader += "\n\n>> "; + + ui->interactiveOutput->setText(minidocHeader); + + if(interactiveContext) + interactiveContext->Finish(); + + interactiveContext = newContext(); +} + +void PythonShell::on_newScript_clicked() +{ + QString minidocHeader = scriptHeader(); + + minidocHeader.replace('\n', "\n# "); + + minidocHeader = "# " + minidocHeader + "\n\n"; + + scriptEditor->setText(minidocHeader.toUtf8().data()); + + scriptEditor->emptyUndoBuffer(); +} + +void PythonShell::on_openScript_clicked() +{ + QString filename = + RDDialog::getOpenFileName(this, "Open Python Script", "", tr("Python scripts (*.py)")); + + if(filename != "") + { + QFile f(filename); + if(f.open(QIODevice::ReadOnly | QIODevice::Text)) + { + scriptEditor->setText(f.readAll().data()); + } + else + { + RDDialog::critical(this, "Error loading script", tr("Couldn't open path %1.").arg(filename)); + } + } +} + +void PythonShell::on_saveScript_clicked() +{ + QString filename = RDDialog::getSaveFileName(this, tr("Save Python Script"), QString(), + tr("Python scripts (*.py)")); + + if(filename != "") + { + QDir dirinfo = QFileInfo(filename).dir(); + if(dirinfo.exists()) + { + QFile f(filename); + if(f.open(QIODevice::WriteOnly | QIODevice::Truncate)) + { + f.write(scriptEditor->getText(scriptEditor->textLength() + 1)); + } + else + { + RDDialog::critical( + this, tr("Error saving script"), + tr("Couldn't open path %1 for write.\n%2").arg(filename).arg(f.errorString())); + } + } + else + { + RDDialog::critical(this, tr("Invalid directory"), + tr("Cannot find target directory to save to")); + } + } +} + +void PythonShell::on_runScript_clicked() +{ + PythonContext *context = newContext(); + + ui->scriptOutput->clear(); + + QString script = QString::fromUtf8(scriptEditor->getText(scriptEditor->textLength() + 1)); + + enableButtons(false); + + LambdaThread *thread = new LambdaThread([this, script, context]() { + + scriptContext = context; + context->executeString("script.py", script); + scriptContext = NULL; + + context->Finish(); + + GUIInvoke::call([this]() { enableButtons(true); }); + }); + + thread->selfDelete(true); + thread->start(); +} + +void PythonShell::on_abortRun_clicked() +{ + if(scriptContext) + scriptContext->abort(); +} + +void PythonShell::traceLine(const QString &file, int line) +{ + if(QObject::sender() == (QObject *)interactiveContext) + return; + + scriptEditor->markerDeleteAll(CURRENT_MARKER); + scriptEditor->markerDeleteAll(CURRENT_MARKER + 1); + + scriptEditor->markerAdd(line > 0 ? line - 1 : 0, CURRENT_MARKER); + scriptEditor->markerAdd(line > 0 ? line - 1 : 0, CURRENT_MARKER + 1); +} + +void PythonShell::exception(const QString &type, const QString &value, QList frames) +{ + QTextEdit *out = ui->scriptOutput; + if(QObject::sender() == (QObject *)interactiveContext) + out = ui->interactiveOutput; + + QString exString; + + if(!out->toPlainText().endsWith('\n')) + exString = "\n"; + if(!frames.isEmpty()) + { + exString += "Traceback (most recent call last):\n"; + for(const QString &f : frames) + exString += QString(" %1\n").arg(f); + } + exString += QString("%1: %2\n").arg(type).arg(value); + + appendText(out, exString); +} + +void PythonShell::textOutput(bool isStdError, const QString &output) +{ + QTextEdit *out = ui->scriptOutput; + if(QObject::sender() == (QObject *)interactiveContext) + out = ui->interactiveOutput; + + appendText(out, output); +} + +void PythonShell::interactive_keypress(QKeyEvent *event) +{ + if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + on_execute_clicked(); + + bool moved = false; + + if(event->key() == Qt::Key_Down && historyidx > -1) + { + historyidx--; + + moved = true; + } + + QString workingtext = ""; + + if(event->key() == Qt::Key_Up && historyidx + 1 < history.count()) + { + if(historyidx == -1) + workingtext = ui->lineInput->text(); + + historyidx++; + + moved = true; + } + + if(moved) + { + if(historyidx == -1) + ui->lineInput->setText(workingtext); + else + ui->lineInput->setText(history[historyidx]); + + ui->lineInput->deselect(); + } +} + +QString PythonShell::scriptHeader() +{ + return tr(R"(RenderDoc Python console, powered by python %1. +The 'pyrenderdoc' object is the current CaptureContext instance. +The 'renderdoc' and 'qrenderdoc' modules are available. +Documentation is available: https://renderdoc.org/docs/python_api/index.html)") + .arg(interactiveContext->versionString()); +} + +void PythonShell::appendText(QTextEdit *output, const QString &text) +{ + output->moveCursor(QTextCursor::End); + output->insertPlainText(text); + + // scroll to the bottom + QScrollBar *vscroll = output->verticalScrollBar(); + vscroll->setValue(vscroll->maximum()); +} + +void PythonShell::enableButtons(bool enable) +{ + ui->newScript->setEnabled(enable); + ui->openScript->setEnabled(enable); + ui->saveScript->setEnabled(enable); + ui->runScript->setEnabled(enable); + ui->abortRun->setEnabled(!enable); +} + +PythonContext *PythonShell::newContext() +{ + PythonContext *ret = new PythonContext(); + + QObject::connect(ret, &PythonContext::traceLine, this, &PythonShell::traceLine); + QObject::connect(ret, &PythonContext::exception, this, &PythonShell::exception); + QObject::connect(ret, &PythonContext::textOutput, this, &PythonShell::textOutput); + + ret->setGlobal("pyrenderdoc", &m_Ctx); + + return ret; +} diff --git a/qrenderdoc/Windows/PythonShell.h b/qrenderdoc/Windows/PythonShell.h new file mode 100644 index 000000000..5af4f9ba3 --- /dev/null +++ b/qrenderdoc/Windows/PythonShell.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2017 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include +#include "Code/CaptureContext.h" + +class PythonContext; +class ScintillaEdit; +class QTextEdit; + +namespace Ui +{ +class PythonShell; +} + +class PythonShell : public QFrame, public IPythonShell +{ + Q_OBJECT + +public: + explicit PythonShell(ICaptureContext &ctx, QWidget *parent = 0); + + ~PythonShell(); + + // IPythonShell + QWidget *Widget() override { return this; } +private slots: + // automatic slots + void on_execute_clicked(); + void on_clear_clicked(); + void on_newScript_clicked(); + void on_openScript_clicked(); + void on_saveScript_clicked(); + void on_runScript_clicked(); + void on_abortRun_clicked(); + + // manual slots + void interactive_keypress(QKeyEvent *e); + void traceLine(const QString &file, int line); + void exception(const QString &type, const QString &value, QList frames); + void textOutput(bool isStdError, const QString &output); + +private: + Ui::PythonShell *ui; + ICaptureContext &m_Ctx; + + ScintillaEdit *scriptEditor; + + static const int CURRENT_MARKER = 0; + + PythonContext *interactiveContext, *scriptContext; + + QList history; + int historyidx = -1; + + PythonContext *newContext(); + + QString scriptHeader(); + void appendText(QTextEdit *output, const QString &text); + void enableButtons(bool enable); +}; diff --git a/qrenderdoc/Windows/PythonShell.ui b/qrenderdoc/Windows/PythonShell.ui new file mode 100644 index 000000000..201e964c9 --- /dev/null +++ b/qrenderdoc/Windows/PythonShell.ui @@ -0,0 +1,315 @@ + + + PythonShell + + + + 0 + 0 + 544 + 346 + + + + Interactive Python Shell + + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 1 + + + + Interactive Shell + + + + 4 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Qt::ScrollBarAlwaysOn + + + true + + + + + + + 6 + + + + + + + + Execute + + + + + + + Clear + + + + + + + + + + Run Scripts + + + + 4 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 0 + 28 + + + + QFrame::Panel + + + QFrame::Raised + + + + 2 + + + 6 + + + 2 + + + 6 + + + 2 + + + + + Create a new blank script + + + New + + + + :/page_white_edit.png:/page_white_edit.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Open an existing python script + + + Open + + + + :/folder_page.png:/folder_page.png + + + QToolButton::InstantPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Save the current script to disk + + + Save As + + + + :/save.png:/save.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Vertical + + + + + + + Begin running the script in python + + + Run + + + + :/runfwd.png:/runfwd.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Stop execution of the current script + + + Abort + + + + :/del.png:/del.png + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + false + + + + Qt::ScrollBarAlwaysOn + + + true + + + + + + + + + + + + + RDLineEdit + QLineEdit +
Widgets/Extended/RDLineEdit.h
+
+ + RDTextEdit + QTextEdit +
Widgets/Extended/RDTextEdit.h
+
+
+ + + + +
diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 34676ebce..478985fec 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -203,7 +203,8 @@ SOURCES += Code/qrenderdoc.cpp \ Windows/Dialogs/EnvironmentEditor.cpp \ Widgets/FindReplace.cpp \ Widgets/Extended/RDSplitter.cpp \ - Windows/Dialogs/TipsDialog.cpp + Windows/Dialogs/TipsDialog.cpp \ + Windows/PythonShell.cpp HEADERS += Code/CaptureContext.h \ Code/qprocessinfo.h \ Code/ReplayManager.h \ @@ -260,7 +261,8 @@ HEADERS += Code/CaptureContext.h \ Windows/Dialogs/EnvironmentEditor.h \ Widgets/FindReplace.h \ Widgets/Extended/RDSplitter.h \ - Windows/Dialogs/TipsDialog.h + Windows/Dialogs/TipsDialog.h \ + Windows/PythonShell.h FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/MainWindow.ui \ Windows/EventBrowser.ui \ @@ -290,7 +292,8 @@ FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/PixelHistoryView.ui \ Windows/Dialogs/EnvironmentEditor.ui \ Widgets/FindReplace.ui \ - Windows/Dialogs/TipsDialog.ui + Windows/Dialogs/TipsDialog.ui \ + Windows/PythonShell.ui RESOURCES += Resources/resources.qrc diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index a6433e794..aae5c8ad7 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -673,6 +673,7 @@ + @@ -740,6 +741,7 @@ + @@ -884,6 +886,7 @@ + @@ -1168,6 +1171,12 @@ MOC %(Filename).h $(IntDir)generated\moc_%(Filename).cpp + + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs) + $(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I$(ProjectDir) -I$(SolutionDir)\renderdoc\api\replay -I$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015 -I$(ProjectDir)3rdparty\qt\$(Platform)\include -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore %(Fullpath) -o $(IntDir)generated\moc_%(Filename).cpp + MOC %(Filename).h + $(IntDir)generated\moc_%(Filename).cpp + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe;%(AdditionalInputs) $(ProjectDir)3rdparty\qt\$(Platform)\bin\moc.exe -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I$(ProjectDir) -I$(SolutionDir)\renderdoc\api\replay -I$(ProjectDir)3rdparty\qt\$(Platform)\mkspecs/win32-msvc2015 -I$(ProjectDir)3rdparty\qt\$(Platform)\include -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtWidgets -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtGui -I$(ProjectDir)3rdparty\qt\$(Platform)\include\QtCore %(Fullpath) -o $(IntDir)generated\moc_%(Filename).cpp @@ -1368,6 +1377,12 @@ UIC %(Filename).ui $(IntDir)generated\ui_%(Filename).h + + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs) + $(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe %(Fullpath) -o $(IntDir)generated\ui_%(Filename).h + UIC %(Filename).ui + $(IntDir)generated\ui_%(Filename).h + %(Fullpath);$(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe;%(AdditionalInputs) $(ProjectDir)3rdparty\qt\$(Platform)\bin\uic.exe %(Fullpath) -o $(IntDir)generated\ui_%(Filename).h @@ -1569,4 +1584,4 @@ - \ No newline at end of file + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 13cefc644..fc5ca26e6 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -576,6 +576,12 @@ Code + + Generated Files + + + Windows + Widgets\Extended @@ -887,6 +893,9 @@ Code + + Generated Files + @@ -1337,5 +1346,11 @@ Widgets\Extended + + Windows + + + Windows + \ No newline at end of file