From ee2ed461e6f2bab10234e437276c7b312437bf0c Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 16 Jul 2018 21:15:49 +0200 Subject: [PATCH] Added Yosys tab with interactive console --- gui/basewindow.cc | 23 +++++++++++ gui/basewindow.h | 2 + gui/line_editor.cc | 1 - gui/yosys_edit.cc | 96 ++++++++++++++++++++++++++++++++++++++++++++++ gui/yosys_edit.h | 57 +++++++++++++++++++++++++++ gui/yosystab.cc | 91 +++++++++++++++++++++++++++++++++++++++++++ gui/yosystab.h | 59 ++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 gui/yosys_edit.cc create mode 100644 gui/yosys_edit.h create mode 100644 gui/yosystab.cc create mode 100644 gui/yosystab.h diff --git a/gui/basewindow.cc b/gui/basewindow.cc index 07b71105..af048f0e 100644 --- a/gui/basewindow.cc +++ b/gui/basewindow.cc @@ -29,6 +29,7 @@ #include "log.h" #include "mainwindow.h" #include "pythontab.h" +#include "yosystab.h" static void initBasenameResource() { Q_INIT_RESOURCE(base); } @@ -73,8 +74,12 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent connect(this, SIGNAL(contextChanged(Context *)), console, SLOT(newContext(Context *))); centralTabWidget = new QTabWidget(); + centralTabWidget->setTabsClosable(true); + connect(centralTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + FPGAViewWidget *fpgaView = new FPGAViewWidget(); centralTabWidget->addTab(fpgaView, "Graphics"); + centralTabWidget->tabBar()->tabButton(0, QTabBar::RightSide)->resize(0, 0); connect(this, SIGNAL(contextChanged(Context *)), fpgaView, SLOT(newContext(Context *))); connect(designview, SIGNAL(selected(std::vector)), fpgaView, @@ -95,6 +100,8 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr context, QWidget *parent BaseMainWindow::~BaseMainWindow() {} +void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); } + void BaseMainWindow::displaySplash() { splash = new QSplashScreen(); @@ -140,6 +147,10 @@ void BaseMainWindow::createMenusAndBars() actionExit->setStatusTip("Exit the application"); connect(actionExit, SIGNAL(triggered()), this, SLOT(close())); + QAction *actionYosys = new QAction("Yosys", this); + actionYosys->setStatusTip("Run Yosys"); + connect(actionYosys, SIGNAL(triggered()), this, SLOT(yosys())); + QAction *actionAbout = new QAction("About", this); menuBar = new QMenuBar(); @@ -172,6 +183,18 @@ void BaseMainWindow::createMenusAndBars() mainToolBar->addAction(actionNew); mainToolBar->addAction(actionOpen); mainToolBar->addAction(actionSave); + mainToolBar->addAction(actionYosys); } +void BaseMainWindow::yosys() +{ + QString folder = QFileDialog::getExistingDirectory(0, ("Select Work Folder"), QDir::currentPath(), + QFileDialog::ShowDirsOnly); + if (!folder.isEmpty() && !folder.isNull()) { + YosysTab *yosysTab = new YosysTab(folder); + yosysTab->setAttribute(Qt::WA_DeleteOnClose); + centralTabWidget->addTab(yosysTab, "Yosys"); + centralTabWidget->setCurrentWidget(yosysTab); + } +} NEXTPNR_NAMESPACE_END diff --git a/gui/basewindow.h b/gui/basewindow.h index 18b5339e..087880ed 100644 --- a/gui/basewindow.h +++ b/gui/basewindow.h @@ -55,10 +55,12 @@ class BaseMainWindow : public QMainWindow protected Q_SLOTS: void writeInfo(std::string text); void displaySplashMessage(std::string msg); + void closeTab(int index); virtual void new_proj() = 0; virtual void open_proj() = 0; virtual bool save_proj() = 0; + void yosys(); Q_SIGNALS: void contextChanged(Context *ctx); diff --git a/gui/line_editor.cc b/gui/line_editor.cc index 425f2876..23415092 100644 --- a/gui/line_editor.cc +++ b/gui/line_editor.cc @@ -48,7 +48,6 @@ void LineEditor::keyPressEvent(QKeyEvent *ev) QToolTip::hideText(); if (lines.empty()) return; - printf("Key_Up\n"); if (ev->key() == Qt::Key_Up) index--; if (ev->key() == Qt::Key_Down) diff --git a/gui/yosys_edit.cc b/gui/yosys_edit.cc new file mode 100644 index 00000000..72d2430d --- /dev/null +++ b/gui/yosys_edit.cc @@ -0,0 +1,96 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * 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. + * + */ + +#include "yosys_edit.h" +#include +#include + +NEXTPNR_NAMESPACE_BEGIN + +YosysLineEditor::YosysLineEditor(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 YosysLineEditor::keyPressEvent(QKeyEvent *ev) +{ + + if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { + QToolTip::hideText(); + 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) { + QToolTip::hideText(); + clear(); + return; + } else if (ev->key() == Qt::Key_Tab) { + return; + } + QToolTip::hideText(); + + QLineEdit::keyPressEvent(ev); +} + +// This makes TAB work +bool YosysLineEditor::focusNextPrevChild(bool next) { return false; } + +void YosysLineEditor::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 YosysLineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } + +void YosysLineEditor::clearHistory() +{ + lines.clear(); + index = 0; + clear(); +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/yosys_edit.h b/gui/yosys_edit.h new file mode 100644 index 00000000..05e4ae36 --- /dev/null +++ b/gui/yosys_edit.h @@ -0,0 +1,57 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * 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 YOSYS_EDIT_H +#define YOSYS_EDIT_H + +#include +#include +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +class YosysLineEditor : public QLineEdit +{ + Q_OBJECT + + public: + explicit YosysLineEditor(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; + bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE; + + private: + int index; + QStringList lines; + QMenu *contextMenu; +}; + +NEXTPNR_NAMESPACE_END + +#endif // YOSYS_EDIT_H diff --git a/gui/yosystab.cc b/gui/yosystab.cc new file mode 100644 index 00000000..1661c74f --- /dev/null +++ b/gui/yosystab.cc @@ -0,0 +1,91 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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. + * + */ + +#include "yosystab.h" +#include + +NEXTPNR_NAMESPACE_BEGIN + +YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent) +{ + QFont f("unexistent"); + f.setStyleHint(QFont::Monospace); + + console = new QPlainTextEdit(); + console->setMinimumHeight(100); + console->setReadOnly(true); + console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + console->setFont(f); + + console->setContextMenuPolicy(Qt::CustomContextMenu); + QAction *clearAction = new QAction("Clear &buffer", this); + clearAction->setStatusTip("Clears display buffer"); + connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer())); + contextMenu = console->createStandardContextMenu(); + contextMenu->addSeparator(); + contextMenu->addAction(clearAction); + connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint))); + + lineEdit = new YosysLineEditor(); + lineEdit->setMinimumHeight(30); + lineEdit->setMaximumHeight(30); + lineEdit->setFont(f); + lineEdit->setFocus(); + lineEdit->setPlaceholderText("yosys>"); + 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); + + process = new QProcess(); + connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError())); + connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput())); + process->setWorkingDirectory(folder); + process->start("yosys"); +} + +YosysTab::~YosysTab() +{ + process->terminate(); + process->waitForFinished(1000); // in ms + process->kill(); + process->close(); +} + +void YosysTab::displayString(QString text) +{ + QTextCursor cursor = console->textCursor(); + cursor.movePosition(QTextCursor::End); + cursor.insertText(text); + cursor.movePosition(QTextCursor::End); + console->setTextCursor(cursor); +} + +void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); } +void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); } + +void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); } + +void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); } + +void YosysTab::clearBuffer() { console->clear(); } + +NEXTPNR_NAMESPACE_END diff --git a/gui/yosystab.h b/gui/yosystab.h new file mode 100644 index 00000000..1c668d15 --- /dev/null +++ b/gui/yosystab.h @@ -0,0 +1,59 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 YOSYSTAB_H +#define YOSYSTAB_H + +#include +#include +#include +#include +#include "nextpnr.h" +#include "yosys_edit.h" + +NEXTPNR_NAMESPACE_BEGIN + +class YosysTab : public QWidget +{ + Q_OBJECT + + public: + explicit YosysTab(QString folder, QWidget *parent = 0); + ~YosysTab(); + + private: + void displayString(QString text); + private Q_SLOTS: + void showContextMenu(const QPoint &pt); + void editLineReturnPressed(QString text); + void onReadyReadStandardOutput(); + void onReadyReadStandardError(); + public Q_SLOTS: + void clearBuffer(); + + private: + QPlainTextEdit *console; + YosysLineEditor *lineEdit; + QMenu *contextMenu; + QProcess *process; +}; + +NEXTPNR_NAMESPACE_END + +#endif // YOSYSTAB_H