diff --git a/gui/gui.cmake b/gui/gui.cmake index 961d498a..08a2c0b4 100644 --- a/gui/gui.cmake +++ b/gui/gui.cmake @@ -12,6 +12,7 @@ qt5_generate_moc(gui/fpgaviewwidget.h ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_ qt5_generate_moc(gui/pythontab.h ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_pythontab.cc) qt5_generate_moc(gui/infotab.h ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_infotab.cc) qt5_generate_moc(gui/designwidget.h ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_designwidget.cc) +qt5_generate_moc(gui/line_editor.h ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_line_editor.cc) set(GENERATED_MOC_FILES ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_mainwindow.cc @@ -19,6 +20,7 @@ set(GENERATED_MOC_FILES ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_pythontab.cc ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_infotab.cc ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_designwidget.cc + ${CMAKE_CURRENT_BINARY_DIR}/generated/moc_line_editor.cc ) qt5_add_resources_custom(GUI_RESOURCE_FILES gui/nextpnr.qrc) diff --git a/gui/line_editor.cc b/gui/line_editor.cc new file mode 100644 index 00000000..b5ed955f --- /dev/null +++ b/gui/line_editor.cc @@ -0,0 +1,67 @@ +#include "line_editor.h" + +#include + +LineEditor::LineEditor(QWidget *parent) : QLineEdit(parent), index(0) +{ + setContextMenuPolicy(Qt::CustomContextMenu); + QAction *clearAction = new QAction("Clear &history", this); + clearAction->setStatusTip("Clears line edit history"); + connect(clearAction, SIGNAL(triggered()), this, SLOT(clearHistory())); + contextMenu = createStandardContextMenu(); + contextMenu->addSeparator(); + contextMenu->addAction(clearAction); + + connect(this, SIGNAL(returnPressed()), SLOT(textInserted())); + connect(this, SIGNAL(customContextMenuRequested(const QPoint)), this, + SLOT(showContextMenu(const QPoint))); +} + +void LineEditor::keyPressEvent(QKeyEvent *ev) +{ + if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { + if (lines.empty()) + return; + + if (ev->key() == Qt::Key_Up) + index--; + if (ev->key() == Qt::Key_Down) + index++; + + if (index < 0) + index = 0; + if (index >= lines.size()) { + index = lines.size(); + clear(); + return; + } + setText(lines[index]); + } else if (ev->key() == Qt::Key_Escape) { + clear(); + return; + } + QLineEdit::keyPressEvent(ev); +} + +void LineEditor::textInserted() +{ + if (lines.empty() || lines.back() != text()) + lines += text(); + if (lines.size() > 100) + lines.removeFirst(); + index = lines.size(); + clear(); + Q_EMIT textLineInserted(lines.back()); +} + +void LineEditor::showContextMenu(const QPoint &pt) +{ + contextMenu->exec(mapToGlobal(pt)); +} + +void LineEditor::clearHistory() +{ + lines.clear(); + index = 0; + clear(); +} \ No newline at end of file diff --git a/gui/line_editor.h b/gui/line_editor.h new file mode 100644 index 00000000..15b675f9 --- /dev/null +++ b/gui/line_editor.h @@ -0,0 +1,31 @@ +#ifndef LINE_EDITOR_H +#define LINE_EDITOR_H + +#include +#include + +class LineEditor : public QLineEdit +{ + Q_OBJECT + + public: + explicit LineEditor(QWidget *parent = 0); + + private Q_SLOTS: + void textInserted(); + void showContextMenu(const QPoint &pt); + void clearHistory(); + + Q_SIGNALS: + void textLineInserted(QString); + + protected: + void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; + + private: + int index; + QStringList lines; + QMenu *contextMenu; +}; + +#endif // LINE_EDITOR_H diff --git a/gui/pythontab.cc b/gui/pythontab.cc index 3764696e..04db056d 100644 --- a/gui/pythontab.cc +++ b/gui/pythontab.cc @@ -15,7 +15,7 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent) f.setStyleHint(QFont::Monospace); plainTextEdit->setFont(f); - lineEdit = new QLineEdit(); + lineEdit = new LineEditor(); lineEdit->setMinimumHeight(30); lineEdit->setMaximumHeight(30); lineEdit->setFont(f); @@ -25,8 +25,8 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent) mainLayout->addWidget(lineEdit, 1, 0); setLayout(mainLayout); - connect(lineEdit, SIGNAL(returnPressed()), this, - SLOT(editLineReturnPressed())); + connect(lineEdit, SIGNAL(textLineInserted(QString)), this, + SLOT(editLineReturnPressed(QString))); write = [this](std::string s) { plainTextEdit->moveCursor(QTextCursor::End); @@ -34,11 +34,22 @@ PythonTab::PythonTab(QWidget *parent) : QWidget(parent) plainTextEdit->moveCursor(QTextCursor::End); }; emb::set_stdout(write); + + char buff[1024]; + sprintf(buff, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform()); + print(buff); +} + +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) +int PythonTab::executePython(std::string &command) { PyObject *m, *d, *v; m = PyImport_AddModule("__main__"); @@ -70,9 +81,7 @@ int PythonTab::executePython(std::string command) PyObject *objectsRepresentation = PyObject_Str(v); std::string errorStr = PyUnicode_AsUTF8(objectsRepresentation) + std::string("\n"); - plainTextEdit->moveCursor(QTextCursor::End); - plainTextEdit->insertPlainText(errorStr.c_str()); - plainTextEdit->moveCursor(QTextCursor::End); + print(errorStr); Py_DECREF(objectsRepresentation); Py_XDECREF(exception); Py_XDECREF(v); @@ -83,13 +92,9 @@ int PythonTab::executePython(std::string command) return 0; } -void PythonTab::editLineReturnPressed() +void PythonTab::editLineReturnPressed(QString text) { - std::string input = lineEdit->text().toStdString(); - plainTextEdit->moveCursor(QTextCursor::End); - plainTextEdit->insertPlainText(std::string(">>> " + input + "\n").c_str()); - plainTextEdit->moveCursor(QTextCursor::End); - plainTextEdit->update(); - lineEdit->clear(); + std::string input = text.toStdString(); + print(std::string(">>> " + input + "\n")); int error = executePython(input); } diff --git a/gui/pythontab.h b/gui/pythontab.h index 4290d277..3876b3df 100644 --- a/gui/pythontab.h +++ b/gui/pythontab.h @@ -4,6 +4,7 @@ #include #include #include "emb.h" +#include "line_editor.h" #include "nextpnr.h" // FIXME @@ -17,13 +18,14 @@ class PythonTab : public QWidget explicit PythonTab(QWidget *parent = 0); private: - int executePython(std::string command); + void print(std::string line); + int executePython(std::string &command); private Q_SLOTS: - void editLineReturnPressed(); + void editLineReturnPressed(QString text); private: QPlainTextEdit *plainTextEdit; - QLineEdit *lineEdit; + LineEditor *lineEdit; emb::stdout_write_type write; };