I built a QWidget to integrate Jupyter Lab into my project and wanted to get your impressions and advice. I have main.py that shows the JupyterWidget in a single iframe, and main2,py that passes a callback and an extra parameter to allow navigation. I want to add additional widgets to launch from the main widget and navigate between them.
jupyter_widget.py
from PyQt6.QtCore import QUrl, QTimer
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLabel
from PyQt6.QtWebEngineWidgets import QWebEngineView
class JupyterWidget(QWidget):
def __init__(self, switch_to_main_callback=None, extra_param=None):
super().__init__()
self.browser = QWebEngineView()
# Add a startup page
self.browser.setHtml('''
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome to the Jupyter Notebook Interface</h1>
<p>Checking Jupyter Server status...</p>
</body>
</html>
''')
# Button to go back to main menu if callback provided
if switch_to_main_callback:
self.back_button = QPushButton('Back to Main Menu')
self.back_button.clicked.connect(switch_to_main_callback)
else:
self.back_button = None
# Create layout for the widget
layout = QVBoxLayout()
# Add back button if it exists
if self.back_button:
layout.addWidget(self.back_button)
# Add the web view
layout.addWidget(self.browser)
# Extra param usage example, add only if not None or empty
if extra_param:
self.extra_label = QLabel(f"Extra Param: {extra_param}")
layout.addWidget(self.extra_label)
self.setLayout(layout)
self.jupyter_process = None
self.check_server_and_start()
def check_server_and_start(self):
if not self.is_server_running():
self.start_jupyter()
else:
self.load_jupyter_lab()
def is_server_running(self):
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex(('localhost', 8888))
return result == 0
def start_jupyter(self):
import subprocess
self.jupyter_process = subprocess.Popen(['jupyter', 'lab', '--no-browser'])
QTimer.singleShot(2000, self.load_jupyter_lab) # Adjust delay as needed
def load_jupyter_lab(self):
self.browser.setUrl(QUrl("http://localhost:8888/lab"))
def stop_jupyter(self):
if self.jupyter_process:
self.jupyter_process.terminate()
self.jupyter_process = None
def closeEvent(self, event):
self.stop_jupyter() # Ensure the server is stopped
event.accept()
main.py
import subprocess
import socket
import jupyter_widget
from PyQt6.QtCore import QUrl, QTimer
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QLabel, QVBoxLayout
from PyQt6.QtWebEngineWidgets import QWebEngineView
import sys
import subprocess
import socket
from jupyter_widget import JupyterWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Frank Math')
self.resize(1600, 900)
jupyter_widget = JupyterWidget()
self.setCentralWidget(jupyter_widget)
def closeEvent(self, event):
self.centralWidget().stop_jupyter() # Ensure the server is stopped
event.accept()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
main2.py
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QStackedWidget
import sys
from jupyter_widget import JupyterWidget
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Frank Math Navigator')
self.resize(800, 600)
# Create stacked widget
self.stacked_widget = QStackedWidget()
# Create main menu layout
self.main_menu = QWidget()
self.main_layout = QVBoxLayout()
# Add buttons for different programs
self.jupyter_button = QPushButton('Open Jupyter Notebook')
self.jupyter_button.clicked.connect(self.show_jupyter)
self.main_layout.addWidget(self.jupyter_button)
self.main_menu.setLayout(self.main_layout)
# Add widgets to stacked widget
self.stacked_widget.addWidget(self.main_menu)
self.jupyter_widget = None
self.setCentralWidget(self.stacked_widget)
def show_jupyter(self):
if self.jupyter_widget is None:
self.jupyter_widget = JupyterWidget(self.show_main_menu, extra_param="Extra Info")
self.stacked_widget.addWidget(self.jupyter_widget)
self.stacked_widget.setCurrentWidget(self.jupyter_widget)
def show_main_menu(self):
if self.jupyter_widget:
self.jupyter_widget.stop_jupyter()
self.jupyter_widget = None
self.stacked_widget.setCurrentWidget(self.main_menu)
def closeEvent(self, event):
if self.jupyter_widget:
self.jupyter_widget.stop_jupyter()
event.accept()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())