Made python console use edit line and better

This commit is contained in:
Miodrag Milanovic 2018-07-13 19:56:11 +02:00
parent 013cfebcc5
commit 07ff5ad8b8
10 changed files with 249 additions and 412 deletions

View File

@ -1,322 +0,0 @@
#include "pyconsole.h"
#include "pyinterpreter.h"
#include "ColumnFormatter.h"
#include <iostream>
#include <QKeyEvent>
#include <QFont>
#include "Utils.h"
const QString PythonConsole::PROMPT = ">>> ";
const QString PythonConsole::MULTILINE_PROMPT = "... ";
const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 );
const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 );
const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 );
PythonConsole::PythonConsole( QWidget* parent ):
QTextEdit( parent )
{
QFont font("unexistent");
font.setStyleHint(QFont::Monospace);
setFont(font);
m_parseHelper.subscribe( this );
}
void PythonConsole::keyPressEvent( QKeyEvent* e )
{
switch ( e->key() )
{
case Qt::Key_Return:
handleReturnKeyPress( );
return;
case Qt::Key_Tab:
autocomplete( );
return;
case Qt::Key_Backspace:
if ( ! canBackspace( ) )
return;
break;
case Qt::Key_Up:
previousHistory( );
return;
case Qt::Key_Down:
nextHistory( );
return;
case Qt::Key_Left:
if ( ! canGoLeft( ) )
return;
}
if (!cursorIsOnInputLine()) return;
if (textCursor().columnNumber() < PythonConsole::PROMPT.size()) return;
QTextEdit::keyPressEvent( e );
}
void PythonConsole::handleReturnKeyPress( )
{
if ( ! cursorIsOnInputLine( ) )
{
return;
}
QString line = getLine( );
m_parseHelper.process( line.toStdString( ) );
if ( m_parseHelper.buffered( ) )
{
append("");
displayPrompt( );
}
if ( line.size( ) )
{
m_historyBuffer.push_back( line.toStdString( ) );
m_historyIt = m_historyBuffer.end();
}
moveCursorToEnd( );
}
void PythonConsole::parseEvent( const ParseMessage& message )
{
// handle invalid user input
if ( message.errorCode )
{
setTextColor( ERROR_COLOR );
append(message.message.c_str());
setTextColor( NORMAL_COLOR );
append("");
displayPrompt( );
return;
}
// interpret valid user input
int errorCode = 0;
std::string res;
if ( message.message.size() )
res = pyinterpreter_execute( message.message, &errorCode );
if ( errorCode )
{
setTextColor( ERROR_COLOR );
}
else
{
setTextColor( OUTPUT_COLOR );
}
if ( res.size( ) )
{
append(res.c_str());
}
setTextColor( NORMAL_COLOR );
// set up the next line on the console
append("");
displayPrompt( );
}
QString PythonConsole::getLine( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
QString line = cursor.selectedText( );
cursor.clearSelection( );
return line;
}
bool PythonConsole::cursorIsOnInputLine( )
{
int cursorBlock = textCursor( ).blockNumber( );
QTextCursor bottomCursor = textCursor( );
bottomCursor.movePosition( QTextCursor::End );
int bottomBlock = bottomCursor.blockNumber( );
return ( cursorBlock == bottomBlock );
}
bool PythonConsole::inputLineIsEmpty( )
{
QTextCursor bottomCursor = textCursor( );
bottomCursor.movePosition( QTextCursor::End );
int col = bottomCursor.columnNumber( );
return ( col == PythonConsole::PROMPT.size( ) );
}
bool PythonConsole::canBackspace( )
{
if ( ! cursorIsOnInputLine( ) )
{
return false;
}
if ( inputLineIsEmpty( ) )
{
return false;
}
return true;
}
bool PythonConsole::canGoLeft( )
{
if ( cursorIsOnInputLine( ) )
{
QTextCursor bottomCursor = textCursor( );
int col = bottomCursor.columnNumber( );
return (col > PythonConsole::PROMPT.size( ));
}
return true;
}
void PythonConsole::displayPrompt( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
if ( m_parseHelper.buffered( ) )
{
cursor.insertText( PythonConsole::MULTILINE_PROMPT );
}
else
{
cursor.insertText( PythonConsole::PROMPT );
}
cursor.movePosition( QTextCursor::EndOfLine );
}
void PythonConsole::displayString(QString text)
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
cursor.insertText( text );
cursor.movePosition( QTextCursor::EndOfLine );
}
void PythonConsole::autocomplete( )
{
if ( ! cursorIsOnInputLine( ) )
return;
QString line = getLine( );
const std::list<std::string>& suggestions =
pyinterpreter_suggest( line.toStdString( ) );
if (suggestions.size() == 1)
{
line = suggestions.back().c_str();
}
else
{
// try to complete to longest common prefix
std::string prefix =
LongestCommonPrefix(suggestions.begin(), suggestions.end());
if (prefix.size() > (size_t)line.size())
{
line = prefix.c_str();
}
else
{
ColumnFormatter fmt;
fmt.setItems(suggestions.begin(), suggestions.end());
fmt.format(width() / 10);
setTextColor( OUTPUT_COLOR );
const std::list<std::string>& formatted = fmt.formattedOutput();
for (std::list<std::string>::const_iterator it = formatted.begin();
it != formatted.end(); ++it)
{
append(it->c_str());
}
setTextColor( NORMAL_COLOR );
}
}
// set up the next line on the console
append("");
displayPrompt( );
moveCursorToEnd( );
QTextCursor cursor = textCursor();
cursor.insertText( line );
moveCursorToEnd( );
}
void PythonConsole::previousHistory( )
{
if ( ! cursorIsOnInputLine( ) )
return;
if ( ! m_historyBuffer.size( ) )
return;
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
cursor.removeSelectedText( );
if ( m_historyIt != m_historyBuffer.begin( ) )
{
--m_historyIt;
}
cursor.insertText( m_historyIt->c_str() );
}
void PythonConsole::nextHistory( )
{
if ( ! cursorIsOnInputLine( ) )
return;
if ( ! m_historyBuffer.size( ) )
return;
if ( m_historyIt == m_historyBuffer.end( ) )
{
return;
}
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::StartOfLine );
cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PythonConsole::PROMPT.size( ) );
cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor );
cursor.removeSelectedText( );
++m_historyIt;
if ( m_historyIt == m_historyBuffer.end( ) )
{
return;
}
cursor.insertText( m_historyIt->c_str() );
}
void PythonConsole::moveCursorToEnd( )
{
QTextCursor cursor = textCursor();
cursor.movePosition( QTextCursor::End );
setTextCursor( cursor );
}
void PythonConsole::insertFromMimeData(const QMimeData *src)
{
if (src->hasText()) {
QStringList list = src->text().split("\n",QString::KeepEmptyParts);
bool lastends = src->text().endsWith("\n");
for (int i=0;i<list.size();i++)
{
QString line = list.at(i);
displayString(line);
if (!lastends && (i==list.size()-1)) break;
m_parseHelper.process( line.toStdString( ) );
if ( m_parseHelper.buffered( ) )
{
append("");
displayPrompt( );
}
if ( line.size( ) )
{
m_historyBuffer.push_back( line.toStdString( ) );
m_historyIt = m_historyBuffer.end();
}
moveCursorToEnd( );
}
}
}

