Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Oct 16, 2023
1 parent 9385a64 commit d7960f6
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 93 deletions.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,55 @@

[![CI](https://github.com/DerThorsten/xeus-qt-python/actions/workflows/main.yml/badge.svg)](https://github.com/DerThorsten/xeus-qt-python/actions/workflows/main.yml)

You will need to compile [xeus-qt](https://github.com/jupyter-xeus/xeus-qt) and [xeus-qt-python](https://github.com/jupyter-xeus/xeus-qt-python) (this repository) from source.
First clone these two repositories under `path/to/xeus-qt` and `path/to/xeus-qt-python`, respectively (or whatever place you'd like).
Then enter in a terminal:

```console
micromamba create -n xeus-qt-python
micromamba activate xeus-qt-python
micromamba install -c conda-forge \
"python<3.12" \
pip \
compilers \
cmake \
qt \
xeus \
xtl \
nlohmann_json \
xeus-zmq \
cppzmq \
xeus-python \
pybind11 \
pybind11_json \
pyqt \
qtpy \
pyqtwebengine pyside2 \
"fps-lab>=0.3.0,<1" \
"fps-auth>=0.3.0,<1" \
"fps-jupyterlab>=0.3.0,<1" \
"fps-contents>=0.3.0,<1" \
"fps-kernels>=0.3.0,<1" \
"fps-frontend>=0.3.0,<1"
cd path/to/xeus-qt
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX
make install
cd path/to/xeus-qt-python
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=$CONDA_PREFIX -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX
make
make install
cd ..
python kernel_widget.py
```

This launches JupyterLab in a Qt application. If you look at running kernels in the left tab, you will see a kernel named `qt-python`.
Right-click on it and choose "New Console for Kernel". Now go to the file browser and double-click on the `my_notebook.ipynb` notebook.
In the "Select Kernel" menu, under "Use Kernel from Other Session" at the bottom, select "Console 1".
Now execute all the cells. The last one should display a button with the name "black magic" at the bottom of the Qt application.
If you click on it, it should display "hello from here" inside the last cell.
That's it, you've just connected your custom Qt application to Jupyter!
You added a button to your Qt application from a Jupyter notebook, which when clicked on, prints a message to a notebook cell output.
18 changes: 8 additions & 10 deletions env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ dependencies:
- xeus >= 3.0.3
- xeus-zmq >= 1.0.2
- python=3.10
- fps-jupyterlab
- fps-auth
- python >=3.7
- fastapi >=0.87.0,<1
- fps >=0.0.21,<1
- fps-uvicorn >=0.0.19,<1
- fps-auth-base <1
- fps-contents >=0.0.37,<1
- fps-nbconvert >=0.0.37,<1
- fps-yjs >=0.0.37,<1
- jupyverse-api >=0.3.0,<1
- fps-lab >=0.3.0,<1
- fps-noauth >=0.3.0,<1
- fps-jupyterlab >=0.3.0,<1
- fps-contents >=0.3.0,<1
- fps-kernels >=0.3.0,<1
- fps-frontend >=0.3.0,<1
- pyside2
- pyqt
- PyQtWebEngine
- qtpy
Expand Down
63 changes: 22 additions & 41 deletions kernel_widget.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,36 @@
""" README:
- this is a proxy application for an actual qt application which wants to intergrate
jupyter
- this is a proxy application for an actual qt application which wants to intergrate
jupyter
- code to execute:
- code to execute:
from kernel_widget import get_kernel_widget
from PyQt5.QtWidgets import QPushButton
button = QPushButton()
button.setText("black magic")
from kernel_widget import get_kernel_widget
from PyQt5.QtWidgets import QPushButton
button = QPushButton()
button.setText("black magic")
def say_hello():
print("hello from here")
def say_hello():
print("hello from here")
button.clicked.connect(say_hello)
button.clicked.connect(say_hello)
get_kernel_widget().layout.addWidget(button)
get_kernel_widget().layout.addWidget(button)
"""

import json
import os
from pathlib import Path
import sys
import tempfile
import time
import subprocess
import xqtpython
import socket
from contextlib import closing
from types import ModuleType

import PyQt5
from PyQt5.QtCore import QUrl, QTimer
from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QVBoxLayout,
QPushButton,
QWidget,
)
from PyQt5.QtWebEngineWidgets import QWebEngineView
Expand All @@ -53,14 +48,13 @@ class KernelWidget(QWidget):
A webview widget showing a jupyterlab instance
"""

def __init__(self, kernel_name, jupyverse_dir, *args, **kwargs):
def __init__(self, kernel_name, *args, **kwargs):
super(KernelWidget, self).__init__(*args, **kwargs)

self.layout = QVBoxLayout()
self.setLayout(self.layout)

self.kernel_name = kernel_name
self.jupyverse_dir = jupyverse_dir

# browser
self.browser = QWebEngineView()
Expand Down Expand Up @@ -112,25 +106,24 @@ def _start_kernel(self):
def _start_jupyverse(self):
# self.start_server_button.setDisabled(True)
self.server_port = find_free_port()
token = "my_token"

# atm we still run a dev version of jupyverse
args = [
"hatch",
"run",
"dev.jupyterlab-noauth:jupyverse",
f"--kernels.connection_path={str(self.kernel_file_dir.name)}",
"--port",
f"{self.server_port}",
"jupyverse",
"--set", "kernels.allow_external_kernels=true",
"--set" ,f"kernels.external_connection_dir={str(self.kernel_file_dir.name)}",
"--set" ,f"auth.token={token}",
"--port", f"{self.server_port}",
]
self.server_process = subprocess.Popen(
args, cwd=self.jupyverse_dir, shell=False
args, shell=False
)

# we need to wait a tiny bit st the page is ready
def setUrl():
self.browser.setUrl(QUrl(f"http://127.0.0.1:{self.server_port}"))
self.browser.setUrl(QUrl(f"http://127.0.0.1:{self.server_port}/?token={token}"))

QTimer.singleShot(1000, setUrl)
QTimer.singleShot(2000, setUrl)

def closeEvent(self, event):
# do stuff
Expand All @@ -141,20 +134,8 @@ def closeEvent(self, event):


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(
prog="kernel-demo", description="show a kernel in qt application"
)

parser.add_argument("jupyverse_dir")

args = parser.parse_args()

app = QApplication(sys.argv)
kernel_widget = KernelWidget(
kernel_name="qt-python", jupyverse_dir=args.jupyverse_dir
)
kernel_widget = KernelWidget(kernel_name="qt-python")

kernel_widget.show()

Expand Down
77 changes: 77 additions & 0 deletions my_notebook.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"metadata": {
"kernelspec": {
"name": "qt-python"
},
"language_info": {
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"version": "3.12.0rc3"
}
},
"nbformat_minor": 5,
"nbformat": 4,
"cells": [
{
"id": "82774918-e61b-4383-b44d-ad2739ff5e34",
"cell_type": "code",
"source": "from kernel_widget import get_kernel_widget\nfrom PyQt5.QtWidgets import QPushButton",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"id": "f249770b-d42c-4432-953c-36ae94ee850b",
"cell_type": "code",
"source": "button = QPushButton()\nbutton.setText(\"black magic\")",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"id": "9aa5486b-ff9b-46a4-b661-b2634bcbdec8",
"cell_type": "code",
"source": "def say_hello():\n print(\"hello from here\")",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"id": "ab7d7956-10d3-4032-9e8f-9b67ac5984b1",
"cell_type": "code",
"source": "button.clicked.connect(say_hello)",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"id": "02ee9a0a-27d1-4706-8e9c-32fed24bf24f",
"cell_type": "code",
"source": "get_kernel_widget().layout.addWidget(button)",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
},
{
"id": "2d724999-921f-438c-afa8-aeef905e4ef6",
"cell_type": "code",
"source": "",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
}
]
}
Loading

0 comments on commit d7960f6

Please sign in to comment.