Re-organise python shell to be dockable and dock REPL at bottom

This commit is contained in:
baldurk committed 2026-08-13 17:46:40 +01:00
1 parent 60b989f2b7
commit 836a5da7fa
4 files changed
+425 -382

No files matched your search

+1 -1
View File
@@ -250,7 +250,7 @@
</action>
<action name="action_Python_Shell">
<property name="text">
<string>P&amp;ython Shell</string>
<string>P&amp;ython Scripting</string>
</property>
</action>
<action name="action_Event_Browser">
+58 -5
View File
@@ -934,6 +934,10 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
ui->scriptOutput->setFont(Formatter::FixedFont());
ui->helpText->setFont(Formatter::FixedFont());
ui->outputGroup->setWindowTitle(tr("Output"));
ui->helpGroup->setWindowTitle(tr("Help"));
ui->replGroup->setWindowTitle(tr("Interactive REPL"));
ui->lineInput->setAcceptTabCharacters(true);
scriptEditor = new ScintillaEdit(this);
@@ -968,6 +972,15 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
scriptEditor->colourise(0, -1);
QObject::connect(ui->interactiveOutput, &RDTextEdit::keyPress, [this](QKeyEvent *e) {
// ignore keypresses that aren't typing, but for up/down redirect that to the line input to get history
if((e->text().isEmpty() || !e->text()[0].isPrint()) && e->key() != Qt::Key_Up &&
e->key() != Qt::Key_Down)
return;
ui->lineInput->setFocus(Qt::OtherFocusReason);
QApplication::postEvent(ui->lineInput, new QKeyEvent(*e));
});
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 |
@@ -1003,11 +1016,37 @@ PythonShell::PythonShell(ICaptureContext &ctx, QWidget *parent)
}
});
ui->scriptSplitter->insertWidget(0, scriptEditor);
int w = ui->scriptSplitter->rect().width();
ui->scriptSplitter->setSizes({w * 2 / 3, w / 3});
scriptEditor->setWindowTitle(tr("Script Editor"));
ui->tabWidget->setCurrentIndex(0);
ui->docking->addToolWindow(scriptEditor, ToolWindowManager::EmptySpace);
ui->docking->setToolWindowProperties(
scriptEditor, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->docking->addToolWindow(
ui->replGroup, ToolWindowManager::AreaReference(ToolWindowManager::BottomOf,
ui->docking->areaOf(scriptEditor), 0.3f));
ui->docking->setToolWindowProperties(
ui->replGroup, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->docking->addToolWindow(ui->outputGroup,
ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->docking->areaOf(ui->replGroup)));
ui->docking->setToolWindowProperties(
ui->outputGroup, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->docking->addToolWindow(ui->helpGroup,
ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->docking->areaOf(ui->replGroup)));
ui->docking->setToolWindowProperties(
ui->helpGroup, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ToolWindowManager::raiseToolWindow(ui->replGroup);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setSpacing(3);
layout->setContentsMargins(3, 3, 3, 3);
layout->addWidget(ui->toolbar);
layout->addWidget(ui->docking);
interactiveContext = NULL;
@@ -1029,6 +1068,20 @@ PythonShell::~PythonShell()
delete ui;
}
QVariant PythonShell::persistData()
{
QVariantMap state = ui->docking->saveState();
return state;
}
void PythonShell::setPersistData(const QVariant &persistData)
{
QVariantMap state = persistData.toMap();
ui->docking->restoreState(state);
}
PythonContext *PythonShell::GetScriptContext()
{
return scriptContext;
@@ -1065,7 +1118,7 @@ void PythonShell::runScript(bool debugging)
ANALYTIC_SET(UIFeatures.PythonInterop, true);
ui->outputHelpTabs->setCurrentIndex(0);
ToolWindowManager::raiseToolWindow(ui->outputGroup);
ui->scriptOutput->clear();
+6
View File
@@ -42,6 +42,8 @@ class PythonShell : public QFrame, public IPythonShell
{
Q_OBJECT
Q_PROPERTY(QVariant persistData READ persistData WRITE setPersistData DESIGNABLE false SCRIPTABLE false)
public:
explicit PythonShell(ICaptureContext &ctx, QWidget *parent = 0);
@@ -57,6 +59,10 @@ public:
rdcstr GetScriptText() override;
void RunScript() override { runScript(false); }
void DebugScript() override { runScript(true); }
QVariant persistData();
void setPersistData(const QVariant &persistData);
private slots:
// automatic slots
void on_execute_clicked();
+360 -376
View File
@@ -6,391 +6,375 @@
<rect>
<x>0</x>
<y>0</y>
<width>544</width>
<height>346</height>
<width>705</width>
<height>448</height>
</rect>
</property>
<property name="windowTitle">
<string>Interactive Python Shell</string>
<string>Python Scripting</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
<widget class="QWidget" name="replGroup" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>361</width>
<height>291</height>
</rect>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="interactiveTab">
<attribute name="title">
<string>Interactive Shell</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="RDTextEdit" name="interactiveOutput">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="RDLineEdit" name="lineInput"/>
</item>
<item>
<widget class="QPushButton" name="execute">
<property name="text">
<string>Execute</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clear">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="RDTextEdit" name="interactiveOutput">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QWidget" name="scriptTab">
<attribute name="title">
<string>Run Scripts</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QFrame" name="toolbar">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QToolButton" name="newScript">
<property name="toolTip">
<string>Create a new blank script</string>
</property>
<property name="text">
<string>New</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/page_white_edit.png</normaloff>:/page_white_edit.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openScript">
<property name="toolTip">
<string>Open an existing python script</string>
</property>
<property name="text">
<string>Open</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/folder_page_white.png</normaloff>:/folder_page_white.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveScript">
<property name="toolTip">
<string>Save the current script to disk</string>
</property>
<property name="text">
<string>Save As</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="sep">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="runScript">
<property name="toolTip">
<string>Begin running the script in python</string>
</property>
<property name="text">
<string>Run</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/arrow_right.png</normaloff>:/arrow_right.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="debugScript">
<property name="toolTip">
<string>Begin running the script in python</string>
</property>
<property name="text">
<string>Debug</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/wrench.png</normaloff>:/wrench.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="abortRun">
<property name="toolTip">
<string>Stop execution of the current script</string>
</property>
<property name="text">
<string>Abort</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/del.png</normaloff>:/del.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="scriptSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QTabWidget" name="outputHelpTabs">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="outputTab">
<attribute name="title">
<string>Output</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="RDTextEdit" name="scriptOutput">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="helpTab">
<attribute name="title">
<string>Help</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="horizontalSpacing">
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="RDLineEdit" name="helpSearch"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="RDTextEdit" name="helpText">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="replCommandsLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="RDLineEdit" name="lineInput"/>
</item>
<item>
<widget class="QPushButton" name="execute">
<property name="text">
<string>Execute</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clear">
<property name="text">
<string>Clear/Reset</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QFrame" name="helpGroup">
<property name="geometry">
<rect>
<x>410</x>
<y>50</y>
<width>111</width>
<height>81</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Search</string>
</property>
</widget>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="RDLineEdit" name="helpSearch"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="RDTextEdit" name="helpText">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="toolbar">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>432</width>
<height>28</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>28</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QToolButton" name="newScript">
<property name="toolTip">
<string>Create a new blank script</string>
</property>
<property name="text">
<string>New</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/page_white_edit.png</normaloff>:/page_white_edit.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openScript">
<property name="toolTip">
<string>Open an existing python script</string>
</property>
<property name="text">
<string>Open</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/folder_page_white.png</normaloff>:/folder_page_white.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveScript">
<property name="toolTip">
<string>Save the current script to disk</string>
</property>
<property name="text">
<string>Save As</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="sep">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="runScript">
<property name="toolTip">
<string>Begin running the script in python</string>
</property>
<property name="text">
<string>Run</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/arrow_right.png</normaloff>:/arrow_right.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="debugScript">
<property name="toolTip">
<string>Begin running the script in python</string>
</property>
<property name="text">
<string>Debug</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/wrench.png</normaloff>:/wrench.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="abortRun">
<property name="toolTip">
<string>Stop execution of the current script</string>
</property>
<property name="text">
<string>Abort</string>
</property>
<property name="icon">
<iconset resource="../Resources/resources.qrc">
<normaloff>:/del.png</normaloff>:/del.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QFrame" name="outputGroup">
<property name="geometry">
<rect>
<x>390</x>
<y>210</y>
<width>276</width>
<height>212</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="0" column="0">
<widget class="RDTextEdit" name="scriptOutput">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="ToolWindowManager" name="docking" native="true">
<property name="geometry">
<rect>
<x>220</x>
<y>40</y>
<width>351</width>
<height>301</height>
</rect>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ToolWindowManager</class>
<extends>QWidget</extends>
<header>3rdparty/toolwindowmanager/ToolWindowManager.h</header>
</customwidget>
<customwidget>
<class>RDLineEdit</class>
<extends>QLineEdit</extends>