View File

@ -1,80 +0,0 @@
/**
python-console
Copyright (C) 2018 Alex Tsui
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.
*/
#ifndef PYCONSOLE_H
#define PYCONSOLE_H
#include <QColor>
#include <QTextEdit>
#include <QMimeData>
#include "ParseHelper.h"
#include "ParseListener.h"
class QWidget;
class QKeyEvent;
class PythonConsole : public QTextEdit, ParseListener
{
Q_OBJECT
public:
PythonConsole(QWidget *parent = 0);
void displayPrompt();
void displayString(QString text);
protected:
// override QTextEdit
virtual void keyPressEvent(QKeyEvent *e);
virtual void handleReturnKeyPress();
virtual void insertFromMimeData(const QMimeData *src);
/**
Handle a compilable chunk of Python user input.
*/
virtual void parseEvent(const ParseMessage &message);
QString getLine();
bool cursorIsOnInputLine();
bool inputLineIsEmpty();
bool canBackspace();
bool canGoLeft();
void autocomplete();
void previousHistory();
void nextHistory();
void moveCursorToEnd();
static const QString PROMPT;
static const QString MULTILINE_PROMPT;
static const QColor NORMAL_COLOR;
static const QColor ERROR_COLOR;
static const QColor OUTPUT_COLOR;
ParseHelper m_parseHelper;
std::list<std::string> m_historyBuffer;
std::list<std::string>::const_iterator m_historyIt;
};
#endif // PYCONSOLE_H

