Skip to content

Commit

Permalink
add Jupyter console
Browse files Browse the repository at this point in the history
(console code generated by claude.ai)
  • Loading branch information
hongquanli authored and ianohara committed Jan 20, 2025
1 parent 560142b commit a894d80
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 6 deletions.
3 changes: 3 additions & 0 deletions software/control/_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,9 @@ def load_formats():
AWB_RATIOS_G = 1
AWB_RATIOS_B = 1.4141

USE_TERMINAL_CONSOLE = False
USE_JUPYTER_CONSOLE = False

try:
with open("cache/config_file_path.txt", "r") as file:
for line in file:
Expand Down
72 changes: 72 additions & 0 deletions software/control/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import traceback
import functools
import inspect
from qtconsole.rich_jupyter_widget import RichJupyterWidget
from qtconsole.inprocess import QtInProcessKernelManager


class QtCompleter:
Expand Down Expand Up @@ -213,3 +215,73 @@ def run(self):
)
except SystemExit:
break


from IPython.core.completer import IPCompleter
class NoFileCompleter(IPCompleter):
"""Custom completer that filters out file completions"""

def file_matches(self, text):
"""Override file_matches to return empty list"""
return []


class JupyterWidget(QWidget):
"""Widget that embeds a Jupyter console with PyQt5 integration"""
kernel_ready = Signal() # Signal emitted when kernel is ready

def __init__(self, namespace=None, parent=None):
super().__init__(parent)

if namespace is None:
namespace = {}

# Create kernel manager and kernel
self.kernel_manager = QtInProcessKernelManager()
self.kernel_manager.start_kernel()

# Get the kernel
kernel = self.kernel_manager.kernel
kernel.gui = 'qt'

# Replace the default completer with our custom one
kernel.shell.Completer = NoFileCompleter(
shell=kernel.shell,
namespace=kernel.shell.user_ns,
global_namespace=kernel.shell.user_global_ns,
use_jedi=True
)

# Update namespace
kernel.shell.user_ns.update(namespace)

# Create kernel client
self.kernel_client = self.kernel_manager.client()
self.kernel_client.start_channels()

# Create Jupyter widget
self.jupyter_widget = RichJupyterWidget()
self.jupyter_widget.kernel_manager = self.kernel_manager
self.jupyter_widget.kernel_client = self.kernel_client

# Layout
layout = QVBoxLayout()
layout.addWidget(self.jupyter_widget)
self.setLayout(layout)

# Emit signal when kernel is ready
self.kernel_ready.emit()

def execute_command(self, command):
"""Execute a command in the Jupyter kernel"""
self.jupyter_widget.execute(command)

def clear_console(self):
"""Clear the Jupyter console"""
self.jupyter_widget.clear()

def closeEvent(self, event):
"""Handle cleanup when widget is closed"""
self.kernel_client.stop_channels()
self.kernel_manager.shutdown_kernel()
event.accept()
15 changes: 15 additions & 0 deletions software/control/gui_hcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@

SINGLE_WINDOW = True # set to False if use separate windows for display and control

if USE_JUPYTER_CONSOLE:
from control.console import JupyterWidget


class MovementUpdater(QObject):
position_after_move = Signal(squid.abc.Pos)
Expand Down Expand Up @@ -179,6 +182,18 @@ def __init__(self, is_simulation=False, live_only_mode=False, *args, **kwargs):
led_matrix_action.triggered.connect(self.openLedMatrixSettings)
settings_menu.addAction(led_matrix_action)

if USE_JUPYTER_CONSOLE:
# Create namespace to expose to Jupyter
self.namespace = {
'microscope': self.microscope,
}

# Create Jupyter widget as a dock widget
self.jupyter_dock = QDockWidget("Jupyter Console", self)
self.jupyter_widget = JupyterWidget(namespace=self.namespace)
self.jupyter_dock.setWidget(self.jupyter_widget)
self.addDockWidget(Qt.LeftDockWidgetArea, self.jupyter_dock)

def loadObjects(self, is_simulation):
self.illuminationController = None
if is_simulation:
Expand Down
13 changes: 7 additions & 6 deletions software/main_hcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
from configparser import ConfigParser
from control.widgets import ConfigEditorBackwardsCompatible, ConfigEditorForAcquisitions
from control._def import CACHED_CONFIG_FILE_PATH
from control.console import ConsoleThread

from control._def import USE_TERMINAL_CONSOLE
if USE_TERMINAL_CONSOLE:
from control.console import ConsoleThread

def show_config(cfp, configpath, main_gui):
config_widget = ConfigEditorBackwardsCompatible(cfp, configpath, main_gui)
Expand Down Expand Up @@ -99,9 +100,9 @@ def show_acq_config(cfm):
menu_bar.addMenu(file_menu)
win.show()

console_locals = {"microscope": win.microscope}

console_thread = ConsoleThread(console_locals)
console_thread.start()
if USE_TERMINAL_CONSOLE:
console_locals = {"microscope": win.microscope}
console_thread = ConsoleThread(console_locals)
console_thread.start()

sys.exit(app.exec_())

0 comments on commit a894d80

Please sign in to comment.