Skip to content

Commit

Permalink
Add notebook top bar with busy indicator (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart authored Feb 24, 2024
1 parent 942f57e commit f1a5dd7
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
1 change: 1 addition & 0 deletions plugins/kernel/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ classifiers = [
]
dependencies = [
"txl",
"asphalt",
"python-dateutil >=2.8.2",
"pycrdt >=0.8.11,<0.9.0",
]
Expand Down
15 changes: 15 additions & 0 deletions plugins/kernel/txl_kernel/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import time
from typing import Dict

from asphalt.core import Event, Signal
from pycrdt import Array, Map

from .message import create_message
Expand Down Expand Up @@ -31,7 +32,15 @@ def send(self, buffers):
asyncio.create_task(self.send_message(msg, self.shell_channel, change_date_to_str=True))


class BusyEvent(Event):
def __init__(self, source, topic, busy: bool):
super().__init__(source, topic)
self.busy = busy


class KernelMixin:
busy = Signal(BusyEvent)

def __init__(self):
self.msg_cnt = 0
self.execute_requests: Dict[str, Dict[str, asyncio.Queue]] = {}
Expand Down Expand Up @@ -89,6 +98,12 @@ async def recv(self):
elif msg_type == "comm_msg":
for comm_handler in self.comm_handlers:
comm_handler.comm_msg(msg)
elif msg_type == "status":
execution_state = msg["content"]["execution_state"]
if execution_state == "idle":
self.busy.dispatch(False)
elif execution_state == "busy":
self.busy.dispatch(True)
msg_id = msg["parent_header"].get("msg_id")
if msg_id in self.execute_requests:
# msg["header"] = str_to_date(msg["header"])
Expand Down
37 changes: 37 additions & 0 deletions plugins/notebook_editor/txl_notebook_editor/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
import anyio
from asphalt.core import Component, Context
from httpx import AsyncClient
from textual.app import RenderResult
from textual.containers import VerticalScroll
from textual.events import Event
from textual.keys import Keys
from textual.reactive import Reactive
from textual.widget import Widget
from textual.widgets import Select

from txl.base import (
Expand All @@ -27,6 +30,33 @@
ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}


class TopBar(Widget):
DEFAULT_CSS = """
TopBar {
dock: top;
width: 100%;
background: $foreground 5%;
text-align: right;
color: $text;
height: 1;
}
"""
_busy_indicator = Reactive("○")
_busy = False

@property
def busy(self) -> bool:
return self._busy

@busy.setter
def busy(self, value: bool):
self._busy = value
self._busy_indicator = "◉" if value else "○"

def render(self) -> RenderResult:
return self._busy_indicator


class NotebookEditorMeta(type(Editor), type(VerticalScroll)):
pass

Expand Down Expand Up @@ -55,6 +85,11 @@ def __init__(
self.edit_mode = False
self.nb_change_target = asyncio.Queue()
self.nb_change_events = asyncio.Queue()
self.top_bar = TopBar()
self.mount(self.top_bar)

async def watch_busy(self, event):
self.top_bar.busy = event.busy

async def on_open(self, event: FileOpenEvent) -> None:
await self.open(event.path)
Expand Down Expand Up @@ -123,6 +158,7 @@ def update(self):
kernel_name = ipynb.get("metadata", {}).get("kernelspec", {}).get("name")
if kernel_name:
self.kernel = self.kernels(kernel_name)
self.kernel.kernel.busy.connect(self.watch_busy)
for i_cell in range(self.ynb.cell_number):
cell = self.cell_factory(
self.ynb.ycells[i_cell], self.language, self.kernel
Expand Down Expand Up @@ -152,6 +188,7 @@ async def observe_nb_changes(self):
kernel_name = kernelspec.get("name")
if kernel_name:
self.kernel = self.kernels(kernel_name)
self.kernel.kernel.busy.connect(self.watch_busy)
elif target == "state":
if "dirty" in events.keys:
dirty = events.keys["dirty"]["newValue"]
Expand Down

0 comments on commit f1a5dd7

Please sign in to comment.