View File

@ -12,7 +12,6 @@ if (BUILD_PYTHON)
../3rdparty/python-console/modified/pyredirector.cc ../3rdparty/python-console/modified/pyredirector.cc
../3rdparty/python-console/modified/pyinterpreter.cc ../3rdparty/python-console/modified/pyinterpreter.cc
../3rdparty/python-console/modified/pyconsole.cc
) )
endif() endif()

View File

@ -76,7 +76,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
tabWidget = new QTabWidget(); tabWidget = new QTabWidget();
#ifndef NO_PYTHON #ifndef NO_PYTHON
PythonTab *pythontab = new PythonTab(); PythonTab *pythontab = new PythonTab();
tabWidget->addTab(pythontab, "Python"); tabWidget->addTab(pythontab, "Console");
connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *))); connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *)));
#endif #endif
info = new InfoTab(); info = new InfoTab();

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -17,12 +18,18 @@
* *
*/ */
#ifndef NO_PYTHON
#include "line_editor.h" #include "line_editor.h"
#include <QKeyEvent> #include <QKeyEvent>
#include <QToolTip>
#include "ColumnFormatter.h"
#include "Utils.h"
#include "pyinterpreter.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) LineEditor::LineEditor(ParseHelper *helper, QWidget *parent) : QLineEdit(parent), index(0), parseHelper(helper)
{ {
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &history", this); QAction *clearAction = new QAction("Clear &history", this);
@ -38,10 +45,12 @@ LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0)
void LineEditor::keyPressEvent(QKeyEvent *ev) void LineEditor::keyPressEvent(QKeyEvent *ev)
{ {
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
QToolTip::hideText();
if (lines.empty()) if (lines.empty())
return; return;
printf("Key_Up\n");
if (ev->key() == Qt::Key_Up) if (ev->key() == Qt::Key_Up)
index--; index--;
if (ev->key() == Qt::Key_Down) if (ev->key() == Qt::Key_Down)
@ -56,12 +65,21 @@ void LineEditor::keyPressEvent(QKeyEvent *ev)
} }
setText(lines[index]); setText(lines[index]);
} else if (ev->key() == Qt::Key_Escape) { } else if (ev->key() == Qt::Key_Escape) {
QToolTip::hideText();
clear(); clear();
return; return;
} else if (ev->key() == Qt::Key_Tab) {
autocomplete();
return;
} }
QToolTip::hideText();
QLineEdit::keyPressEvent(ev); QLineEdit::keyPressEvent(ev);
} }
// This makes TAB work
bool LineEditor::focusNextPrevChild(bool next) { return false; }
void LineEditor::textInserted() void LineEditor::textInserted()
{ {
if (lines.empty() || lines.back() != text()) if (lines.empty() || lines.back() != text())
@ -82,4 +100,36 @@ void LineEditor::clearHistory()
clear(); clear();
} }
void LineEditor::autocomplete()
{
QString line = text();
const std::list<std::string> &suggestions = pyinterpreter_suggest(line.toStdString());
if (suggestions.size() == 1) {
line = suggestions.back().c_str();
} else {
// try to complete to longest common prefix
std::string prefix = LongestCommonPrefix(suggestions.begin(), suggestions.end());
if (prefix.size() > (size_t)line.size()) {
line = prefix.c_str();
} else {
ColumnFormatter fmt;
fmt.setItems(suggestions.begin(), suggestions.end());
fmt.format(width() / 5);
QString out = "";
for (auto &it : fmt.formattedOutput()) {
if (!out.isEmpty())
out += "\n";
out += it.c_str();
}
QToolTip::setFont(font());
if (!out.trimmed().isEmpty())
QToolTip::showText(mapToGlobal(QPoint(0, 0)), out);
}
}
// set up the next line on the console
setText(line);
}
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON

