From 1710829267ed90fa6959f895c9038462dcc747d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=A4rkl?=
Date: Mon, 25 Mar 2019 21:43:00 +0100
Subject: [PATCH] Remove Jupyter Integration (#1398)
* Remove Jupyter Integration
Replaced by https://github.com/radareorg/cutter-jupyter
* Remove duplicate vars in .appveyor.yml
---
.appveyor.yml | 4 +-
.travis.yml | 9 --
build.sh | 1 -
docs/source/building.rst | 22 +---
meson.py | 8 +-
scripts/bundle_python.ps1 | 1 -
scripts/pip_requirements.txt | 3 -
src/CMakeLists.txt | 19 ----
src/Cutter.pro | 32 ------
src/CutterApplication.cpp | 3 -
src/Main.cpp | 2 +
src/common/JupyterConnection.cpp | 148 -------------------------
src/common/JupyterConnection.h | 54 ----------
src/common/NestedIPyKernel.cpp | 87 ---------------
src/common/NestedIPyKernel.h | 38 -------
src/common/PythonAPI.cpp | 114 --------------------
src/common/PythonManager.cpp | 3 -
src/core/MainWindow.cpp | 13 ---
src/core/MainWindow.h | 6 --
src/core/MainWindow.ui | 9 --
src/dialogs/AboutDialog.cpp | 8 +-
src/meson.build | 10 --
src/meson_options.txt | 2 -
src/python/cutter_ipykernel.py | 84 ---------------
src/python/cutter_jupyter.py | 140 ------------------------
src/resources.qrc | 2 -
src/widgets/JupyterWidget.cpp | 179 -------------------------------
src/widgets/JupyterWidget.h | 78 --------------
src/widgets/JupyterWidget.ui | 38 -------
29 files changed, 11 insertions(+), 1106 deletions(-)
delete mode 100644 scripts/pip_requirements.txt
delete mode 100644 src/common/JupyterConnection.cpp
delete mode 100644 src/common/JupyterConnection.h
delete mode 100644 src/common/NestedIPyKernel.cpp
delete mode 100644 src/common/NestedIPyKernel.h
delete mode 100644 src/python/cutter_ipykernel.py
delete mode 100644 src/python/cutter_jupyter.py
delete mode 100644 src/widgets/JupyterWidget.cpp
delete mode 100644 src/widgets/JupyterWidget.h
delete mode 100644 src/widgets/JupyterWidget.ui
diff --git a/.appveyor.yml b/.appveyor.yml
index 20bf8d73..564f2406 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -49,8 +49,8 @@ before_build:
# Build config
build_script:
- - cmd: if defined QMAKE ( call prepare_r2.bat && call build.bat CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON_BINDINGS=false CUTTER_ENABLE_JUPYTER=true CUTTER_ENABLE_QTWEBENGINE=false CUTTER_APPVEYOR_R2DEC=true CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON_BINDINGS=true SHIBOKEN_EXECUTABLE="%CUTTER_DEPS_DIR%\pyside\bin\shiboken2.exe" SHIBOKEN_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/shiboken2" SHIBOKEN_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/shiboken2.cp36-win_amd64.lib" PYSIDE_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/PySide2" PYSIDE_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/pyside2.cp36-win_amd64.lib" PYSIDE_TYPESYSTEMS="%CUTTER_DEPS_DIR%/pyside/share/PySide2/typesystems")
- - cmd: if defined MESON ( python meson.py --release --dist=%ARTIFACT_PATH% --backend=%BACKEND% --python --jupyter )
+ - cmd: if defined QMAKE ( call prepare_r2.bat && call build.bat CUTTER_APPVEYOR_R2DEC=true CUTTER_ENABLE_PYTHON=true CUTTER_ENABLE_PYTHON_BINDINGS=true SHIBOKEN_EXECUTABLE="%CUTTER_DEPS_DIR%\pyside\bin\shiboken2.exe" SHIBOKEN_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/shiboken2" SHIBOKEN_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/shiboken2.cp36-win_amd64.lib" PYSIDE_INCLUDEDIR="%CUTTER_DEPS_DIR%/pyside/include/PySide2" PYSIDE_LIBRARY="%CUTTER_DEPS_DIR%/pyside/lib/pyside2.cp36-win_amd64.lib" PYSIDE_TYPESYSTEMS="%CUTTER_DEPS_DIR%/pyside/share/PySide2/typesystems")
+ - cmd: if defined MESON ( python meson.py --release --dist=%ARTIFACT_PATH% --backend=%BACKEND% --python )
after_build:
- cmd: if defined QMAKE ( set "PATH=%CD%\r2_dist_%ARCH%;%PATH%" && powershell scripts\bundle_r2dec.ps1 "%CD%\%ARTIFACT_PATH%" )
diff --git a/.travis.yml b/.travis.yml
index 0202d5e6..d64b0d58 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -68,7 +68,6 @@ addons:
install:
- scripts/fetch_deps.sh
- source cutter-deps/env.sh
- - python3 -m pip install -r scripts/pip_requirements.txt
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH=/usr/local/opt/llvm/bin:$PATH; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH"; fi
@@ -90,8 +89,6 @@ script:
qmake
CUTTER_ENABLE_PYTHON=true
CUTTER_ENABLE_PYTHON_BINDINGS=true
- CUTTER_ENABLE_JUPYTER=true
- CUTTER_ENABLE_QTWEBENGINE=false
PREFIX=/usr
APPIMAGE=1
../src &&
@@ -104,8 +101,6 @@ script:
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
- -DCUTTER_ENABLE_JUPYTER=ON
- -DCUTTER_ENABLE_QTWEBENGINE=OFF
../src &&
make -j4;
fi
@@ -114,8 +109,6 @@ script:
qmake
CUTTER_ENABLE_PYTHON=true
CUTTER_ENABLE_PYTHON_BINDINGS=true
- CUTTER_ENABLE_JUPYTER=true
- CUTTER_ENABLE_QTWEBENGINE=false
CUTTER_BUNDLE_R2_APPBUNDLE=true
PYTHON_FRAMEWORK_DIR=$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR
../src &&
@@ -128,8 +121,6 @@ script:
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
-DCUTTER_ENABLE_PYTHON=ON
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
- -DCUTTER_ENABLE_JUPYTER=ON
- -DCUTTER_ENABLE_QTWEBENGINE=OFF
../src &&
make -j4;
fi
diff --git a/build.sh b/build.sh
index 353aeef6..f4c7c7ba 100755
--- a/build.sh
+++ b/build.sh
@@ -9,7 +9,6 @@ ERR=0
BUILD="build"
QMAKE_CONF=""
ROOT_DIR=`pwd`
-#QMAKE_CONF="CUTTER_ENABLE_JUPYTER=false CUTTER_ENABLE_QTWEBENGINE=false"
# Create translations
lrelease ./src/Cutter.pro
diff --git a/docs/source/building.rst b/docs/source/building.rst
index f20cfbc7..5c1ef9c2 100644
--- a/docs/source/building.rst
+++ b/docs/source/building.rst
@@ -24,8 +24,8 @@ Building options
----------------
Note that there are two major building options available:
-- ``CUTTER_ENABLE_JUPYTER`` is used to compile Cutter with bundled Python and Jupyter module
-- ``CUTTER_ENABLE_QTWEBENGINE`` is used to compile Cutter with bundled QtWebEngine (to ease jupyter console usage)
+- ``CUTTER_ENABLE_PYTHON`` compile with Python support
+- ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins!
--------------
@@ -124,24 +124,6 @@ containing bin/, lib/, include/, etc.) and specify it to CMake using
rm CMakeCache.txt # the cache may be polluted with unwanted libraries found before
cmake -DCMAKE_PREFIX_PATH=/opt/Qt/5.9.1/gcc_64 ..
-..
-
- ``ModuleNotFoundError`` upon starting Cutter.
-
-This can be resolved by either: 1. Disabling the optional jupyter
-support during building by modifying ``build.sh`` as follows:
-
-- Uncomment
- ``#QMAKE_CONF="CUTTER_ENABLE_JUPYTER=false CUTTER_ENABLE_QTWEBENGINE=false"``
-- Comment out the prior empty ``QMAKE_CONF=""``
-
-2. Or alternatively by installing the two python dependencies manually
- afterwards via:
-
-::
-
- pip3 install notebook jupyter_client
-
Building with Meson (Windows)
-----------------------------
diff --git a/meson.py b/meson.py
index 259f9f88..3ebdf200 100644
--- a/meson.py
+++ b/meson.py
@@ -48,9 +48,7 @@ def build(args):
cutter_builddir = os.path.join(ROOT, args.dir)
if not os.path.exists(cutter_builddir):
defines = ['-Denable_python=%s' % str(args.python).lower(),
- '-Denable_python_bindings=%s' % str(args.python_bindings).lower(),
- '-Denable_jupyter=%s' % str(args.jupyter).lower(),
- '-Denable_webengine=%s' % str(args.webengine).lower()]
+ '-Denable_python_bindings=%s' % str(args.python_bindings).lower()]
if os.name == 'nt':
defines.append('-Dradare2:r2_incdir=radare2/include')
defines.append('-Dradare2:r2_libdir=radare2/lib')
@@ -79,10 +77,6 @@ def main():
help='Enable Python support')
parser.add_argument('--python-bindings', action='store_true',
help='Enable Python Bindings')
- parser.add_argument('--jupyter', action='store_true',
- help='Enable Jupyter support')
- parser.add_argument('--webengine', action='store_true',
- help='Enable QtWebEngine support')
parser.add_argument('--release', action='store_true',
help='Set the build as Release (remove debug info)')
parser.add_argument('--nobuild', action='store_true',
diff --git a/scripts/bundle_python.ps1 b/scripts/bundle_python.ps1
index bd6ae8d2..80b11d6e 100644
--- a/scripts/bundle_python.ps1
+++ b/scripts/bundle_python.ps1
@@ -12,5 +12,4 @@ Copy-Item .\python_embed\${py_base}.zip -Destination $dist\$py_base
Copy-Item .\python_embed\*.pyd -Destination $dist\$py_base
Copy-Item .\python_embed\sqlite3.dll -Destination $dist\$py_base
Copy-Item .\python_embed\python*.dll -Destination $dist
-& python -m pip install -I --no-compile -t "${dist}\${py_base}\site-packages" jupyter ipykernel==4.8.2 jsonschema==2.6.0 pyzmq==17.1.2 notebook==5.6.0 tornado==5.1.1
[System.IO.File]::WriteAllLines("${dist}\${py_base}._pth", "${py_base}`r`n${py_base}\${py_base}.zip`r`n${py_base}\site-packages")
diff --git a/scripts/pip_requirements.txt b/scripts/pip_requirements.txt
deleted file mode 100644
index bb605b5d..00000000
--- a/scripts/pip_requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-jupyter
-ipykernel==4.10.0
-pyzmq==17.1.2
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index aac6c90d..edc58d88 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,8 +10,6 @@ set(CUTTER_PYTHON_MIN 3.5)
option(CUTTER_ENABLE_PYTHON "Enable Python integration. Requires Python >= ${CUTTER_PYTHON_MIN}." OFF)
option(CUTTER_ENABLE_PYTHON_BINDINGS "Enable generating Python bindings with Shiboken2. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
-option(CUTTER_ENABLE_JUPYTER "Enable Jupyter integration. Unused if CUTTER_ENABLE_PYTHON=OFF." OFF)
-option(CUTTER_ENABLE_QTWEBENGINE "Use QtWebEngine for in-app Jupyter Browser. Unused if CUTTER_ENABLE_JUPYTER=OFF." OFF)
if(NOT CUTTER_ENABLE_PYTHON)
set(CUTTER_ENABLE_PYTHON_BINDINGS OFF)
@@ -45,14 +43,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Svg Network)
-if(CUTTER_ENABLE_JUPYTER AND CUTTER_ENABLE_QTWEBENGINE)
- find_package(Qt5 COMPONENTS WebEngineWidgets)
- if(NOT Qt5_FOUND)
- message(FATAL_ERROR "QtWebEngine could not be found which is required for the in-app Jupyter Browser.
-If you do not want to enable this in-app Browser, re-run CMake with -DCUTTER_ENABLE_QTWEBENGINE=OFF.")
- endif()
- add_definitions(-DCUTTER_ENABLE_QTWEBENGINE)
-endif()
if(WIN32)
@@ -80,10 +70,6 @@ if(CUTTER_ENABLE_PYTHON)
include_directories(${PYTHON_INCLUDE_DIRS})
add_definitions(-DCUTTER_ENABLE_PYTHON)
- if(CUTTER_ENABLE_JUPYTER)
- add_definitions(-DCUTTER_ENABLE_JUPYTER)
- endif()
-
if(CUTTER_ENABLE_PYTHON_BINDINGS)
find_package(PythonInterp REQUIRED)
find_package(Shiboken2 "${Qt5_VERSION}" REQUIRED)
@@ -107,8 +93,6 @@ message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}")
message(STATUS "Options:")
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
-message(STATUS "- Jupyter: ${CUTTER_ENABLE_JUPYTER}")
-message(STATUS "- QtWebEngine: ${CUTTER_ENABLE_QTWEBENGINE}")
message(STATUS "")
@@ -172,7 +156,4 @@ if(CUTTER_ENABLE_PYTHON)
endif()
endif()
-if(CUTTER_ENABLE_PYTHON AND CUTTER_ENABLE_JUPYTER AND CUTTER_ENABLE_QTWEBENGINE)
- target_link_libraries(Cutter Qt5::WebEngineWidgets)
-endif()
diff --git a/src/Cutter.pro b/src/Cutter.pro
index e1d0e770..e92bb16f 100644
--- a/src/Cutter.pro
+++ b/src/Cutter.pro
@@ -43,16 +43,6 @@ equals(CUTTER_ENABLE_PYTHON, true) {
}
}
-!defined(CUTTER_ENABLE_JUPYTER, var) CUTTER_ENABLE_JUPYTER=false
-equals(CUTTER_ENABLE_PYTHON, true) {
- equals(CUTTER_ENABLE_JUPYTER, true) CONFIG += CUTTER_ENABLE_JUPYTER
-}
-
-!defined(CUTTER_ENABLE_QTWEBENGINE, var) CUTTER_ENABLE_QTWEBENGINE=false
-equals(CUTTER_ENABLE_JUPYTER, true) {
- equals(CUTTER_ENABLE_QTWEBENGINE, true) CONFIG += CUTTER_ENABLE_QTWEBENGINE
-}
-
!defined(CUTTER_BUNDLE_R2_APPBUNDLE, var) CUTTER_BUNDLE_R2_APPBUNDLE=false
equals(CUTTER_BUNDLE_R2_APPBUNDLE, true) CONFIG += CUTTER_BUNDLE_R2_APPBUNDLE
@@ -75,21 +65,6 @@ CUTTER_ENABLE_PYTHON_BINDINGS {
message("Python Bindings disabled. (requires CUTTER_ENABLE_PYTHON=true)")
}
-CUTTER_ENABLE_JUPYTER {
- message("Jupyter support enabled.")
- DEFINES += CUTTER_ENABLE_JUPYTER
-} else {
- message("Jupyter support disabled. (requires CUTTER_ENABLE_PYTHON=true)")
-}
-
-CUTTER_ENABLE_QTWEBENGINE {
- message("QtWebEngine support enabled.")
- DEFINES += CUTTER_ENABLE_QTWEBENGINE
- QT += webenginewidgets
-} else {
- message("QtWebEngine support disabled. (requires CUTTER_ENABLE_JUPYTER=true)")
-}
-
INCLUDEPATH *= . core widgets dialogs common plugins
win32 {
@@ -290,10 +265,7 @@ SOURCES += \
widgets/HeadersWidget.cpp \
widgets/SearchWidget.cpp \
CutterApplication.cpp \
- common/JupyterConnection.cpp \
- widgets/JupyterWidget.cpp \
common/PythonAPI.cpp \
- common/NestedIPyKernel.cpp \
dialogs/R2PluginsDialog.cpp \
widgets/CutterDockWidget.cpp \
widgets/CutterTreeWidget.cpp \
@@ -404,10 +376,7 @@ HEADERS += \
widgets/TypesWidget.h \
widgets/HeadersWidget.h \
widgets/SearchWidget.h \
- common/JupyterConnection.h \
- widgets/JupyterWidget.h \
common/PythonAPI.h \
- common/NestedIPyKernel.h \
dialogs/R2PluginsDialog.h \
widgets/CutterDockWidget.h \
widgets/CutterTreeWidget.h \
@@ -496,7 +465,6 @@ FORMS += \
widgets/TypesWidget.ui \
widgets/HeadersWidget.ui \
widgets/SearchWidget.ui \
- widgets/JupyterWidget.ui \
dialogs/R2PluginsDialog.ui \
dialogs/VersionInfoDialog.ui \
widgets/ZignaturesWidget.ui \
diff --git a/src/CutterApplication.cpp b/src/CutterApplication.cpp
index 25b6c1fb..abd24626 100644
--- a/src/CutterApplication.cpp
+++ b/src/CutterApplication.cpp
@@ -1,8 +1,5 @@
#include "common/PythonManager.h"
#include "CutterApplication.h"
-#ifdef CUTTER_ENABLE_JUPYTER
-#include "common/JupyterConnection.h"
-#endif
#include "plugins/PluginManager.h"
#include "CutterConfig.h"
diff --git a/src/Main.cpp b/src/Main.cpp
index 8c2e7c47..ddf1e433 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -34,6 +34,8 @@ int main(int argc, char *argv[])
settings.setValue("settings_migrated", true);
}
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins
+
CutterApplication a(argc, argv);
if (Config()->getAutoUpdateEnabled()) {
diff --git a/src/common/JupyterConnection.cpp b/src/common/JupyterConnection.cpp
deleted file mode 100644
index c76e131a..00000000
--- a/src/common/JupyterConnection.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include
-
-#include "JupyterConnection.h"
-#include "NestedIPyKernel.h"
-#include "PythonManager.h"
-#include "QtResImporter.h"
-
-#include
-#include
-
-Q_GLOBAL_STATIC(JupyterConnection, uniqueInstance)
-
-JupyterConnection *JupyterConnection::getInstance()
-{
- return uniqueInstance;
-}
-
-JupyterConnection::JupyterConnection(QObject *parent) : QObject(parent)
-{
- connect(Python(), &PythonManager::willShutDown, this, &JupyterConnection::stop);
-}
-
-JupyterConnection::~JupyterConnection()
-{
-}
-
-
-void JupyterConnection::start()
-{
- if (notebookInstanceExists) {
- return;
- }
-
- notebookInstanceExists = startJupyterNotebook();
-
- emit urlReceived(getUrl());
-}
-
-void JupyterConnection::stop()
-{
- if (cutterNotebookAppInstance) {
- Python()->restoreThread();
- auto stopFunc = PyObject_GetAttrString(cutterNotebookAppInstance, "stop");
- PyObject_CallObject(stopFunc, nullptr);
- Py_DECREF(cutterNotebookAppInstance);
- Python()->saveThread();
- }
-}
-
-QString JupyterConnection::getUrl()
-{
- if (!notebookInstanceExists) {
- return nullptr;
- }
-
- QString url = getJupyterUrl();
- return url;
-}
-
-long JupyterConnection::startNestedIPyKernel(const QStringList &argv)
-{
- NestedIPyKernel *kernel = NestedIPyKernel::start(argv);
-
- if (!kernel) {
- qWarning() << "Could not start nested IPyKernel.";
- return 0;
- }
-
- long id = nextKernelId++;
- kernels.insert(id, kernel);
-
- return id;
-}
-
-NestedIPyKernel *JupyterConnection::getNestedIPyKernel(long id)
-{
- auto it = kernels.find(id);
- if (it == kernels.end()) {
- return nullptr;
- }
- return *it;
-}
-
-QVariant JupyterConnection::pollNestedIPyKernel(long id)
-{
- auto it = kernels.find(id);
- if (it == kernels.end()) {
- return QVariant(0);
- }
-
- NestedIPyKernel *kernel = *it;
- QVariant v = kernel->poll();
-
- if (!v.isNull()) {
- // if poll of kernel returns anything but None, it has already quit and should be cleaned up
- PyThreadState *subinterpreterState = kernel->getThreadState();
- delete kernel;
- kernels.erase(it);
-
- PyThreadState *parentThreadState = PyThreadState_Swap(subinterpreterState);
- Py_EndInterpreter(subinterpreterState);
- PyThreadState_Swap(parentThreadState);
- }
-
- return v;
-}
-
-bool JupyterConnection::startJupyterNotebook()
-{
- PythonManager::ThreadHolder threadHolder;
-
- if (!cutterJupyterModule) {
- cutterJupyterModule = QtResImport("cutter_jupyter");
- if (!cutterJupyterModule) {
- qWarning() << "Failed to load cutter_jupyter module. Make sure jupyter is installed.";
- return false;
- }
- }
-
- PyObject *startFunc = PyObject_GetAttrString(cutterJupyterModule, "start_jupyter");
- if (!startFunc) {
- qWarning() << "Couldn't get attribute start_jupyter.";
- return false;
- }
-
- cutterNotebookAppInstance = PyObject_CallObject(startFunc, nullptr);
-
- return cutterNotebookAppInstance != nullptr;
-}
-
-QString JupyterConnection::getJupyterUrl()
-{
- Python()->restoreThread();
-
- auto urlWithToken = PyObject_GetAttrString(cutterNotebookAppInstance, "url_with_token");
- auto asciiBytes = PyUnicode_AsASCIIString(urlWithToken);
- auto urlWithTokenString = QString::fromUtf8(PyBytes_AsString(asciiBytes));
- Py_DECREF(asciiBytes);
- Py_DECREF(urlWithToken);
-
- Python()->saveThread();
-
- return urlWithTokenString;
-}
-
-#endif
diff --git a/src/common/JupyterConnection.h b/src/common/JupyterConnection.h
deleted file mode 100644
index 148c8e4d..00000000
--- a/src/common/JupyterConnection.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef JUPYTERCONNECTION_H
-#define JUPYTERCONNECTION_H
-
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include
-#include
-
-class NestedIPyKernel;
-typedef struct _object PyObject;
-
-class JupyterConnection : public QObject
-{
- Q_OBJECT
-
-public:
- static JupyterConnection *getInstance();
-
- JupyterConnection(QObject *parent = nullptr);
- ~JupyterConnection();
-
- void start();
- QString getUrl();
-
- long startNestedIPyKernel(const QStringList &argv);
- NestedIPyKernel *getNestedIPyKernel(long id);
- QVariant pollNestedIPyKernel(long id);
-
-public slots:
- void stop();
-
-signals:
- void urlReceived(const QString &url);
- void creationFailed();
-
-private:
- QMap kernels;
- long nextKernelId = 1;
-
- bool notebookInstanceExists = false;
-
- PyObject *cutterJupyterModule = nullptr;
- PyObject *cutterNotebookAppInstance = nullptr;
-
- bool startJupyterNotebook();
- QString getJupyterUrl();
-};
-
-
-#define Jupyter() (JupyterConnection::getInstance())
-
-#endif // CUTTER_ENABLE_JUPYTER
-
-#endif // JUPYTERCONNECTION_H
diff --git a/src/common/NestedIPyKernel.cpp b/src/common/NestedIPyKernel.cpp
deleted file mode 100644
index 0a4db7a9..00000000
--- a/src/common/NestedIPyKernel.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include
-#include
-
-#include "core/Cutter.h"
-#include "NestedIPyKernel.h"
-#include "QtResImporter.h"
-
-NestedIPyKernel *NestedIPyKernel::start(const QStringList &argv)
-{
- PyThreadState *parentThreadState = PyThreadState_Get();
-
- PyThreadState *threadState = Py_NewInterpreter();
- if (!threadState) {
- qWarning() << "Could not create subinterpreter.";
- return nullptr;
- }
-
- RegQtResImporter();
- auto cutterIPykernelModule = QtResImport("cutter_ipykernel");
- if (!cutterIPykernelModule) {
- qWarning() << "Could not import cutter_ipykernel.";
- return nullptr;
- }
-
- auto kernel = new NestedIPyKernel(cutterIPykernelModule, argv);
-
- PyThreadState_Swap(parentThreadState);
-
- return kernel;
-}
-
-NestedIPyKernel::NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv)
-{
- threadState = PyThreadState_Get();
-
- auto launchFunc = PyObject_GetAttrString(cutterIPykernelModule, "launch_ipykernel");
-
- PyObject *argvListObject = PyList_New(argv.size());
- for (int i = 0; i < argv.size(); i++) {
- QString s = argv[i];
- PyList_SetItem(argvListObject, i, PyUnicode_DecodeUTF8(s.toUtf8().constData(), s.length(),
- nullptr));
- }
-
- kernel = PyObject_CallFunction(launchFunc, "O", argvListObject);
-}
-
-NestedIPyKernel::~NestedIPyKernel()
-{
- auto parentThreadState = PyThreadState_Swap(threadState);
- auto ret = PyObject_CallMethod(kernel, "cleanup", nullptr);
- if (!ret) {
- PyErr_Print();
- }
- PyThreadState_Swap(parentThreadState);
-}
-
-void NestedIPyKernel::sendSignal(long signum)
-{
- auto parentThreadState = PyThreadState_Swap(threadState);
- auto ret = PyObject_CallMethod(kernel, "send_signal", "l", signum);
- if (!ret) {
- PyErr_Print();
- }
- PyThreadState_Swap(parentThreadState);
-}
-
-QVariant NestedIPyKernel::poll()
-{
- QVariant ret;
- auto parentThreadState = PyThreadState_Swap(threadState);
- PyObject *pyRet = PyObject_CallMethod(kernel, "poll", nullptr);
- if (pyRet) {
- if (PyLong_Check(pyRet)) {
- ret = (qlonglong)PyLong_AsLong(pyRet);
- }
- } else {
- PyErr_Print();
- }
- PyThreadState_Swap(parentThreadState);
- return ret;
-}
-
-#endif
diff --git a/src/common/NestedIPyKernel.h b/src/common/NestedIPyKernel.h
deleted file mode 100644
index 03da0661..00000000
--- a/src/common/NestedIPyKernel.h
+++ /dev/null
@@ -1,38 +0,0 @@
-
-#ifndef NESTEDIPYKERNEL_H
-#define NESTEDIPYKERNEL_H
-
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include
-
-struct _object;
-typedef _object PyObject;
-
-struct _ts;
-typedef _ts PyThreadState;
-
-class NestedIPyKernel
-{
-public:
- static NestedIPyKernel *start(const QStringList &argv);
- ~NestedIPyKernel();
-
- void sendSignal(long signum);
- QVariant poll();
-
- PyThreadState *getThreadState()
- {
- return threadState;
- }
-
-private:
- NestedIPyKernel(PyObject *cutterIPykernelModule, const QStringList &argv);
-
- PyThreadState *threadState;
- PyObject *kernel;
-};
-
-#endif
-
-#endif //NESTEDIPYKERNEL_H
diff --git a/src/common/PythonAPI.cpp b/src/common/PythonAPI.cpp
index 37558de7..cd833f5b 100644
--- a/src/common/PythonAPI.cpp
+++ b/src/common/PythonAPI.cpp
@@ -84,118 +84,4 @@ PyObject *PyInit_api()
return PyModule_Create(&CutterModule);
}
-// -----------------------------
-
-#ifdef CUTTER_ENABLE_JUPYTER
-#include "JupyterConnection.h"
-#include "NestedIPyKernel.h"
-
-PyObject *api_internal_launch_ipykernel(PyObject *self, PyObject *args, PyObject *kw)
-{
- Q_UNUSED(self);
- Q_UNUSED(kw);
-
- QStringList argv;
- PyObject *argvListObject;
-
- if (!PyArg_ParseTuple(args, "O", &argvListObject)
- || !PyList_Check(argvListObject)) {
- const char *msg = "Invalid args passed to api_internal_launch_ipykernel().";
- qWarning() << msg;
- PyErr_SetString(PyExc_RuntimeError, msg);
- return nullptr;
- }
-
- for (int i = 0; i < PyList_Size(argvListObject); i++) {
- PyObject *o = PyList_GetItem(argvListObject, i);
- QString s = QString::fromUtf8(PyUnicode_AsUTF8(o));
- argv.append(s);
- }
-
- long id = Jupyter()->startNestedIPyKernel(argv);
-
- return PyLong_FromLong(id);
-}
-
-PyObject *api_internal_kernel_interface_send_signal(PyObject *, PyObject *args)
-{
- long id;
- long signum;
-
- if (!PyArg_ParseTuple(args, "ll", &id, &signum)) {
- const char *msg = "Invalid args passed to api_internal_kernel_interface_send_signal().";
- qWarning() << msg;
- PyErr_SetString(PyExc_RuntimeError, msg);
- return nullptr;
- }
-
- NestedIPyKernel *kernel = Jupyter()->getNestedIPyKernel(id);
- if (kernel) {
- kernel->sendSignal(signum);
- }
-
- Py_RETURN_NONE;
-}
-
-PyObject *api_internal_kernel_interface_poll(PyObject *, PyObject *args)
-{
- long id;
-
- if (!PyArg_ParseTuple(args, "l", &id)) {
- const char *msg = "Invalid args passed to api_internal_kernel_interface_poll().";
- qWarning() << msg;
- PyErr_SetString(PyExc_RuntimeError, msg);
- return nullptr;
- }
-
- QVariant v = Jupyter()->pollNestedIPyKernel(id);
- bool ok;
- auto ret = static_cast(v.toLongLong(&ok));
- if (ok) {
- return PyLong_FromLong(ret);
- } else {
- Py_RETURN_NONE;
- }
-}
-
-PyObject *api_internal_thread_set_async_exc(PyObject *, PyObject *args)
-{
- long id;
- PyObject *exc;
-
- if (!PyArg_ParseTuple(args, "lO", &id, &exc)) {
- const char *msg = "Invalid args passed to api_internal_thread_set_async_exc().";
- qWarning() << msg;
- PyErr_SetString(PyExc_RuntimeError, msg);
- return nullptr;
- }
-
- int ret = PyThreadState_SetAsyncExc(id, exc);
- return PyLong_FromLong(ret);
-}
-
-PyMethodDef CutterInternalMethods[] = {
- {
- "launch_ipykernel", reinterpret_cast((void *)api_internal_launch_ipykernel), METH_VARARGS | METH_KEYWORDS,
- "Launch an IPython Kernel in a subinterpreter"
- },
- {"kernel_interface_send_signal", (PyCFunction)api_internal_kernel_interface_send_signal, METH_VARARGS, ""},
- {"kernel_interface_poll", (PyCFunction)api_internal_kernel_interface_poll, METH_VARARGS, ""},
- {"thread_set_async_exc", (PyCFunction)api_internal_thread_set_async_exc, METH_VARARGS, ""},
- {NULL, NULL, 0, NULL}
-};
-
-PyModuleDef CutterInternalModule = {
- PyModuleDef_HEAD_INIT, "cutter_internal", NULL, -1, CutterInternalMethods,
- NULL, NULL, NULL, NULL
-};
-
-
-PyObject *PyInit_api_internal()
-{
- return PyModule_Create(&CutterInternalModule);
-}
-
-#endif // CUTTER_ENABLE_JUPYTER
-
#endif // CUTTER_ENABLE_PYTHON
diff --git a/src/common/PythonManager.cpp b/src/common/PythonManager.cpp
index 9de6b70a..32620fc7 100644
--- a/src/common/PythonManager.cpp
+++ b/src/common/PythonManager.cpp
@@ -74,9 +74,6 @@ void PythonManager::initialize()
initPythonHome();
PyImport_AppendInittab("_cutter", &PyInit_api);
-#ifdef CUTTER_ENABLE_JUPYTER
- PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
-#endif
PyImport_AppendInittab("_qtres", &PyInit_qtres);
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
PyImport_AppendInittab("CutterBindings", &PyInit_CutterBindings);
diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp
index 200fa2fd..cc988b02 100644
--- a/src/core/MainWindow.cpp
+++ b/src/core/MainWindow.cpp
@@ -52,7 +52,6 @@
#include "widgets/ClassesWidget.h"
#include "widgets/ResourcesWidget.h"
#include "widgets/VTablesWidget.h"
-#include "widgets/JupyterWidget.h"
#include "widgets/HeadersWidget.h"
#include "widgets/ZignaturesWidget.h"
#include "widgets/DebugActions.h"
@@ -268,12 +267,6 @@ void MainWindow::initDocks()
memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
registerRefsDock = new RegisterRefsWidget(this, ui->actionRegisterRefs);
-#ifdef CUTTER_ENABLE_JUPYTER
- jupyterDock = new JupyterWidget(this, ui->actionJupyter);
-#else
- ui->actionJupyter->setEnabled(false);
- ui->actionJupyter->setVisible(false);
-#endif
dashboardDock = new Dashboard(this, ui->actionDashboard);
sdbDock = new SdbWidget(this, ui->actionSDBBrowser);
classesDock = new ClassesWidget(this, ui->actionClasses);
@@ -790,9 +783,6 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, memoryMapDock);
tabifyDockWidget(dashboardDock, breakpointDock);
tabifyDockWidget(dashboardDock, registerRefsDock);
-#ifdef CUTTER_ENABLE_JUPYTER
- tabifyDockWidget(dashboardDock, jupyterDock);
-#endif
updateDockActionsChecked();
}
@@ -827,9 +817,6 @@ void MainWindow::showZenDocks()
hexdumpDock,
searchDock,
importsDock,
-#ifdef CUTTER_ENABLE_JUPYTER
- jupyterDock
-#endif
};
for (auto w : dockWidgets) {
if (zenDocks.contains(w)) {
diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h
index bab32454..4e82bf31 100644
--- a/src/core/MainWindow.h
+++ b/src/core/MainWindow.h
@@ -43,9 +43,6 @@ class TypesWidget;
class HeadersWidget;
class ZignaturesWidget;
class SearchWidget;
-#ifdef CUTTER_ENABLE_JUPYTER
-class JupyterWidget;
-#endif
class QDockWidget;
class DisassemblyWidget;
class GraphWidget;
@@ -251,9 +248,6 @@ private:
NewFileDialog *newFileDialog = nullptr;
QDockWidget *breakpointDock = nullptr;
QDockWidget *registerRefsDock = nullptr;
-#ifdef CUTTER_ENABLE_JUPYTER
- JupyterWidget *jupyterDock = nullptr;
-#endif
void initToolBar();
void initDocks();
diff --git a/src/core/MainWindow.ui b/src/core/MainWindow.ui
index bb736b6d..0d123360 100644
--- a/src/core/MainWindow.ui
+++ b/src/core/MainWindow.ui
@@ -213,7 +213,6 @@ QToolTip {
-
@@ -1107,14 +1106,6 @@ QToolTip {
Show/Hide Zignatures panel
-
-
- true
-
-
- Jupyter
-
-
Export as code
diff --git a/src/dialogs/AboutDialog.cpp b/src/dialogs/AboutDialog.cpp
index e5e1d59d..a2d6e590 100644
--- a/src/dialogs/AboutDialog.cpp
+++ b/src/dialogs/AboutDialog.cpp
@@ -30,15 +30,15 @@ AboutDialog::AboutDialog(QWidget *parent) :
+ tr("Version") + " " CUTTER_VERSION_FULL "
"
+ tr("Using r2-") + R2_GITTAP
+ "" + tr("Optional Features:") + "
"
- + QString("Jupyter: %1
").arg(
-#ifdef CUTTER_ENABLE_JUPYTER
+ + QString("Python: %1
").arg(
+#ifdef CUTTER_ENABLE_PYTHON
"ON"
#else
"OFF"
#endif
)
- + QString("QtWebEngine: %2
").arg(
-#ifdef CUTTER_ENABLE_QTWEBENGINE
+ + QString("Python Bindings: %2
").arg(
+#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
"ON"
#else
"OFF"
diff --git a/src/meson.build b/src/meson.build
index d2a955fb..6a1f8155 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -13,16 +13,6 @@ if get_option('enable_python')
message('Python Bindings are enabled')
feature_define_args += ['-DCUTTER_ENABLE_PYTHON_BINDINGS']
endif
-
- if get_option('enable_jupyter')
- message('Jupyter support enabled')
- feature_define_args += ['-DCUTTER_ENABLE_JUPYTER']
- if get_option('enable_webengine')
- message('QtWebEngine support enabled')
- feature_define_args += ['-DCUTTER_ENABLE_QTWEBENGINE']
- qt_modules += 'WebEngineWidgets'
- endif
- endif
endif
add_project_arguments(feature_define_args, language: 'cpp')
diff --git a/src/meson_options.txt b/src/meson_options.txt
index 395f4c67..7ca77564 100644
--- a/src/meson_options.txt
+++ b/src/meson_options.txt
@@ -1,4 +1,2 @@
option('enable_python', type: 'boolean', value: true)
option('enable_python_bindings', type: 'boolean', value: true)
-option('enable_jupyter', type: 'boolean', value: false)
-option('enable_webengine', type: 'boolean', value: false)
diff --git a/src/python/cutter_ipykernel.py b/src/python/cutter_ipykernel.py
deleted file mode 100644
index 89f4f572..00000000
--- a/src/python/cutter_ipykernel.py
+++ /dev/null
@@ -1,84 +0,0 @@
-
-import logging
-import threading
-import signal
-import cutter_internal
-import zmq
-from ipykernel.kernelapp import IPKernelApp
-from ipykernel.ipkernel import IPythonKernel
-
-
-class IPyKernelInterfaceKernel:
- def __init__(self, thread, app):
- self._thread = thread
- self._app = app
-
- def send_signal(self, signum):
- if not self._thread.is_alive():
- return
-
- if signum == signal.SIGKILL or signum == signal.SIGTERM:
- self._app.io_loop.stop()
- elif signum == signal.SIGINT and self._app.kernel.interruptable:
- self._app.log.debug("Sending KeyboardInterrupt to ioloop thread.")
- cutter_internal.thread_set_async_exc(self._thread.ident, KeyboardInterrupt())
-
- def poll(self):
- if self._thread.is_alive():
- return None
- else:
- return 0
-
- def cleanup(self):
- self._app.heartbeat.context.destroy()
- self._thread.join()
- self._app.heartbeat.join()
- self._app.iopub_thread.stop()
- try:
- self._app.kernel.shell.history_manager.save_thread.stop()
- except AttributeError:
- pass
- zmq.Context.instance().destroy()
- # successful if only the main thread remains
- return len(threading.enumerate()) == 1
-
-
-class CutterIPythonKernel(IPythonKernel):
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- self.interruptable = False
-
- def pre_handler_hook(self):
- self.interruptable = True
-
- def post_handler_hook(self):
- self.interruptable = False
-
-
-class CutterIPKernelApp(IPKernelApp):
- def init_signal(self):
- # This would call signal.signal(signal.SIGINT, signal.SIG_IGN)
- # Not needed in supinterpreter.
- pass
-
- def log_connection_info(self):
- # Just skip this. It would only pollute Cutter's output.
- pass
-
-
-def launch_ipykernel(argv):
- app = CutterIPKernelApp.instance()
-
- def run_kernel():
- import asyncio
- asyncio.set_event_loop(asyncio.new_event_loop())
- app.kernel_class = CutterIPythonKernel
- # app.log_level = logging.DEBUG
- app.initialize(argv[3:])
- app.start()
-
- thread = threading.Thread(target=run_kernel)
- thread.start()
-
- return IPyKernelInterfaceKernel(thread, app)
-
diff --git a/src/python/cutter_jupyter.py b/src/python/cutter_jupyter.py
deleted file mode 100644
index 4813521c..00000000
--- a/src/python/cutter_jupyter.py
+++ /dev/null
@@ -1,140 +0,0 @@
-
-import asyncio
-import queue
-import sys
-from jupyter_client.ioloop import IOLoopKernelManager
-from notebook.notebookapp import *
-import cutter_internal
-
-
-class IPyKernelInterfaceJupyter:
- def __init__(self, id):
- self._id = id
-
- def send_signal(self, signum):
- cutter_internal.kernel_interface_send_signal(self._id, signum)
-
- def kill(self):
- self.send_signal(signal.SIGKILL)
-
- def terminate(self):
- self.send_signal(signal.SIGTERM)
-
- def poll(self):
- return cutter_internal.kernel_interface_poll(self._id)
-
- def wait(self, timeout=None):
- if timeout is not None:
- start_time = time.process_time()
- else:
- start_time = None
- while timeout is None or time.process_time() - start_time < timeout:
- if self.poll() is not None:
- return
- time.sleep(0.1)
-
-
-class CutterInternalIPyKernelManager(IOLoopKernelManager):
- def start_kernel(self, **kw):
- self.write_connection_file()
-
- self._launch_args = kw.copy()
-
- extra_arguments = kw.pop('extra_arguments', [])
- kernel_cmd = self.format_kernel_cmd(extra_arguments=extra_arguments)
- env = kw.pop('env', os.environ).copy()
- # Don't allow PYTHONEXECUTABLE to be passed to kernel process.
- # If set, it can bork all the things.
- env.pop('PYTHONEXECUTABLE', None)
- if not self.kernel_cmd:
- # If kernel_cmd has been set manually, don't refer to a kernel spec
- # Environment variables from kernel spec are added to os.environ
- env.update(self.kernel_spec.env or {})
-
- # launch the kernel subprocess
- id = cutter_internal.launch_ipykernel(kernel_cmd, env=env, **kw)
- self.kernel = IPyKernelInterfaceJupyter(id)
- # self._launch_kernel(kernel_cmd, env=env, **kw)
-
- self.start_restarter()
- self._connect_control_socket()
-
- def signal_kernel(self, signum):
- self.kernel.send_signal(signum)
-
-
-def kernel_manager_factory(kernel_name, **kwargs):
- if kernel_name in {"python", "python2", "python3"}:
- return CutterInternalIPyKernelManager(kernel_name=kernel_name, **kwargs)
- else:
- return IOLoopKernelManager(kernel_name=kernel_name, **kwargs)
-
-
-class CutterNotebookApp(NotebookApp):
- def __init__(self, **kwargs):
- self.thread = None
- self.io_loop = None
- super().__init__(**kwargs)
-
- def start(self):
- """ see NotebookApp.start() """
- self.kernel_manager.kernel_manager_factory = kernel_manager_factory
-
- super(NotebookApp, self).start()
-
- self.write_server_info_file()
-
- self.io_loop = ioloop.IOLoop.current()
- if sys.platform.startswith('win'):
- # add no-op to wake every 5s
- # to handle signals that may be ignored by the inner loop
- pc = ioloop.PeriodicCallback(lambda: None, 5000)
- pc.start()
- try:
- self.io_loop.start()
- except KeyboardInterrupt:
- self.log.info(_("Interrupted..."))
- finally:
- self.remove_server_info_file()
- self.cleanup_kernels()
-
- def stop(self):
- super().stop()
- if self.thread is not None:
- self.thread.join()
-
- def init_signal(self):
- # This would call signal.signal(signal.SIGINT, signal.SIG_IGN)
- # Not needed in supinterpreter.
- pass
-
- @property
- def url_with_token(self):
- return url_concat(self.connection_url, {'token': self.token})
-
-
-def start_jupyter():
- q = queue.Queue()
-
- def start_jupyter_async():
- # workaround for misbehavior under certain circumstances
- # with argumentparser and jupyter accessing out of bounds
- # program name via argv[0]
- if not sys.argv:
- sys.argv.append("Cutter")
-
- asyncio.set_event_loop(asyncio.new_event_loop())
- app = CutterNotebookApp()
- # app.log_level = logging.DEBUG
- app.thread = threading.current_thread()
- app.initialize()
- q.put(app)
- app.start()
-
- threading.Thread(target=start_jupyter_async).start()
- return q.get()
-
-
-if __name__ == "__main__":
- app = start_jupyter()
- print("Started " + app.url_with_token)
diff --git a/src/resources.qrc b/src/resources.qrc
index 9b563c5a..ce5afe3c 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -77,8 +77,6 @@
img/cutter_plain.svg
img/cutter_white_plain.svg
img/cutter.svg
- python/cutter_jupyter.py
- python/cutter_ipykernel.py
img/icons/copy.svg
img/icons/delete.svg
img/icons/previous.svg
diff --git a/src/widgets/JupyterWidget.cpp b/src/widgets/JupyterWidget.cpp
deleted file mode 100644
index f02aba68..00000000
--- a/src/widgets/JupyterWidget.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include "common/JupyterConnection.h"
-#include "JupyterWidget.h"
-#include "ui_JupyterWidget.h"
-
-#include
-#include
-#include
-#include
-
-#ifdef CUTTER_ENABLE_QTWEBENGINE
-#include
-#endif
-
-JupyterWidget::JupyterWidget(MainWindow *main, QAction *action) :
- CutterDockWidget(main, action),
- ui(new Ui::JupyterWidget)
-{
- ui->setupUi(this);
-
- ui->tabWidget->setTabsClosable(true);
- ui->tabWidget->setMovable(true);
-
- QWidget *cornerWidget = new QWidget(ui->tabWidget);
- QHBoxLayout *cornerWidgetLayout = new QHBoxLayout(cornerWidget);
- cornerWidget->setLayout(cornerWidgetLayout);
- cornerWidgetLayout->setContentsMargins(4, 4, 4, 4);
- homeButton = new QPushButton(cornerWidget);
- homeButton->setStyleSheet("QPushButton { padding: 2px; background-color: palette(light); border-radius: 4px; }"
- "QPushButton:pressed { background-color: palette(dark); }");
- homeButton->setIcon(QIcon(":/img/icons/home.svg"));
- homeButton->setEnabled(false);
- cornerWidgetLayout->addWidget(homeButton);
- ui->tabWidget->setCornerWidget(cornerWidget);
-
- connect(homeButton, &QAbstractButton::clicked, this, &JupyterWidget::openHomeTab);
- connect(ui->tabWidget, &QTabWidget::tabCloseRequested, this, &JupyterWidget::tabCloseRequested);
-
- connect(Jupyter(), &JupyterConnection::urlReceived, this, &JupyterWidget::urlReceived);
- connect(Jupyter(), &JupyterConnection::creationFailed, this, &JupyterWidget::creationFailed);
- Jupyter()->start();
-}
-
-JupyterWidget::~JupyterWidget()
-{
-}
-
-#ifdef CUTTER_ENABLE_QTWEBENGINE
-JupyterWebView *JupyterWidget::createNewTab()
-{
- auto webView = new JupyterWebView(this);
- int index = ui->tabWidget->addTab(webView, "Tab");
- webView->setTabWidget(ui->tabWidget);
- ui->tabWidget->setCurrentIndex(index);
- return webView;
-}
-#endif
-
-void JupyterWidget::urlReceived(const QString &url)
-{
-#ifdef CUTTER_ENABLE_QTWEBENGINE
- Q_UNUSED(url);
- openHomeTab();
- homeButton->setEnabled(true);
-#else
- clearTabs();
- QWidget *failPage = new QWidget(this);
- QLabel *label = new QLabel(failPage);
- label->setText(
- tr("Cutter has been built without QtWebEngine.
Open the following URL in your Browser to use Jupyter:
%1").arg(
- url));
- label->setTextFormat(Qt::RichText);
- label->setTextInteractionFlags(Qt::TextBrowserInteraction);
- label->setOpenExternalLinks(true);
- QHBoxLayout *layout = new QHBoxLayout();
- layout->addWidget(label);
- layout->setAlignment(label, Qt::AlignCenter);
- failPage->setLayout(layout);
- ui->tabWidget->addTab(failPage, tr("Jupyter"));
- homeButton->setEnabled(false);
- ui->tabWidget->setTabsClosable(false);
-#endif
-}
-
-void JupyterWidget::creationFailed()
-{
- clearTabs();
- QWidget *failPage = new QWidget(this);
- QLabel *label = new QLabel(failPage);
- label->setText(
- tr("An error occurred while opening jupyter. Make sure Jupyter is installed system-wide."));
- QHBoxLayout *layout = new QHBoxLayout();
- layout->addWidget(label);
- layout->setAlignment(label, Qt::AlignCenter);
- failPage->setLayout(layout);
- ui->tabWidget->addTab(failPage, tr("Error"));
- homeButton->setEnabled(false);
- ui->tabWidget->setTabsClosable(false);
-}
-
-void JupyterWidget::openHomeTab()
-{
-#ifdef CUTTER_ENABLE_QTWEBENGINE
- QString url = Jupyter()->getUrl();
- if (!url.isNull()) {
- createNewTab()->load(QUrl(url));
- }
-#endif
-}
-
-void JupyterWidget::tabCloseRequested(int index)
-{
- removeTab(index);
- if (ui->tabWidget->count() == 0) {
- openHomeTab();
- }
-}
-
-void JupyterWidget::removeTab(int index)
-{
- QWidget *widget = ui->tabWidget->widget(index);
- ui->tabWidget->removeTab(index);
- delete widget;
-}
-
-void JupyterWidget::clearTabs()
-{
- while (ui->tabWidget->count() > 0) {
- removeTab(0);
- }
-}
-
-#ifdef CUTTER_ENABLE_QTWEBENGINE
-JupyterWebView::JupyterWebView(JupyterWidget *mainWidget, QWidget *parent) : QWebEngineView(parent)
-{
- this->mainWidget = mainWidget;
- this->tabWidget = nullptr;
-
- connect(this, &QWebEngineView::titleChanged, this, &JupyterWebView::onTitleChanged);
-}
-
-void JupyterWebView::setTabWidget(QTabWidget *tabWidget)
-{
- this->tabWidget = tabWidget;
- updateTitle();
-}
-
-QWebEngineView *JupyterWebView::createWindow(QWebEnginePage::WebWindowType type)
-{
- switch (type) {
- case QWebEnginePage::WebBrowserTab:
- return mainWidget->createNewTab();
- default:
- return nullptr;
- }
-}
-
-void JupyterWebView::onTitleChanged(const QString &)
-{
- updateTitle();
-}
-
-void JupyterWebView::updateTitle()
-{
- if (!tabWidget) {
- return;
- }
-
- QString title = this->title();
- if (title.isEmpty()) {
- title = tr("Jupyter");
- }
- tabWidget->setTabText(tabWidget->indexOf(this), title);
-}
-
-#endif
-
-#endif
diff --git a/src/widgets/JupyterWidget.h b/src/widgets/JupyterWidget.h
deleted file mode 100644
index c7c9264e..00000000
--- a/src/widgets/JupyterWidget.h
+++ /dev/null
@@ -1,78 +0,0 @@
-
-#ifndef JUPYTERWIDGET_H
-#define JUPYTERWIDGET_H
-
-#ifdef CUTTER_ENABLE_JUPYTER
-
-#include "CutterDockWidget.h"
-
-#include
-#include
-
-namespace Ui {
-class JupyterWidget;
-}
-
-class JupyterWebView;
-class QTabWidget;
-class MainWindow;
-
-class JupyterWidget : public CutterDockWidget
-{
- Q_OBJECT
-
-public:
- JupyterWidget(MainWindow *main, QAction *action = nullptr);
- ~JupyterWidget();
-
-#ifdef CUTTER_ENABLE_QTWEBENGINE
- JupyterWebView *createNewTab();
-#endif
-
-private slots:
- void urlReceived(const QString &url);
- void creationFailed();
-
- void openHomeTab();
- void tabCloseRequested(int index);
-
-private:
- std::unique_ptr ui;
-
- QAbstractButton *homeButton;
-
- void removeTab(int index);
- void clearTabs();
-};
-
-#ifdef CUTTER_ENABLE_QTWEBENGINE
-
-#include
-
-class JupyterWebView : public QWebEngineView
-{
- Q_OBJECT
-
-public:
- JupyterWebView(JupyterWidget *mainWidget, QWidget *parent = nullptr);
-
- void setTabWidget(QTabWidget *tabWidget);
-
-protected:
- QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override;
-
-private slots:
- void onTitleChanged(const QString &title);
-
-private:
- JupyterWidget *mainWidget;
- QTabWidget *tabWidget;
-
- void updateTitle();
-};
-
-#endif
-
-#endif
-
-#endif //JUPYTERWIDGET_H
diff --git a/src/widgets/JupyterWidget.ui b/src/widgets/JupyterWidget.ui
deleted file mode 100644
index 3f01039c..00000000
--- a/src/widgets/JupyterWidget.ui
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
- JupyterWidget
-
-
-
- 0
- 0
- 644
- 484
-
-
-
- Jupyter
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
-
-
-
-
-
-