Skip to content

Commit 7fae1a6

Browse files
feat: support nbagg matplotlib backend (WIP)
1 parent 24c9b03 commit 7fae1a6

File tree

4 files changed

+909
-1
lines changed

4 files changed

+909
-1
lines changed

nbclient/client.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
)
2525
from .util import run_sync, ensure_async
2626
from .output_widget import OutputWidget
27+
from .matplotlib import MatplotlibCommHandler
2728

2829

2930
def timestamp():
@@ -293,7 +294,8 @@ def __init__(self, nb, km=None, **kw):
293294
}
294295
# comm_open_handlers should return an object with a .handle_msg(msg) method or None
295296
self.comm_open_handlers = {
296-
'jupyter.widget': self.on_comm_open_jupyter_widget
297+
'jupyter.widget': self.on_comm_open_jupyter_widget,
298+
'matplotlib': self.on_comm_open_matplotlib
297299
}
298300

299301
def reset_execution_trackers(self):
@@ -914,6 +916,12 @@ def on_comm_open_jupyter_widget(self, msg):
914916
if widget_class:
915917
return widget_class(comm_id, state, self.kc, self)
916918

919+
def on_comm_open_matplotlib(self, msg):
920+
content = msg['content']
921+
data = content['data']
922+
return MatplotlibCommHandler(comm_id=content['comm_id'], kernel_client=self.kc,
923+
parent_header=msg['parent_header'], nbagg_id=data['id'])
924+
917925

918926
def execute(nb, cwd=None, km=None, **kwargs):
919927
"""Execute a notebook's code, updating outputs within the notebook object.

nbclient/matplotlib.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
3+
from .jsonutil import json_clean
4+
5+
6+
class MatplotlibCommHandler:
7+
def __init__(self, comm_id, kernel_client, parent_header, nbagg_id):
8+
self.comm_id = comm_id
9+
self.kernel_client = kernel_client
10+
self.parent_header = parent_header
11+
self.nbagg_id = nbagg_id
12+
13+
# mimics https://github.com/matplotlib/matplotlib/blob/002b27e352b90410c9840233b6ce42c54e291403/lib/matplotlib/backends/web_backend/js/mpl.js#L63 # noqa
14+
self.send('{"value":false,"type":"supports_binary","figure_id":"%s"}' % nbagg_id)
15+
self.send('{"type":"send_image_mode","figure_id":"%s"}' % nbagg_id)
16+
self.send('{"dpi_ratio":2,"type":"set_dpi_ratio","figure_id":"%s"}' % nbagg_id)
17+
self.send('{"type":"refresh","figure_id":"%s"}' % nbagg_id)
18+
19+
def _publish_msg(self, msg_type, data=None, metadata=None, buffers=None, **keys):
20+
"""Helper for sending a comm message on IOPub"""
21+
data = {} if data is None else data
22+
metadata = {} if metadata is None else metadata
23+
content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
24+
# it seems from looking at the websocket output in Chrome, that parent header
25+
# is always empty
26+
msg = self.kernel_client.session.msg(msg_type, content=content, parent={},
27+
metadata=metadata)
28+
print("SEND", msg)
29+
self.kernel_client.shell_channel.send(msg)
30+
31+
def send(self, data=None, metadata=None, buffers=None):
32+
self._publish_msg('comm_msg', data=data, metadata=metadata, buffers=buffers)
33+
34+
def handle_msg(self, msg):
35+
print("RECV", msg)
36+
content = msg['content']
37+
data = content['data']
38+
nbagg_data = data.get('data')
39+
if nbagg_data:
40+
nbagg_data = json.loads(nbagg_data)
41+
print(nbagg_data)
42+
if nbagg_data.get('type') == 'refresh':
43+
# cannot figure out when we should send this
44+
self.send('{"type":"refresh","figure_id":"%s"}' % self.nbagg_id)
45+
if nbagg_data.get('type') == 'resize':
46+
# mimics https://github.com/matplotlib/matplotlib/blob/002b27e352b90410c9840233b6ce42c54e291403/lib/matplotlib/backends/web_backend/js/mpl.js#L354 # noqa
47+
self.send('{"type":"draw","figure_id":"%s"}' % self.nbagg_id)

0 commit comments

Comments
 (0)