View File

@ -2,6 +2,7 @@
* nextpnr -- Next Generation Place and Route * nextpnr -- Next Generation Place and Route
* *
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com> * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -20,8 +21,11 @@
#ifndef LINE_EDITOR_H #ifndef LINE_EDITOR_H
#define LINE_EDITOR_H #define LINE_EDITOR_H
#ifndef NO_PYTHON
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include "ParseHelper.h"
#include "nextpnr.h" #include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -31,7 +35,7 @@ class LineEditor : public QLineEdit
Q_OBJECT Q_OBJECT
public: public:
explicit LineEditor(QWidget *parent = 0); explicit LineEditor(ParseHelper *helper, QWidget *parent = 0);
private Q_SLOTS: private Q_SLOTS:
void textInserted(); void textInserted();
@ -43,13 +47,18 @@ class LineEditor : public QLineEdit
protected: protected:
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
void autocomplete();
private: private:
int index; int index;
QStringList lines; QStringList lines;
QMenu *contextMenu; QMenu *contextMenu;
ParseHelper *parseHelper;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON
#endif // LINE_EDITOR_H #endif // LINE_EDITOR_H

82
gui/pyconsole.cc Normal file
View File

@ -0,0 +1,82 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NO_PYTHON
#include "pyconsole.h"
#include "pyinterpreter.h"
NEXTPNR_NAMESPACE_BEGIN
const QColor PythonConsole::NORMAL_COLOR = QColor::fromRgbF(0, 0, 0);
const QColor PythonConsole::ERROR_COLOR = QColor::fromRgbF(1.0, 0, 0);
const QColor PythonConsole::OUTPUT_COLOR = QColor::fromRgbF(0, 0, 1.0);
PythonConsole::PythonConsole(QWidget *parent) : QTextEdit(parent) {}
void PythonConsole::parseEvent(const ParseMessage &message)
{
// handle invalid user input
if (message.errorCode) {
setTextColor(ERROR_COLOR);
append(message.message.c_str());
setTextColor(NORMAL_COLOR);
append("");
return;
}
// interpret valid user input
int errorCode = 0;
std::string res;
if (message.message.size())
res = pyinterpreter_execute(message.message, &errorCode);
if (errorCode) {
setTextColor(ERROR_COLOR);
} else {
setTextColor(OUTPUT_COLOR);
}
if (res.size()) {
append(res.c_str());
}
setTextColor(NORMAL_COLOR);
append("");
moveCursorToEnd();
}
void PythonConsole::displayString(QString text)
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::End);
setTextColor(NORMAL_COLOR);
cursor.insertText(text);
cursor.movePosition(QTextCursor::EndOfLine);
}
void PythonConsole::moveCursorToEnd()
{
QTextCursor cursor = textCursor();
cursor.movePosition(QTextCursor::End);
setTextCursor(cursor);
}
NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON

58
gui/pyconsole.h Normal file
View File

@ -0,0 +1,58 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
* Copyright (C) 2018 Alex Tsui
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef PYCONSOLE_H
#define PYCONSOLE_H
#ifndef NO_PYTHON
#include <QColor>
#include <QMimeData>
#include <QTextEdit>
#include "ParseHelper.h"
#include "ParseListener.h"
#include "nextpnr.h"
class QWidget;
class QKeyEvent;
NEXTPNR_NAMESPACE_BEGIN
class PythonConsole : public QTextEdit, public ParseListener
{
Q_OBJECT
public:
PythonConsole(QWidget *parent = 0);
void displayString(QString text);
void moveCursorToEnd();
virtual void parseEvent(const ParseMessage &message);
protected:
static const QColor NORMAL_COLOR;
static const QColor ERROR_COLOR;
static const QColor OUTPUT_COLOR;
};
NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON
#endif // PYCONSOLE_H

