diff options
Diffstat (limited to 'gui')
-rw-r--r-- | gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gui/basewindow.cc | 2 | ||||
-rw-r--r-- | gui/line_editor.cc | 56 | ||||
-rw-r--r-- | gui/line_editor.h | 11 | ||||
-rw-r--r-- | gui/pyconsole.cc | 82 | ||||
-rw-r--r-- | gui/pyconsole.h | 58 | ||||
-rw-r--r-- | gui/pythontab.cc | 41 | ||||
-rw-r--r-- | gui/pythontab.h | 8 |
8 files changed, 249 insertions, 10 deletions
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 5ac4d955..2e8e367e 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -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() diff --git a/gui/basewindow.cc b/gui/basewindow.cc index fd9d36f4..b76527e1 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -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();
diff --git a/gui/line_editor.cc b/gui/line_editor.cc index 9d9dac25..3c7ebe94 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -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
\ No newline at end of file +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 diff --git a/gui/line_editor.h b/gui/line_editor.h index 91837182..5a57129b 100644 --- a/gui/line_editor.h +++ b/gui/line_editor.h @@ -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 diff --git a/gui/pyconsole.cc b/gui/pyconsole.cc new file mode 100644 index 00000000..6da06b7e --- /dev/null +++ b/gui/pyconsole.cc @@ -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 diff --git a/gui/pyconsole.h b/gui/pyconsole.h new file mode 100644 index 00000000..60f10672 --- /dev/null +++ b/gui/pyconsole.h @@ -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 diff --git a/gui/pythontab.cc b/gui/pythontab.cc index 897f87b3..5c349d7c 100644 --- a/gui/pythontab.cc +++ b/gui/pythontab.cc @@ -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
\ No newline at end of file +#endif // NO_PYTHON
diff --git a/gui/pythontab.h b/gui/pythontab.h index 4b22e6a9..3fd12981 100644 --- a/gui/pythontab.h +++ b/gui/pythontab.h @@ -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
|