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 - - - - - - - - - -