adapted python-console for easier use
This commit is contained in:
parent
2037fff742
commit
1676b285ae
2
3rdparty/python-console/Utils.h
vendored
2
3rdparty/python-console/Utils.h
vendored
@ -23,7 +23,7 @@ std::string LongestCommonPrefix( InputIterator begin, InputIterator end )
|
|||||||
const std::string& str = *it;
|
const std::string& str = *it;
|
||||||
for (int j = 0; j <= endIndex; ++j)
|
for (int j = 0; j <= endIndex; ++j)
|
||||||
{
|
{
|
||||||
if (j >= str.size() || str[j] != str0[j])
|
if (j >= (int)str.size() || str[j] != str0[j])
|
||||||
endIndex = j - 1;
|
endIndex = j - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
294
3rdparty/python-console/modified/pyconsole.cc
vendored
Normal file
294
3rdparty/python-console/modified/pyconsole.cc
vendored
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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 );
|
||||||
|
}
|
77
3rdparty/python-console/modified/pyconsole.h
vendored
Normal file
77
3rdparty/python-console/modified/pyconsole.h
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
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 "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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
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
|
155
3rdparty/python-console/modified/pyinterpreter.cc
vendored
Normal file
155
3rdparty/python-console/modified/pyinterpreter.cc
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
python-console
|
||||||
|
Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pyinterpreter.h"
|
||||||
|
#include <Python.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include "pyredirector.h"
|
||||||
|
|
||||||
|
static PyThreadState *MainThreadState = NULL;
|
||||||
|
|
||||||
|
static PyThreadState *m_threadState = NULL;
|
||||||
|
static PyObject *glb = NULL;
|
||||||
|
static PyObject *loc = NULL;
|
||||||
|
|
||||||
|
static std::list<std::string> m_suggestions;
|
||||||
|
|
||||||
|
template <typename... Args> std::string string_format(const std::string &format, Args... args)
|
||||||
|
{
|
||||||
|
size_t size = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||||
|
std::unique_ptr<char[]> buf(new char[size]);
|
||||||
|
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||||
|
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pyinterpreter_execute(const std::string &command, int *errorCode)
|
||||||
|
{
|
||||||
|
PyEval_AcquireThread(m_threadState);
|
||||||
|
*errorCode = 0;
|
||||||
|
|
||||||
|
PyObject *py_result;
|
||||||
|
PyObject *dum;
|
||||||
|
std::string res;
|
||||||
|
py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
|
||||||
|
if (py_result == 0) {
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
*errorCode = 1;
|
||||||
|
PyErr_Print();
|
||||||
|
res = redirector_take_output(m_threadState);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
dum = PyEval_EvalCode(py_result, glb, loc);
|
||||||
|
Py_XDECREF(dum);
|
||||||
|
Py_XDECREF(py_result);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
*errorCode = 1;
|
||||||
|
PyErr_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
res = redirector_take_output(m_threadState);
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::list<std::string> &pyinterpreter_suggest(const std::string &hint)
|
||||||
|
{
|
||||||
|
PyEval_AcquireThread(m_threadState);
|
||||||
|
m_suggestions.clear();
|
||||||
|
int i = 0;
|
||||||
|
std::string command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(), i);
|
||||||
|
std::string res;
|
||||||
|
do {
|
||||||
|
PyObject *py_result;
|
||||||
|
PyObject *dum;
|
||||||
|
py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
|
||||||
|
dum = PyEval_EvalCode(py_result, glb, loc);
|
||||||
|
Py_XDECREF(dum);
|
||||||
|
Py_XDECREF(py_result);
|
||||||
|
res = redirector_take_output(m_threadState);
|
||||||
|
|
||||||
|
++i;
|
||||||
|
command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(), i);
|
||||||
|
if (res.size()) {
|
||||||
|
// throw away the newline
|
||||||
|
res = res.substr(1, res.size() - 3);
|
||||||
|
m_suggestions.push_back(res);
|
||||||
|
}
|
||||||
|
} while (res.size());
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
return m_suggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pyinterpreter_preinit()
|
||||||
|
{
|
||||||
|
m_suggestions.clear();
|
||||||
|
inittab_redirector();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pyinterpreter_initialize()
|
||||||
|
{
|
||||||
|
PyEval_InitThreads();
|
||||||
|
MainThreadState = PyEval_SaveThread();
|
||||||
|
PyEval_AcquireThread(MainThreadState);
|
||||||
|
m_threadState = Py_NewInterpreter();
|
||||||
|
|
||||||
|
PyObject *module = PyImport_ImportModule("__main__");
|
||||||
|
loc = glb = PyModule_GetDict(module);
|
||||||
|
|
||||||
|
PyRun_SimpleString("import sys\n"
|
||||||
|
"import redirector\n"
|
||||||
|
"sys.path.insert(0, \".\")\n" // add current path
|
||||||
|
"sys.stdout = redirector.redirector()\n"
|
||||||
|
"sys.stderr = sys.stdout\n"
|
||||||
|
"import rlcompleter\n"
|
||||||
|
"sys.completer = rlcompleter.Completer()\n");
|
||||||
|
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pyinterpreter_finalize()
|
||||||
|
{
|
||||||
|
m_suggestions.clear();
|
||||||
|
|
||||||
|
PyEval_AcquireThread(m_threadState);
|
||||||
|
Py_EndInterpreter(m_threadState);
|
||||||
|
PyEval_ReleaseLock();
|
||||||
|
|
||||||
|
PyEval_RestoreThread(MainThreadState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pyinterpreter_aquire()
|
||||||
|
{
|
||||||
|
PyEval_AcquireThread(m_threadState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pyinterpreter_release()
|
||||||
|
{
|
||||||
|
PyEval_ReleaseThread(m_threadState);
|
||||||
|
}
|
36
3rdparty/python-console/modified/pyinterpreter.h
vendored
Normal file
36
3rdparty/python-console/modified/pyinterpreter.h
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
python-console
|
||||||
|
Copyright (C) 2014 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 PYINTERPRETER_H
|
||||||
|
#define PYINTERPRETER_H
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string pyinterpreter_execute(const std::string &command, int *errorCode);
|
||||||
|
const std::list<std::string> &pyinterpreter_suggest(const std::string &hint);
|
||||||
|
void pyinterpreter_preinit();
|
||||||
|
void pyinterpreter_initialize();
|
||||||
|
void pyinterpreter_finalize();
|
||||||
|
void pyinterpreter_aquire();
|
||||||
|
void pyinterpreter_release();
|
||||||
|
#endif // PYINTERPRETER_H
|
112
3rdparty/python-console/modified/pyredirector.cc
vendored
Normal file
112
3rdparty/python-console/modified/pyredirector.cc
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/**
|
||||||
|
python-console
|
||||||
|
Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pyredirector.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
static std::map<PyThreadState *, std::string> thread_strings;
|
||||||
|
|
||||||
|
static std::string &redirector_string(PyThreadState *threadState)
|
||||||
|
{
|
||||||
|
if (!thread_strings.count(threadState)) {
|
||||||
|
thread_strings[threadState] = "";
|
||||||
|
}
|
||||||
|
return thread_strings[threadState];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string redirector_take_output(PyThreadState *threadState)
|
||||||
|
{
|
||||||
|
std::string res = redirector_string(threadState);
|
||||||
|
redirector_string(threadState) = "";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *redirector_init(PyObject *, PyObject *)
|
||||||
|
{
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *redirector_write(PyObject *, PyObject *args)
|
||||||
|
{
|
||||||
|
char *output;
|
||||||
|
PyObject *selfi;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "Os", &selfi, &output)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string outputString(output);
|
||||||
|
PyThreadState *currentThread = PyThreadState_Get();
|
||||||
|
std::string &resultString = redirector_string(currentThread);
|
||||||
|
resultString = resultString + outputString;
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return Py_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef redirector_methods[] = {
|
||||||
|
{"__init__", redirector_init, METH_VARARGS, "initialize the stdout/err redirector"},
|
||||||
|
{"write", redirector_write, METH_VARARGS, "implement the write method to redirect stdout/err"},
|
||||||
|
{NULL, NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject *createClassObject(const char *name, PyMethodDef methods[])
|
||||||
|
{
|
||||||
|
PyObject *pClassName = PyUnicode_FromString(name);
|
||||||
|
PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)`
|
||||||
|
PyObject *pClassDic = PyDict_New();
|
||||||
|
|
||||||
|
PyMethodDef *def;
|
||||||
|
// add methods to class
|
||||||
|
for (def = methods; def->ml_name != NULL; def++) {
|
||||||
|
PyObject *func = PyCFunction_New(def, NULL);
|
||||||
|
PyObject *method = PyInstanceMethod_New(func);
|
||||||
|
PyDict_SetItemString(pClassDic, def->ml_name, method);
|
||||||
|
Py_DECREF(func);
|
||||||
|
Py_DECREF(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pClass = type(pClassName, pClassBases, pClassDic)
|
||||||
|
PyObject *pClass = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, pClassName, pClassBases, pClassDic, NULL);
|
||||||
|
|
||||||
|
Py_DECREF(pClassName);
|
||||||
|
Py_DECREF(pClassBases);
|
||||||
|
Py_DECREF(pClassDic);
|
||||||
|
|
||||||
|
return pClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "redirector", 0, -1, 0};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC PyInit_redirector(void)
|
||||||
|
{
|
||||||
|
PyObject *m = PyModule_Create(&moduledef);
|
||||||
|
if (m) {
|
||||||
|
PyObject *fooClass = createClassObject("redirector", redirector_methods);
|
||||||
|
PyModule_AddObject(m, "redirector", fooClass);
|
||||||
|
Py_DECREF(fooClass);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inittab_redirector() { PyImport_AppendInittab("redirector", PyInit_redirector); }
|
32
3rdparty/python-console/modified/pyredirector.h
vendored
Normal file
32
3rdparty/python-console/modified/pyredirector.h
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
python-console
|
||||||
|
Copyright (C) 2014 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 PYREDIRECTOR_H
|
||||||
|
#define PYREDIRECTOR_H
|
||||||
|
#include <Python.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string redirector_take_output(PyThreadState *threadState);
|
||||||
|
void inittab_redirector();
|
||||||
|
|
||||||
|
#endif // PYREDIRECTOR_H
|
@ -8,6 +8,10 @@ set(PYTHON_CONSOLE_SRC
|
|||||||
../3rdparty/python-console/ParseHelper.BracketParseState.cpp
|
../3rdparty/python-console/ParseHelper.BracketParseState.cpp
|
||||||
../3rdparty/python-console/ParseHelper.ContinuationParseState.cpp
|
../3rdparty/python-console/ParseHelper.ContinuationParseState.cpp
|
||||||
../3rdparty/python-console/ParseMessage.cpp
|
../3rdparty/python-console/ParseMessage.cpp
|
||||||
|
|
||||||
|
../3rdparty/python-console/modified/pyredirector.cc
|
||||||
|
../3rdparty/python-console/modified/pyinterpreter.cc
|
||||||
|
../3rdparty/python-console/modified/pyconsole.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
aux_source_directory(. GUI_SOURCE_FILES)
|
aux_source_directory(. GUI_SOURCE_FILES)
|
||||||
@ -21,6 +25,6 @@ set(GUI_LIBRARY_FILES_${ufamily} Qt5::Widgets Qt5::OpenGL ${OPENGL_LIBRARIES} Qt
|
|||||||
|
|
||||||
add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES})
|
add_library(gui_${family} STATIC ${GUI_SOURCE_FILES} ${PYTHON_CONSOLE_SRC} ${GUI_RESOURCE_FILES})
|
||||||
|
|
||||||
target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src)
|
target_include_directories(gui_${family} PRIVATE ../${family} ${family} ../3rdparty/QtPropertyBrowser/src ../3rdparty/python-console ../3rdparty/python-console/modified)
|
||||||
target_compile_definitions(gui_${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS)
|
target_compile_definitions(gui_${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family} ARCH_${ufamily} ARCHNAME=${family} QT_NO_KEYWORDS)
|
||||||
target_link_libraries(gui_${family} Qt5::Widgets)
|
target_link_libraries(gui_${family} Qt5::Widgets)
|
||||||
|
111
gui/pythontab.cc
111
gui/pythontab.cc
@ -21,126 +21,65 @@
|
|||||||
#include "pythontab.h"
|
#include "pythontab.h"
|
||||||
#include <QGridLayout>
|
#include <QGridLayout>
|
||||||
#include "pybindings.h"
|
#include "pybindings.h"
|
||||||
|
#include "pyinterpreter.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
PythonTab::PythonTab(QWidget *parent) : QWidget(parent),initialized(false)
|
PythonTab::PythonTab(QWidget *parent) : QWidget(parent), initialized(false)
|
||||||
{
|
{
|
||||||
// Add text area for Python output and input line
|
// Add text area for Python output and input line
|
||||||
plainTextEdit = new QPlainTextEdit();
|
console = new PythonConsole();
|
||||||
plainTextEdit->setReadOnly(true);
|
console->setMinimumHeight(100);
|
||||||
plainTextEdit->setMinimumHeight(100);
|
console->setEnabled(false);
|
||||||
QFont f("unexistent");
|
|
||||||
f.setStyleHint(QFont::Monospace);
|
|
||||||
plainTextEdit->setFont(f);
|
|
||||||
|
|
||||||
plainTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
|
console->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
QAction *clearAction = new QAction("Clear &buffer", this);
|
QAction *clearAction = new QAction("Clear &buffer", this);
|
||||||
clearAction->setStatusTip("Clears display buffer");
|
clearAction->setStatusTip("Clears display buffer");
|
||||||
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
|
connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
|
||||||
contextMenu = plainTextEdit->createStandardContextMenu();
|
contextMenu = console->createStandardContextMenu();
|
||||||
contextMenu->addSeparator();
|
contextMenu->addSeparator();
|
||||||
contextMenu->addAction(clearAction);
|
contextMenu->addAction(clearAction);
|
||||||
connect(plainTextEdit, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
|
connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
|
||||||
|
|
||||||
lineEdit = new LineEditor();
|
|
||||||
lineEdit->setMinimumHeight(30);
|
|
||||||
lineEdit->setMaximumHeight(30);
|
|
||||||
lineEdit->setFont(f);
|
|
||||||
|
|
||||||
QGridLayout *mainLayout = new QGridLayout();
|
QGridLayout *mainLayout = new QGridLayout();
|
||||||
mainLayout->addWidget(plainTextEdit, 0, 0);
|
mainLayout->addWidget(console, 0, 0);
|
||||||
mainLayout->addWidget(lineEdit, 1, 0);
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
|
|
||||||
connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonTab::~PythonTab()
|
PythonTab::~PythonTab()
|
||||||
{
|
{
|
||||||
if (initialized)
|
if (initialized) {
|
||||||
|
pyinterpreter_finalize();
|
||||||
deinit_python();
|
deinit_python();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PythonTab::newContext(Context *ctx)
|
void PythonTab::newContext(Context *ctx)
|
||||||
{
|
{
|
||||||
if (initialized)
|
if (initialized) {
|
||||||
|
pyinterpreter_finalize();
|
||||||
deinit_python();
|
deinit_python();
|
||||||
|
}
|
||||||
|
console->setEnabled(true);
|
||||||
|
console->clear();
|
||||||
|
|
||||||
plainTextEdit->clear();
|
pyinterpreter_preinit();
|
||||||
|
|
||||||
init_python("nextpnr", !initialized);
|
init_python("nextpnr", !initialized);
|
||||||
|
pyinterpreter_initialize();
|
||||||
|
pyinterpreter_aquire();
|
||||||
python_export_global("ctx", ctx);
|
python_export_global("ctx", ctx);
|
||||||
|
pyinterpreter_release();
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
char buff[1024];
|
QString version = QString("Python %1 on %2\n").arg(Py_GetVersion(), Py_GetPlatform());
|
||||||
sprintf(buff, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
|
console->displayString(version);
|
||||||
print(buff);
|
console->displayPrompt();
|
||||||
}
|
|
||||||
|
|
||||||
void PythonTab::print(std::string line)
|
|
||||||
{
|
|
||||||
plainTextEdit->moveCursor(QTextCursor::End);
|
|
||||||
plainTextEdit->insertPlainText(line.c_str());
|
|
||||||
plainTextEdit->moveCursor(QTextCursor::End);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_system_exit() { exit(-1); }
|
|
||||||
|
|
||||||
int PythonTab::executePython(std::string &command)
|
|
||||||
{
|
|
||||||
PyObject *m, *d, *v;
|
|
||||||
m = PyImport_AddModule("__main__");
|
|
||||||
if (m == NULL)
|
|
||||||
return -1;
|
|
||||||
d = PyModule_GetDict(m);
|
|
||||||
v = PyRun_StringFlags(command.c_str(), (command.empty() ? Py_file_input : Py_single_input), d, d, NULL);
|
|
||||||
if (v == NULL) {
|
|
||||||
PyObject *exception, *v, *tb;
|
|
||||||
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
|
||||||
handle_system_exit();
|
|
||||||
}
|
|
||||||
PyErr_Fetch(&exception, &v, &tb);
|
|
||||||
if (exception == NULL)
|
|
||||||
return 0;
|
|
||||||
PyErr_NormalizeException(&exception, &v, &tb);
|
|
||||||
if (tb == NULL) {
|
|
||||||
tb = Py_None;
|
|
||||||
Py_INCREF(tb);
|
|
||||||
}
|
|
||||||
PyException_SetTraceback(v, tb);
|
|
||||||
if (exception == NULL)
|
|
||||||
return 0;
|
|
||||||
PyErr_Clear();
|
|
||||||
|
|
||||||
PyObject *objectsRepresentation = PyObject_Str(v);
|
|
||||||
std::string errorStr = PyUnicode_AsUTF8(objectsRepresentation) + std::string("\n");
|
|
||||||
print(errorStr);
|
|
||||||
Py_DECREF(objectsRepresentation);
|
|
||||||
Py_XDECREF(exception);
|
|
||||||
Py_XDECREF(v);
|
|
||||||
Py_XDECREF(tb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
Py_DECREF(v);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PythonTab::editLineReturnPressed(QString text)
|
|
||||||
{
|
|
||||||
if (initialized)
|
|
||||||
{
|
|
||||||
std::string input = text.toStdString();
|
|
||||||
print(std::string(">>> " + input + "\n"));
|
|
||||||
executePython(input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
|
void PythonTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
|
||||||
|
|
||||||
void PythonTab::clearBuffer() { plainTextEdit->clear(); }
|
void PythonTab::clearBuffer() { console->clear(); }
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include "line_editor.h"
|
#include "line_editor.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
#include "pyconsole.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
@ -38,18 +39,14 @@ class PythonTab : public QWidget
|
|||||||
explicit PythonTab(QWidget *parent = 0);
|
explicit PythonTab(QWidget *parent = 0);
|
||||||
~PythonTab();
|
~PythonTab();
|
||||||
|
|
||||||
private:
|
|
||||||
void print(std::string line);
|
|
||||||
int executePython(std::string &command);
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void editLineReturnPressed(QString text);
|
|
||||||
void showContextMenu(const QPoint &pt);
|
void showContextMenu(const QPoint &pt);
|
||||||
void clearBuffer();
|
void clearBuffer();
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void newContext(Context *ctx);
|
void newContext(Context *ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPlainTextEdit *plainTextEdit;
|
PythonConsole *console;
|
||||||
LineEditor *lineEdit;
|
|
||||||
QMenu *contextMenu;
|
QMenu *contextMenu;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user