From 1ba8e03f049a6df454cc5251de47a49d67e7dc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Wed, 13 Dec 2017 18:36:00 +0100 Subject: [PATCH] Basic Jupyter Integration --- src/CMakeLists.txt | 4 +-- src/MainWindow.cpp | 6 +++- src/MainWindow.h | 6 ++-- src/MainWindow.ui | 9 +++++ src/cutter.pro | 14 +++++--- src/utils/JupyterConnection.cpp | 61 +++++++++++++++++++++++++++++++++ src/utils/JupyterConnection.h | 31 +++++++++++++++++ src/widgets/JupyterWidget.cpp | 52 ++++++++++++++++++++++++++++ src/widgets/JupyterWidget.h | 53 ++++++++++++++++++++++++++++ src/widgets/JupyterWidget.ui | 38 ++++++++++++++++++++ 10 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 src/utils/JupyterConnection.cpp create mode 100644 src/utils/JupyterConnection.h create mode 100644 src/widgets/JupyterWidget.cpp create mode 100644 src/widgets/JupyterWidget.h create mode 100644 src/widgets/JupyterWidget.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d27d211..11e8abd8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) -find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Svg) +find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Svg WebEngineWidgets) if(WIN32) @@ -65,6 +65,6 @@ add_definitions("-DAPP_VERSION=\"${CUTTER_VERSION_FULL}\"") add_executable(cutter ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES}) -qt5_use_modules(cutter Core Widgets Gui Svg) +qt5_use_modules(cutter Core Widgets Gui Svg WebEngineWidgets) target_link_libraries(cutter ${RADARE2_LIBRARIES}) diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 153ae1c3..bb1cfae4 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -66,6 +66,7 @@ #include "widgets/ClassesWidget.h" #include "widgets/ResourcesWidget.h" #include "widgets/VTablesWidget.h" +#include "widgets/JupyterWidget.h" // graphics #include @@ -219,6 +220,7 @@ void MainWindow::initUI() ADD_DOCK(CommentsWidget, commentsDock, ui->actionComments); ADD_DOCK(StringsWidget, stringsDock, ui->actionStrings); ADD_DOCK(FlagsWidget, flagsDock, ui->actionFlags); + ADD_DOCK(JupyterWidget, jupyterDock, ui->actionJupyter); ADD_DOCK(Notepad, notepadDock, ui->actionNotepad); ADD_DOCK(Dashboard, dashboardDock, ui->actionDashboard); ADD_DOCK(SdbDock, sdbDock, ui->actionSDBBrowser); @@ -548,6 +550,7 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, classesDock); tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, vTablesDock); + tabifyDockWidget(dashboardDock, jupyterDock); updateDockActionsChecked(); } @@ -587,7 +590,8 @@ void MainWindow::showDefaultDocks() sidebarDock, hexdumpDock, pseudocodeDock, - dashboardDock + dashboardDock, + jupyterDock }; for (auto w : dockWidgets) diff --git a/src/MainWindow.h b/src/MainWindow.h index 1b9c6fb9..d90606cb 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -40,7 +40,7 @@ class DisassemblerGraphView; class ClassesWidget; class ResourcesWidget; class VTablesWidget; - +class JupyterWidget; class QDockWidget; namespace Ui @@ -151,6 +151,7 @@ private slots: private: CutterCore *core; + bool panelLock; bool tabsOnTop; ut64 hexdumpTopOffset; @@ -191,7 +192,8 @@ private: DisassemblerGraphView *graphView = nullptr; QDockWidget *asmDock = nullptr; QDockWidget *calcDock = nullptr; - NewFileDialog *newFileDialog = nullptr; + NewFileDialog *newFileDialog = nullptr; + JupyterWidget *jupyterDock = nullptr; void toggleDockWidget(QDockWidget *dock_widget, bool show); diff --git a/src/MainWindow.ui b/src/MainWindow.ui index fbec1117..f6c0b4ed 100644 --- a/src/MainWindow.ui +++ b/src/MainWindow.ui @@ -260,6 +260,7 @@ border-top: 0px; + @@ -1062,6 +1063,14 @@ QToolButton:pressed { Show/Hide VTables panel + + + true + + + Jupyter + + diff --git a/src/cutter.pro b/src/cutter.pro index 010fd81a..a6e49cac 100644 --- a/src/cutter.pro +++ b/src/cutter.pro @@ -7,7 +7,7 @@ VERSION = 1.2 ICON = img/cutter.icns -QT += core gui widgets svg +QT += core gui widgets svg webenginewidgets QT_CONFIG -= no-pkg-config CONFIG += c++11 @@ -91,7 +91,9 @@ SOURCES += \ widgets/ClassesWidget.cpp \ widgets/ResourcesWidget.cpp \ widgets/VTablesWidget.cpp \ - CutterApplication.cpp + CutterApplication.cpp \ + utils/JupyterConnection.cpp \ + widgets/JupyterWidget.cpp HEADERS += \ cutter.h \ @@ -152,7 +154,9 @@ HEADERS += \ widgets/ClassesWidget.h \ widgets/ResourcesWidget.h \ CutterApplication.h \ - widgets/VTablesWidget.h + widgets/VTablesWidget.h \ + utils/JupyterConnection.h \ + widgets/JupyterWidget.h FORMS += \ dialogs/AboutDialog.ui \ @@ -189,7 +193,9 @@ FORMS += \ widgets/QuickFilterView.ui \ widgets/PseudocodeWidget.ui \ widgets/ClassesWidget.ui \ - widgets/VTablesWidget.ui + widgets/VTablesWidget.ui \ + widgets/PseudocodeWidget.ui \ + widgets/JupyterWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/utils/JupyterConnection.cpp b/src/utils/JupyterConnection.cpp new file mode 100644 index 00000000..a98ef8fd --- /dev/null +++ b/src/utils/JupyterConnection.cpp @@ -0,0 +1,61 @@ + +#include +#include +#include + +#include "JupyterConnection.h" + +JupyterConnection::JupyterConnection(QObject *parent) : QObject(parent) +{ + process = nullptr; +} + +JupyterConnection::~JupyterConnection() +{ +} + +const char *urlPy = "from notebook import notebookapp\n" + "import json\n" + "import time\n" + "\n" + "time.sleep(3)\n" + "\n" + "servers = [si for si in notebookapp.list_running_servers()]\n" + "print(json.dumps(servers))"; + +void JupyterConnection::start() +{ + process = new QProcess(this); + connect(process, &QProcess::readyReadStandardError, this, &JupyterConnection::readStandardError); + connect(process, &QProcess::readyReadStandardOutput, this, &JupyterConnection::readStandardOutput); + process->start("jupyter", {"notebook", "--no-browser", "-y"}); + + urlProcess = new QProcess(this); + connect(urlProcess, &QProcess::readyReadStandardOutput, this, &JupyterConnection::readUrlStandardOutput); + urlProcess->start("python3", {"-c", urlPy}); +} + +void JupyterConnection::readStandardError() +{ + auto data = process->readAllStandardError(); + printf("jupyter stderr: %s\n", data.constData()); +} + +void JupyterConnection::readStandardOutput() +{ + auto data = process->readAllStandardOutput(); + printf("jupyter stdout: %s\n", data.constData()); +} + +void JupyterConnection::readUrlStandardOutput() +{ + QJsonDocument doc = QJsonDocument::fromJson(urlProcess->readAllStandardOutput()); + + for(QJsonValue value : doc.array()) + { + QJsonObject serverObject = value.toObject(); + QString url = serverObject["url"].toString() + "?token=" + serverObject["token"].toString(); + emit urlReceived(url); + break; + } +} diff --git a/src/utils/JupyterConnection.h b/src/utils/JupyterConnection.h new file mode 100644 index 00000000..a123f026 --- /dev/null +++ b/src/utils/JupyterConnection.h @@ -0,0 +1,31 @@ + +#ifndef JUPYTERCONNECTION_H +#define JUPYTERCONNECTION_H + +#include + +class JupyterConnection : public QObject +{ + Q_OBJECT + +public: + JupyterConnection(QObject *parent = nullptr); + ~JupyterConnection(); + + void start(); + +signals: + void urlReceived(const QString &url); + +private: + QProcess *process; + QProcess *urlProcess; + +private slots: + void readStandardError(); + void readStandardOutput(); + + void readUrlStandardOutput(); +}; + +#endif //JUPYTERCONNECTION_H diff --git a/src/widgets/JupyterWidget.cpp b/src/widgets/JupyterWidget.cpp new file mode 100644 index 00000000..c23defb4 --- /dev/null +++ b/src/widgets/JupyterWidget.cpp @@ -0,0 +1,52 @@ + +#include "ui_JupyterWidget.h" + +#include "JupyterWidget.h" + +#include + +JupyterWidget::JupyterWidget(QWidget *parent, Qt::WindowFlags flags) : + QDockWidget(parent, flags), + ui(new Ui::JupyterWidget) +{ + ui->setupUi(this); + + jupyter = new JupyterConnection(this); + connect(jupyter, &JupyterConnection::urlReceived, this, &JupyterWidget::urlReceived); + jupyter->start(); +} + +JupyterWidget::~JupyterWidget() +{ +} + +JupyterWebView *JupyterWidget::createNewTab() +{ + auto webView = new JupyterWebView(this); + ui->tabWidget->addTab(webView, "Tab"); + return webView; +} + + +void JupyterWidget::urlReceived(const QString &url) +{ + createNewTab()->load(QUrl(url)); +} + + + +JupyterWebView::JupyterWebView(JupyterWidget *mainWidget, QWidget *parent) : QWebEngineView(parent) +{ + this->mainWidget = mainWidget; +} + +QWebEngineView *JupyterWebView::createWindow(QWebEnginePage::WebWindowType type) +{ + switch (type) + { + case QWebEnginePage::WebBrowserTab: + return mainWidget->createNewTab(); + default: + return nullptr; + } +} diff --git a/src/widgets/JupyterWidget.h b/src/widgets/JupyterWidget.h new file mode 100644 index 00000000..0b1bc931 --- /dev/null +++ b/src/widgets/JupyterWidget.h @@ -0,0 +1,53 @@ + +#ifndef JUPYTERWIDGET_H +#define JUPYTERWIDGET_H + +#include + +#include +#include + +#include "utils/JupyterConnection.h" + +namespace Ui +{ + class JupyterWidget; +} + +class JupyterWebView; + +class JupyterWidget : public QDockWidget +{ +Q_OBJECT + +public: + JupyterWidget(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~JupyterWidget(); + + JupyterWebView *createNewTab(); + +private slots: + void urlReceived(const QString &url); + +private: + std::unique_ptr ui; + + JupyterConnection *jupyter; +}; + + +class JupyterWebView : public QWebEngineView +{ +Q_OBJECT + +public: + JupyterWebView(JupyterWidget *mainWidget, QWidget *parent = nullptr); + +protected: + QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override; + +private: + JupyterWidget *mainWidget; +}; + +#endif //JUPYTERWIDGET_H diff --git a/src/widgets/JupyterWidget.ui b/src/widgets/JupyterWidget.ui new file mode 100644 index 00000000..3f01039c --- /dev/null +++ b/src/widgets/JupyterWidget.ui @@ -0,0 +1,38 @@ + + + JupyterWidget + + + + 0 + 0 + 644 + 484 + + + + Jupyter + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + +