Made python console use edit line and better
This commit is contained in:
parent
013cfebcc5
commit
07ff5ad8b8
322
3rdparty/python-console/modified/pyconsole.cc
vendored
322
3rdparty/python-console/modified/pyconsole.cc
vendored
@ -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( );
|
||||
}
|
||||
}
|
||||
}
|
80
3rdparty/python-console/modified/pyconsole.h
vendored
80
3rdparty/python-console/modified/pyconsole.h
vendored
@ -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
|
@ -12,7 +12,6 @@ if (BUILD_PYTHON)
|
||||
|
||||
../3rdparty/python-console/modified/pyredirector.cc
|
||||
../3rdparty/python-console/modified/pyinterpreter.cc
|
||||
../3rdparty/python-console/modified/pyconsole.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -76,7 +76,7 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
|
||||
tabWidget = new QTabWidget();
|
||||
#ifndef NO_PYTHON
|
||||
PythonTab *pythontab = new PythonTab();
|
||||
tabWidget->addTab(pythontab, "Python");
|
||||
tabWidget->addTab(pythontab, "Console");
|
||||
connect(this, SIGNAL(contextChanged(Context *)), pythontab, SLOT(newContext(Context *)));
|
||||
#endif
|
||||
info = new InfoTab();
|
||||
|
@ -2,6 +2,7 @@
|
||||
* 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
|
||||
@ -17,12 +18,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "line_editor.h"
|
||||
#include <QKeyEvent>
|
||||
#include <QToolTip>
|
||||
#include "ColumnFormatter.h"
|
||||
#include "Utils.h"
|
||||
#include "pyinterpreter.h"
|
||||
|
||||
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);
|
||||
QAction *clearAction = new QAction("Clear &history", this);
|
||||
@ -38,10 +45,12 @@ LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0)
|
||||
|
||||
void LineEditor::keyPressEvent(QKeyEvent *ev)
|
||||
{
|
||||
|
||||
if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
|
||||
QToolTip::hideText();
|
||||
if (lines.empty())
|
||||
return;
|
||||
|
||||
printf("Key_Up\n");
|
||||
if (ev->key() == Qt::Key_Up)
|
||||
index--;
|
||||
if (ev->key() == Qt::Key_Down)
|
||||
@ -56,12 +65,21 @@ void LineEditor::keyPressEvent(QKeyEvent *ev)
|
||||
}
|
||||
setText(lines[index]);
|
||||
} else if (ev->key() == Qt::Key_Escape) {
|
||||
QToolTip::hideText();
|
||||
clear();
|
||||
return;
|
||||
} else if (ev->key() == Qt::Key_Tab) {
|
||||
autocomplete();
|
||||
return;
|
||||
}
|
||||
QToolTip::hideText();
|
||||
|
||||
QLineEdit::keyPressEvent(ev);
|
||||
}
|
||||
|
||||
// This makes TAB work
|
||||
bool LineEditor::focusNextPrevChild(bool next) { return false; }
|
||||
|
||||
void LineEditor::textInserted()
|
||||
{
|
||||
if (lines.empty() || lines.back() != text())
|
||||
@ -82,4 +100,36 @@ void LineEditor::clearHistory()
|
||||
clear();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
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
|
||||
|
||||
#endif // NO_PYTHON
|
||||
|
@ -2,6 +2,7 @@
|
||||
* 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
|
||||
@ -20,8 +21,11 @@
|
||||
#ifndef LINE_EDITOR_H
|
||||
#define LINE_EDITOR_H
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include "ParseHelper.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
@ -31,7 +35,7 @@ class LineEditor : public QLineEdit
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LineEditor(QWidget *parent = 0);
|
||||
explicit LineEditor(ParseHelper *helper, QWidget *parent = 0);
|
||||
|
||||
private Q_SLOTS:
|
||||
void textInserted();
|
||||
@ -43,13 +47,18 @@ class LineEditor : public QLineEdit
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
|
||||
bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
|
||||
void autocomplete();
|
||||
|
||||
private:
|
||||
int index;
|
||||
QStringList lines;
|
||||
QMenu *contextMenu;
|
||||
ParseHelper *parseHelper;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // NO_PYTHON
|
||||
|
||||
#endif // LINE_EDITOR_H
|
||||
|
82
gui/pyconsole.cc
Normal file
82
gui/pyconsole.cc
Normal 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
58
gui/pyconsole.h
Normal 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
|
@ -25,12 +25,20 @@
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
const QString PythonTab::PROMPT = ">>> ";
|
||||
const QString PythonTab::MULTILINE_PROMPT = "... ";
|
||||
|
||||
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
|
||||
{
|
||||
QFont f("unexistent");
|
||||
f.setStyleHint(QFont::Monospace);
|
||||
|
||||
// Add text area for Python output and input line
|
||||
console = new PythonConsole();
|
||||
console->setMinimumHeight(100);
|
||||
console->setEnabled(false);
|
||||
console->setReadOnly(true);
|
||||
console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||
console->setFont(f);
|
||||
|
||||
console->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QAction *clearAction = new QAction("Clear &buffer", this);
|
||||
@ -41,9 +49,21 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
|
||||
contextMenu->addAction(clearAction);
|
||||
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();
|
||||
mainLayout->addWidget(console, 0, 0);
|
||||
mainLayout->addWidget(lineEdit, 1, 0);
|
||||
setLayout(mainLayout);
|
||||
|
||||
parseHelper.subscribe(console);
|
||||
|
||||
prompt = PythonTab::PROMPT;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (initialized) {
|
||||
pyinterpreter_finalize();
|
||||
deinit_python();
|
||||
}
|
||||
console->setEnabled(true);
|
||||
console->clear();
|
||||
|
||||
pyinterpreter_preinit();
|
||||
@ -74,7 +108,6 @@ void PythonTab::newContext(Context *ctx)
|
||||
|
||||
QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
|
||||
console->displayString(version);
|
||||
console->displayPrompt();
|
||||
}
|
||||
|
||||
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
|
||||
@ -83,4 +116,4 @@ void PythonTab::clearBuffer() { console->clear(); }
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
#endif // NO_PYTHON
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QPlainTextEdit>
|
||||
#include "ParseHelper.h"
|
||||
#include "line_editor.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pyconsole.h"
|
||||
@ -42,13 +43,20 @@ class PythonTab : public QWidget
|
||||
private Q_SLOTS:
|
||||
void showContextMenu(const QPoint &pt);
|
||||
void clearBuffer();
|
||||
void editLineReturnPressed(QString text);
|
||||
public Q_SLOTS:
|
||||
void newContext(Context *ctx);
|
||||
|
||||
private:
|
||||
PythonConsole *console;
|
||||
LineEditor *lineEdit;
|
||||
QMenu *contextMenu;
|
||||
bool initialized;
|
||||
ParseHelper parseHelper;
|
||||
QString prompt;
|
||||
|
||||
static const QString PROMPT;
|
||||
static const QString MULTILINE_PROMPT;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
Loading…
Reference in New Issue
Block a user