mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 19:06:10 +00:00
Remove Jupyter Integration (#1398)
* Remove Jupyter Integration Replaced by https://github.com/radareorg/cutter-jupyter * Remove duplicate vars in .appveyor.yml
This commit is contained in:
parent
eac91ed9c8
commit
1710829267
@ -49,8 +49,8 @@ before_build:
|
|||||||
|
|
||||||
# Build config
|
# Build config
|
||||||
build_script:
|
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 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 --jupyter )
|
- cmd: if defined MESON ( python meson.py --release --dist=%ARTIFACT_PATH% --backend=%BACKEND% --python )
|
||||||
|
|
||||||
after_build:
|
after_build:
|
||||||
- cmd: if defined QMAKE ( set "PATH=%CD%\r2_dist_%ARCH%;%PATH%" && powershell scripts\bundle_r2dec.ps1 "%CD%\%ARTIFACT_PATH%" )
|
- cmd: if defined QMAKE ( set "PATH=%CD%\r2_dist_%ARCH%;%PATH%" && powershell scripts\bundle_r2dec.ps1 "%CD%\%ARTIFACT_PATH%" )
|
||||||
|
@ -68,7 +68,6 @@ addons:
|
|||||||
install:
|
install:
|
||||||
- scripts/fetch_deps.sh
|
- scripts/fetch_deps.sh
|
||||||
- source cutter-deps/env.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" == "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
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export LD_LIBRARY_PATH="`llvm-config --libdir`:$LD_LIBRARY_PATH"; fi
|
||||||
|
|
||||||
@ -90,8 +89,6 @@ script:
|
|||||||
qmake
|
qmake
|
||||||
CUTTER_ENABLE_PYTHON=true
|
CUTTER_ENABLE_PYTHON=true
|
||||||
CUTTER_ENABLE_PYTHON_BINDINGS=true
|
CUTTER_ENABLE_PYTHON_BINDINGS=true
|
||||||
CUTTER_ENABLE_JUPYTER=true
|
|
||||||
CUTTER_ENABLE_QTWEBENGINE=false
|
|
||||||
PREFIX=/usr
|
PREFIX=/usr
|
||||||
APPIMAGE=1
|
APPIMAGE=1
|
||||||
../src &&
|
../src &&
|
||||||
@ -104,8 +101,6 @@ script:
|
|||||||
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
||||||
-DCUTTER_ENABLE_PYTHON=ON
|
-DCUTTER_ENABLE_PYTHON=ON
|
||||||
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
||||||
-DCUTTER_ENABLE_JUPYTER=ON
|
|
||||||
-DCUTTER_ENABLE_QTWEBENGINE=OFF
|
|
||||||
../src &&
|
../src &&
|
||||||
make -j4;
|
make -j4;
|
||||||
fi
|
fi
|
||||||
@ -114,8 +109,6 @@ script:
|
|||||||
qmake
|
qmake
|
||||||
CUTTER_ENABLE_PYTHON=true
|
CUTTER_ENABLE_PYTHON=true
|
||||||
CUTTER_ENABLE_PYTHON_BINDINGS=true
|
CUTTER_ENABLE_PYTHON_BINDINGS=true
|
||||||
CUTTER_ENABLE_JUPYTER=true
|
|
||||||
CUTTER_ENABLE_QTWEBENGINE=false
|
|
||||||
CUTTER_BUNDLE_R2_APPBUNDLE=true
|
CUTTER_BUNDLE_R2_APPBUNDLE=true
|
||||||
PYTHON_FRAMEWORK_DIR=$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR
|
PYTHON_FRAMEWORK_DIR=$CUTTER_DEPS_PYTHON_FRAMEWORK_DIR
|
||||||
../src &&
|
../src &&
|
||||||
@ -128,8 +121,6 @@ script:
|
|||||||
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
-DPYTHON_EXECUTABLE="$CUTTER_DEPS_PYTHON_PREFIX/bin/python3"
|
||||||
-DCUTTER_ENABLE_PYTHON=ON
|
-DCUTTER_ENABLE_PYTHON=ON
|
||||||
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
-DCUTTER_ENABLE_PYTHON_BINDINGS=ON
|
||||||
-DCUTTER_ENABLE_JUPYTER=ON
|
|
||||||
-DCUTTER_ENABLE_QTWEBENGINE=OFF
|
|
||||||
../src &&
|
../src &&
|
||||||
make -j4;
|
make -j4;
|
||||||
fi
|
fi
|
||||||
|
1
build.sh
1
build.sh
@ -9,7 +9,6 @@ ERR=0
|
|||||||
BUILD="build"
|
BUILD="build"
|
||||||
QMAKE_CONF=""
|
QMAKE_CONF=""
|
||||||
ROOT_DIR=`pwd`
|
ROOT_DIR=`pwd`
|
||||||
#QMAKE_CONF="CUTTER_ENABLE_JUPYTER=false CUTTER_ENABLE_QTWEBENGINE=false"
|
|
||||||
|
|
||||||
# Create translations
|
# Create translations
|
||||||
lrelease ./src/Cutter.pro
|
lrelease ./src/Cutter.pro
|
||||||
|
@ -24,8 +24,8 @@ Building options
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
Note that there are two major building options available:
|
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_PYTHON`` compile with Python support
|
||||||
- ``CUTTER_ENABLE_QTWEBENGINE`` is used to compile Cutter with bundled QtWebEngine (to ease jupyter console usage)
|
- ``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
|
rm CMakeCache.txt # the cache may be polluted with unwanted libraries found before
|
||||||
cmake -DCMAKE_PREFIX_PATH=/opt/Qt/5.9.1/gcc_64 ..
|
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)
|
Building with Meson (Windows)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
8
meson.py
8
meson.py
@ -48,9 +48,7 @@ def build(args):
|
|||||||
cutter_builddir = os.path.join(ROOT, args.dir)
|
cutter_builddir = os.path.join(ROOT, args.dir)
|
||||||
if not os.path.exists(cutter_builddir):
|
if not os.path.exists(cutter_builddir):
|
||||||
defines = ['-Denable_python=%s' % str(args.python).lower(),
|
defines = ['-Denable_python=%s' % str(args.python).lower(),
|
||||||
'-Denable_python_bindings=%s' % str(args.python_bindings).lower(),
|
'-Denable_python_bindings=%s' % str(args.python_bindings).lower()]
|
||||||
'-Denable_jupyter=%s' % str(args.jupyter).lower(),
|
|
||||||
'-Denable_webengine=%s' % str(args.webengine).lower()]
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
defines.append('-Dradare2:r2_incdir=radare2/include')
|
defines.append('-Dradare2:r2_incdir=radare2/include')
|
||||||
defines.append('-Dradare2:r2_libdir=radare2/lib')
|
defines.append('-Dradare2:r2_libdir=radare2/lib')
|
||||||
@ -79,10 +77,6 @@ def main():
|
|||||||
help='Enable Python support')
|
help='Enable Python support')
|
||||||
parser.add_argument('--python-bindings', action='store_true',
|
parser.add_argument('--python-bindings', action='store_true',
|
||||||
help='Enable Python Bindings')
|
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',
|
parser.add_argument('--release', action='store_true',
|
||||||
help='Set the build as Release (remove debug info)')
|
help='Set the build as Release (remove debug info)')
|
||||||
parser.add_argument('--nobuild', action='store_true',
|
parser.add_argument('--nobuild', action='store_true',
|
||||||
|
@ -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\*.pyd -Destination $dist\$py_base
|
||||||
Copy-Item .\python_embed\sqlite3.dll -Destination $dist\$py_base
|
Copy-Item .\python_embed\sqlite3.dll -Destination $dist\$py_base
|
||||||
Copy-Item .\python_embed\python*.dll -Destination $dist
|
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")
|
[System.IO.File]::WriteAllLines("${dist}\${py_base}._pth", "${py_base}`r`n${py_base}\${py_base}.zip`r`n${py_base}\site-packages")
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
jupyter
|
|
||||||
ipykernel==4.10.0
|
|
||||||
pyzmq==17.1.2
|
|
@ -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 "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_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)
|
if(NOT CUTTER_ENABLE_PYTHON)
|
||||||
set(CUTTER_ENABLE_PYTHON_BINDINGS OFF)
|
set(CUTTER_ENABLE_PYTHON_BINDINGS OFF)
|
||||||
@ -45,14 +43,6 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Svg Network)
|
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)
|
if(WIN32)
|
||||||
@ -80,10 +70,6 @@ if(CUTTER_ENABLE_PYTHON)
|
|||||||
include_directories(${PYTHON_INCLUDE_DIRS})
|
include_directories(${PYTHON_INCLUDE_DIRS})
|
||||||
add_definitions(-DCUTTER_ENABLE_PYTHON)
|
add_definitions(-DCUTTER_ENABLE_PYTHON)
|
||||||
|
|
||||||
if(CUTTER_ENABLE_JUPYTER)
|
|
||||||
add_definitions(-DCUTTER_ENABLE_JUPYTER)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CUTTER_ENABLE_PYTHON_BINDINGS)
|
if(CUTTER_ENABLE_PYTHON_BINDINGS)
|
||||||
find_package(PythonInterp REQUIRED)
|
find_package(PythonInterp REQUIRED)
|
||||||
find_package(Shiboken2 "${Qt5_VERSION}" REQUIRED)
|
find_package(Shiboken2 "${Qt5_VERSION}" REQUIRED)
|
||||||
@ -107,8 +93,6 @@ message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}")
|
|||||||
message(STATUS "Options:")
|
message(STATUS "Options:")
|
||||||
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
|
message(STATUS "- Python: ${CUTTER_ENABLE_PYTHON}")
|
||||||
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
|
message(STATUS "- Python Bindings: ${CUTTER_ENABLE_PYTHON_BINDINGS}")
|
||||||
message(STATUS "- Jupyter: ${CUTTER_ENABLE_JUPYTER}")
|
|
||||||
message(STATUS "- QtWebEngine: ${CUTTER_ENABLE_QTWEBENGINE}")
|
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
|
|
||||||
|
|
||||||
@ -172,7 +156,4 @@ if(CUTTER_ENABLE_PYTHON)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CUTTER_ENABLE_PYTHON AND CUTTER_ENABLE_JUPYTER AND CUTTER_ENABLE_QTWEBENGINE)
|
|
||||||
target_link_libraries(Cutter Qt5::WebEngineWidgets)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
@ -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
|
!defined(CUTTER_BUNDLE_R2_APPBUNDLE, var) CUTTER_BUNDLE_R2_APPBUNDLE=false
|
||||||
equals(CUTTER_BUNDLE_R2_APPBUNDLE, true) CONFIG += CUTTER_BUNDLE_R2_APPBUNDLE
|
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)")
|
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
|
INCLUDEPATH *= . core widgets dialogs common plugins
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
@ -290,10 +265,7 @@ SOURCES += \
|
|||||||
widgets/HeadersWidget.cpp \
|
widgets/HeadersWidget.cpp \
|
||||||
widgets/SearchWidget.cpp \
|
widgets/SearchWidget.cpp \
|
||||||
CutterApplication.cpp \
|
CutterApplication.cpp \
|
||||||
common/JupyterConnection.cpp \
|
|
||||||
widgets/JupyterWidget.cpp \
|
|
||||||
common/PythonAPI.cpp \
|
common/PythonAPI.cpp \
|
||||||
common/NestedIPyKernel.cpp \
|
|
||||||
dialogs/R2PluginsDialog.cpp \
|
dialogs/R2PluginsDialog.cpp \
|
||||||
widgets/CutterDockWidget.cpp \
|
widgets/CutterDockWidget.cpp \
|
||||||
widgets/CutterTreeWidget.cpp \
|
widgets/CutterTreeWidget.cpp \
|
||||||
@ -404,10 +376,7 @@ HEADERS += \
|
|||||||
widgets/TypesWidget.h \
|
widgets/TypesWidget.h \
|
||||||
widgets/HeadersWidget.h \
|
widgets/HeadersWidget.h \
|
||||||
widgets/SearchWidget.h \
|
widgets/SearchWidget.h \
|
||||||
common/JupyterConnection.h \
|
|
||||||
widgets/JupyterWidget.h \
|
|
||||||
common/PythonAPI.h \
|
common/PythonAPI.h \
|
||||||
common/NestedIPyKernel.h \
|
|
||||||
dialogs/R2PluginsDialog.h \
|
dialogs/R2PluginsDialog.h \
|
||||||
widgets/CutterDockWidget.h \
|
widgets/CutterDockWidget.h \
|
||||||
widgets/CutterTreeWidget.h \
|
widgets/CutterTreeWidget.h \
|
||||||
@ -496,7 +465,6 @@ FORMS += \
|
|||||||
widgets/TypesWidget.ui \
|
widgets/TypesWidget.ui \
|
||||||
widgets/HeadersWidget.ui \
|
widgets/HeadersWidget.ui \
|
||||||
widgets/SearchWidget.ui \
|
widgets/SearchWidget.ui \
|
||||||
widgets/JupyterWidget.ui \
|
|
||||||
dialogs/R2PluginsDialog.ui \
|
dialogs/R2PluginsDialog.ui \
|
||||||
dialogs/VersionInfoDialog.ui \
|
dialogs/VersionInfoDialog.ui \
|
||||||
widgets/ZignaturesWidget.ui \
|
widgets/ZignaturesWidget.ui \
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#include "common/PythonManager.h"
|
#include "common/PythonManager.h"
|
||||||
#include "CutterApplication.h"
|
#include "CutterApplication.h"
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
#include "common/JupyterConnection.h"
|
|
||||||
#endif
|
|
||||||
#include "plugins/PluginManager.h"
|
#include "plugins/PluginManager.h"
|
||||||
#include "CutterConfig.h"
|
#include "CutterConfig.h"
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ int main(int argc, char *argv[])
|
|||||||
settings.setValue("settings_migrated", true);
|
settings.setValue("settings_migrated", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins
|
||||||
|
|
||||||
CutterApplication a(argc, argv);
|
CutterApplication a(argc, argv);
|
||||||
|
|
||||||
if (Config()->getAutoUpdateEnabled()) {
|
if (Config()->getAutoUpdateEnabled()) {
|
||||||
|
@ -1,148 +0,0 @@
|
|||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include <Python.h>
|
|
||||||
|
|
||||||
#include "JupyterConnection.h"
|
|
||||||
#include "NestedIPyKernel.h"
|
|
||||||
#include "PythonManager.h"
|
|
||||||
#include "QtResImporter.h"
|
|
||||||
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
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
|
|
@ -1,54 +0,0 @@
|
|||||||
#ifndef JUPYTERCONNECTION_H
|
|
||||||
#define JUPYTERCONNECTION_H
|
|
||||||
|
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QMap>
|
|
||||||
|
|
||||||
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<long, NestedIPyKernel *> 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
|
|
@ -1,87 +0,0 @@
|
|||||||
|
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include <Python.h>
|
|
||||||
#include <csignal>
|
|
||||||
|
|
||||||
#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
|
|
@ -1,38 +0,0 @@
|
|||||||
|
|
||||||
#ifndef NESTEDIPYKERNEL_H
|
|
||||||
#define NESTEDIPYKERNEL_H
|
|
||||||
|
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
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
|
|
@ -84,118 +84,4 @@ PyObject *PyInit_api()
|
|||||||
return PyModule_Create(&CutterModule);
|
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<long>(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<PyCFunction>((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
|
#endif // CUTTER_ENABLE_PYTHON
|
||||||
|
@ -74,9 +74,6 @@ void PythonManager::initialize()
|
|||||||
initPythonHome();
|
initPythonHome();
|
||||||
|
|
||||||
PyImport_AppendInittab("_cutter", &PyInit_api);
|
PyImport_AppendInittab("_cutter", &PyInit_api);
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
PyImport_AppendInittab("cutter_internal", &PyInit_api_internal);
|
|
||||||
#endif
|
|
||||||
PyImport_AppendInittab("_qtres", &PyInit_qtres);
|
PyImport_AppendInittab("_qtres", &PyInit_qtres);
|
||||||
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
||||||
PyImport_AppendInittab("CutterBindings", &PyInit_CutterBindings);
|
PyImport_AppendInittab("CutterBindings", &PyInit_CutterBindings);
|
||||||
|
@ -52,7 +52,6 @@
|
|||||||
#include "widgets/ClassesWidget.h"
|
#include "widgets/ClassesWidget.h"
|
||||||
#include "widgets/ResourcesWidget.h"
|
#include "widgets/ResourcesWidget.h"
|
||||||
#include "widgets/VTablesWidget.h"
|
#include "widgets/VTablesWidget.h"
|
||||||
#include "widgets/JupyterWidget.h"
|
|
||||||
#include "widgets/HeadersWidget.h"
|
#include "widgets/HeadersWidget.h"
|
||||||
#include "widgets/ZignaturesWidget.h"
|
#include "widgets/ZignaturesWidget.h"
|
||||||
#include "widgets/DebugActions.h"
|
#include "widgets/DebugActions.h"
|
||||||
@ -268,12 +267,6 @@ void MainWindow::initDocks()
|
|||||||
memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
|
memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
|
||||||
breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
|
breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
|
||||||
registerRefsDock = new RegisterRefsWidget(this, ui->actionRegisterRefs);
|
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);
|
dashboardDock = new Dashboard(this, ui->actionDashboard);
|
||||||
sdbDock = new SdbWidget(this, ui->actionSDBBrowser);
|
sdbDock = new SdbWidget(this, ui->actionSDBBrowser);
|
||||||
classesDock = new ClassesWidget(this, ui->actionClasses);
|
classesDock = new ClassesWidget(this, ui->actionClasses);
|
||||||
@ -790,9 +783,6 @@ void MainWindow::restoreDocks()
|
|||||||
tabifyDockWidget(dashboardDock, memoryMapDock);
|
tabifyDockWidget(dashboardDock, memoryMapDock);
|
||||||
tabifyDockWidget(dashboardDock, breakpointDock);
|
tabifyDockWidget(dashboardDock, breakpointDock);
|
||||||
tabifyDockWidget(dashboardDock, registerRefsDock);
|
tabifyDockWidget(dashboardDock, registerRefsDock);
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
tabifyDockWidget(dashboardDock, jupyterDock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
updateDockActionsChecked();
|
updateDockActionsChecked();
|
||||||
}
|
}
|
||||||
@ -827,9 +817,6 @@ void MainWindow::showZenDocks()
|
|||||||
hexdumpDock,
|
hexdumpDock,
|
||||||
searchDock,
|
searchDock,
|
||||||
importsDock,
|
importsDock,
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
jupyterDock
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
for (auto w : dockWidgets) {
|
for (auto w : dockWidgets) {
|
||||||
if (zenDocks.contains(w)) {
|
if (zenDocks.contains(w)) {
|
||||||
|
@ -43,9 +43,6 @@ class TypesWidget;
|
|||||||
class HeadersWidget;
|
class HeadersWidget;
|
||||||
class ZignaturesWidget;
|
class ZignaturesWidget;
|
||||||
class SearchWidget;
|
class SearchWidget;
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
class JupyterWidget;
|
|
||||||
#endif
|
|
||||||
class QDockWidget;
|
class QDockWidget;
|
||||||
class DisassemblyWidget;
|
class DisassemblyWidget;
|
||||||
class GraphWidget;
|
class GraphWidget;
|
||||||
@ -251,9 +248,6 @@ private:
|
|||||||
NewFileDialog *newFileDialog = nullptr;
|
NewFileDialog *newFileDialog = nullptr;
|
||||||
QDockWidget *breakpointDock = nullptr;
|
QDockWidget *breakpointDock = nullptr;
|
||||||
QDockWidget *registerRefsDock = nullptr;
|
QDockWidget *registerRefsDock = nullptr;
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
JupyterWidget *jupyterDock = nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void initToolBar();
|
void initToolBar();
|
||||||
void initDocks();
|
void initDocks();
|
||||||
|
@ -213,7 +213,6 @@ QToolTip {
|
|||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionComments"/>
|
<addaction name="actionComments"/>
|
||||||
<addaction name="actionConsole"/>
|
<addaction name="actionConsole"/>
|
||||||
<addaction name="actionJupyter"/>
|
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menuAddExtraWidget"/>
|
<addaction name="menuAddExtraWidget"/>
|
||||||
<addaction name="menuPlugins"/>
|
<addaction name="menuPlugins"/>
|
||||||
@ -1107,14 +1106,6 @@ QToolTip {
|
|||||||
<string>Show/Hide Zignatures panel</string>
|
<string>Show/Hide Zignatures panel</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionJupyter">
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Jupyter</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionExport_as_code">
|
<action name="actionExport_as_code">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Export as code</string>
|
<string>Export as code</string>
|
||||||
|
@ -30,15 +30,15 @@ AboutDialog::AboutDialog(QWidget *parent) :
|
|||||||
+ tr("Version") + " " CUTTER_VERSION_FULL "<br/>"
|
+ tr("Version") + " " CUTTER_VERSION_FULL "<br/>"
|
||||||
+ tr("Using r2-") + R2_GITTAP
|
+ tr("Using r2-") + R2_GITTAP
|
||||||
+ "<p><b>" + tr("Optional Features:") + "</b><br/>"
|
+ "<p><b>" + tr("Optional Features:") + "</b><br/>"
|
||||||
+ QString("Jupyter: %1<br/>").arg(
|
+ QString("Python: %1<br/>").arg(
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
#ifdef CUTTER_ENABLE_PYTHON
|
||||||
"ON"
|
"ON"
|
||||||
#else
|
#else
|
||||||
"OFF"
|
"OFF"
|
||||||
#endif
|
#endif
|
||||||
)
|
)
|
||||||
+ QString("QtWebEngine: %2</p>").arg(
|
+ QString("Python Bindings: %2</p>").arg(
|
||||||
#ifdef CUTTER_ENABLE_QTWEBENGINE
|
#ifdef CUTTER_ENABLE_PYTHON_BINDINGS
|
||||||
"ON"
|
"ON"
|
||||||
#else
|
#else
|
||||||
"OFF"
|
"OFF"
|
||||||
|
@ -13,16 +13,6 @@ if get_option('enable_python')
|
|||||||
message('Python Bindings are enabled')
|
message('Python Bindings are enabled')
|
||||||
feature_define_args += ['-DCUTTER_ENABLE_PYTHON_BINDINGS']
|
feature_define_args += ['-DCUTTER_ENABLE_PYTHON_BINDINGS']
|
||||||
endif
|
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
|
endif
|
||||||
|
|
||||||
add_project_arguments(feature_define_args, language: 'cpp')
|
add_project_arguments(feature_define_args, language: 'cpp')
|
||||||
|
@ -1,4 +1,2 @@
|
|||||||
option('enable_python', type: 'boolean', value: true)
|
option('enable_python', type: 'boolean', value: true)
|
||||||
option('enable_python_bindings', 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)
|
|
||||||
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
@ -77,8 +77,6 @@
|
|||||||
<file>img/cutter_plain.svg</file>
|
<file>img/cutter_plain.svg</file>
|
||||||
<file>img/cutter_white_plain.svg</file>
|
<file>img/cutter_white_plain.svg</file>
|
||||||
<file>img/cutter.svg</file>
|
<file>img/cutter.svg</file>
|
||||||
<file>python/cutter_jupyter.py</file>
|
|
||||||
<file>python/cutter_ipykernel.py</file>
|
|
||||||
<file>img/icons/copy.svg</file>
|
<file>img/icons/copy.svg</file>
|
||||||
<file>img/icons/delete.svg</file>
|
<file>img/icons/delete.svg</file>
|
||||||
<file>img/icons/previous.svg</file>
|
<file>img/icons/previous.svg</file>
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include "common/JupyterConnection.h"
|
|
||||||
#include "JupyterWidget.h"
|
|
||||||
#include "ui_JupyterWidget.h"
|
|
||||||
|
|
||||||
#include <QTabWidget>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
#ifdef CUTTER_ENABLE_QTWEBENGINE
|
|
||||||
#include <QWebEngineSettings>
|
|
||||||
#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.<br />Open the following URL in your Browser to use Jupyter:<br /><a href=\"%1\">%1</a>").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
|
|
@ -1,78 +0,0 @@
|
|||||||
|
|
||||||
#ifndef JUPYTERWIDGET_H
|
|
||||||
#define JUPYTERWIDGET_H
|
|
||||||
|
|
||||||
#ifdef CUTTER_ENABLE_JUPYTER
|
|
||||||
|
|
||||||
#include "CutterDockWidget.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <QAbstractButton>
|
|
||||||
|
|
||||||
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::JupyterWidget> ui;
|
|
||||||
|
|
||||||
QAbstractButton *homeButton;
|
|
||||||
|
|
||||||
void removeTab(int index);
|
|
||||||
void clearTabs();
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef CUTTER_ENABLE_QTWEBENGINE
|
|
||||||
|
|
||||||
#include <QWebEngineView>
|
|
||||||
|
|
||||||
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
|
|
@ -1,38 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>JupyterWidget</class>
|
|
||||||
<widget class="QDockWidget" name="JupyterWidget">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>644</width>
|
|
||||||
<height>484</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Jupyter</string>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="dockWidgetContents">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QTabWidget" name="tabWidget"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
Loading…
Reference in New Issue
Block a user