View File

@ -25,12 +25,20 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
const QString PythonTab::PROMPT = ">>> ";
const QString PythonTab::MULTILINE_PROMPT = "... ";
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false) PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
{ {
QFont f("unexistent");
f.setStyleHint(QFont::Monospace);
// Add text area for Python output and input line // Add text area for Python output and input line
console = new PythonConsole(); console = new PythonConsole();
console->setMinimumHeight(100); console->setMinimumHeight(100);
console->setEnabled(false); console->setReadOnly(true);
console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
console->setFont(f);
console->setContextMenuPolicy(Qt::CustomContextMenu); console->setContextMenuPolicy(Qt::CustomContextMenu);
QAction *clearAction = new QAction("Clear &buffer", this); QAction *clearAction = new QAction("Clear &buffer", this);
@ -41,9 +49,21 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
contextMenu->addAction(clearAction); contextMenu->addAction(clearAction);
connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint))); connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
lineEdit = new LineEditor(&parseHelper);
lineEdit->setMinimumHeight(30);
lineEdit->setMaximumHeight(30);
lineEdit->setFont(f);
lineEdit->setPlaceholderText(PythonTab::PROMPT);
connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
QGridLayout *mainLayout = new QGridLayout(); QGridLayout *mainLayout = new QGridLayout();
mainLayout->addWidget(console, 0, 0); mainLayout->addWidget(console, 0, 0);
mainLayout->addWidget(lineEdit, 1, 0);
setLayout(mainLayout); setLayout(mainLayout);
parseHelper.subscribe(console);
prompt = PythonTab::PROMPT;
} }
PythonTab::~PythonTab() PythonTab::~PythonTab()
@ -54,13 +74,27 @@ PythonTab::~PythonTab()
} }
} }
void PythonTab::editLineReturnPressed(QString text)
{
console->displayString(prompt + text + "\n");
console->moveCursorToEnd();
parseHelper.process(text.toStdString());
if (parseHelper.buffered())
prompt = PythonTab::MULTILINE_PROMPT;
else
prompt = PythonTab::PROMPT;
lineEdit->setPlaceholderText(prompt);
}
void PythonTab::newContext(Context *ctx) void PythonTab::newContext(Context *ctx)
{ {
if (initialized) { if (initialized) {
pyinterpreter_finalize(); pyinterpreter_finalize();
deinit_python(); deinit_python();
} }
console->setEnabled(true);
console->clear(); console->clear();
pyinterpreter_preinit(); pyinterpreter_preinit();
@ -74,7 +108,6 @@ void PythonTab::newContext(Context *ctx)
QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform()); QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
console->displayString(version); console->displayString(version);
console->displayPrompt();
} }
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
@ -83,4 +116,4 @@ void PythonTab::clearBuffer() { console->clear(); }
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END
#endif #endif // NO_PYTHON

View File

@ -25,6 +25,7 @@
#include <QLineEdit> #include <QLineEdit>
#include <QMenu> #include <QMenu>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include "ParseHelper.h"
#include "line_editor.h" #include "line_editor.h"
#include "nextpnr.h" #include "nextpnr.h"
#include "pyconsole.h" #include "pyconsole.h"
@ -42,13 +43,20 @@ class PythonTab : public QWidget
private Q_SLOTS: private Q_SLOTS:
void showContextMenu(const QPoint &pt); void showContextMenu(const QPoint &pt);
void clearBuffer(); void clearBuffer();
void editLineReturnPressed(QString text);
public Q_SLOTS: public Q_SLOTS:
void newContext(Context *ctx); void newContext(Context *ctx);
private: private:
PythonConsole *console; PythonConsole *console;
LineEditor *lineEdit;
QMenu *contextMenu; QMenu *contextMenu;
bool initialized; bool initialized;
ParseHelper parseHelper;
QString prompt;
static const QString PROMPT;
static const QString MULTILINE_PROMPT;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END