Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎉 Adds kernel menu to control kernel #282

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pyflow/blocks/executableblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ def run_blocks(self):
def run_left(self):
"""Run all of the block's dependencies and then run the block."""

# Start kernel
self.scene().kernel.start()

# Reset state to make sure that the self is run again
self.run_state = ExecutableState.IDLE

Expand All @@ -339,6 +342,9 @@ def run_left(self):
def run_right(self):
"""Run all of the output blocks and all their dependencies."""

# Start kernel
self.scene().kernel.start()

# To avoid crashing when spamming the button
if self.transmitting_queue:
return
Expand Down
32 changes: 31 additions & 1 deletion pyflow/core/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,39 @@ class Kernel:

@log_init_time(LOGGER)
def __init__(self):
self.kernel_manager, self.client = start_new_kernel()
self.client = None
self.kernel_manager = None
self.execution_queue: List[Tuple["ExecutableBlock", str]] = []
self.busy = False

def start(self):
"""
Starts the kernel
"""
# Check if client exists
if self.client is None:
self.kernel_manager, self.client = start_new_kernel()

def stop(self):
"""
Stops the kernel
"""
self.kernel_manager.shutdown_kernel()
self.client = None
self.kernel_manager = None

def interrupt(self):
"""
Interrupts the kernel
"""
self.kernel_manager.interrupt_kernel()

def restart(self):
"""
Restarts the kernel
"""
self.kernel_manager.restart_kernel()

def message_to_output(self, message: dict) -> Tuple[str, str]:
"""
Converts a message sent by the kernel into a relevant output
Expand Down Expand Up @@ -104,6 +133,7 @@ def execute(self, code: str) -> str:
Return:
output from the last message sent by the kernel
"""
self.start()
_ = self.client.execute(code)
done = False
while not done:
Expand Down
76 changes: 76 additions & 0 deletions pyflow/graphics/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
QCheckBox,
)

from pyflow.blocks.executableblock import ExecutableBlock
from pyflow.core.executable import ExecutableState
from pyflow.graphics.widget import Widget
from pyflow.graphics.theme_manager import theme_manager

Expand Down Expand Up @@ -224,6 +226,32 @@ def createActions(self):
self._actSeparator = QAction(self)
self._actSeparator.setSeparator(True)

# Kernel
self._actStartKernel = QAction(
"&Start Kernel",
self,
statusTip="Start the kernel",
triggered=self.start_kernel,
)
self._actInterruptKernel = QAction(
"&Interrupt Kernel",
self,
statusTip="Interrupt the kernel",
triggered=self.interrupt_kernel,
)
self._actStopKernel = QAction(
"&Stop Kernel",
self,
statusTip="Stop the kernel",
triggered=self.stop_kernel,
)
self._actRestartKernel = QAction(
"&Restart Kernel",
self,
statusTip="Restart the kernel",
triggered=self.restart_kernel,
)

def createMenus(self):
"""Create the File menu with linked shortcuts."""
self.filemenu = self.menuBar().addMenu("&File")
Expand All @@ -248,6 +276,12 @@ def createMenus(self):
self.editmenu.addAction(self._actDuplicate)
self.editmenu.addAction(self._actRun)

self.kernelMenu = self.menuBar().addMenu("&Kernel")
self.kernelMenu.addAction(self._actStartKernel)
self.kernelMenu.addAction(self._actInterruptKernel)
self.kernelMenu.addAction(self._actStopKernel)
self.kernelMenu.addAction(self._actRestartKernel)

self.viewmenu = self.menuBar().addMenu("&View")
self.thememenu = self.viewmenu.addMenu("Theme")
self.thememenu.aboutToShow.connect(self.updateThemeMenu)
Expand Down Expand Up @@ -546,3 +580,45 @@ def onMoveToItems(self):
def setTheme(self, theme_index):
"""Set the theme of the application."""
theme_manager().selected_theme_index = theme_index

def start_kernel(self):
"""Start the kernel."""
current_window = self._call_kernel("Kernel started")
if current_window is not None:
current_window.scene.kernel.start()

def interrupt_kernel(self):
"""Interrupt the kernel."""
current_window = self._call_kernel("Kernel interrupted")
if current_window is not None:
current_window.scene.kernel.interrupt()

def stop_kernel(self):
"""Stop the kernel."""
current_window = self._call_kernel("Kernel stopped")
if current_window is not None:
current_window.scene.kernel.stop()
self.reset_block_states()

def restart_kernel(self):
"""Restart the kernel."""
current_window = self._call_kernel("Kernel restarted")
if current_window is not None:
current_window.scene.kernel.restart()
self.reset_block_states()

def _call_kernel(self, message):
"""Call a kernel function but check if a kernel exists first"""
current_window = self.activeMdiChild()
if current_window is not None:
self.statusbar.showMessage(message)
return current_window
else:
self.statusbar.showMessage("No active window")
return None

def reset_block_states(self):
current_scene = self.activeMdiChild().scene
for item in current_scene.items():
if isinstance(item, ExecutableBlock):
item.run_state = ExecutableState.IDLE