How to display a graphic
jlries61 opened this issue · comments
I have been developing a Metakernel based kernel for Salford Predictive Miner (https://www.salford-systems.com/products/spm) and while text output is displayed in a Jupyter notebook very nicely, attempts to display graphics give me something like this:
<Figure size 640x480 with 1 Axes>
...which is, of course, completely unhelpful.
The code to display the graphic looks like this:
fig = plt.figure()
varimp_series.plot.barh(title="Variable Importances", color="blue")
plt.xlabel("Importance")
plt.ylabel("Predictor Name")
self.Display(fig)
I have written a version of handle_plot_settings
as follows (copied from the Gnuplot kernel):
def handle_plot_settings(self):
"""Handle the current plot settings"""
settings = self.plot_settings
if ('termspec' not in settings or not settings['termspec']):
settings['termspec'] = ('pngcairo size 385, 256' ' font "Arial,10"')
if ('format' not in settings or not settings['format']):
settings['format'] = 'png'
self.inline_plotting = settings['backend'] == 'inline'
This is invoked in do_execute_direct
like so:
if self._first:
self._first = False
self.handle_plot_settings()
The header of my kernel class looks like this:
class SPMKernel(ProcessMetaKernel):
implementation = 'spm_kernel'
implementation_version = __version__
language = 'SPM'
language_version = '8.3'
language_info = {'name': 'str',
'codemirror_mode': 'shell',
'mimetype': 'text/x-sh',
'file_extension': '.cmd'}
_first = True
Unfortunately, the documentation I have seen thus far isn't much help, but I know it can be done because other kernels do it. Any guidance I might get on this would be greatly appreciated.
The version of the Metakernel I am using is 0.20.14
Thanks, Steve, but it looks like I'm not quite there yet. I changed my code as follows:
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi='figure')
width = fig.get_figwidth()
data = "<img src='data:image/png;base64,{0}' width={1}/>"
data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
display(HTML(data))
And I get:
<IPython.core.display.HTML object>
Any ideas?
John
You need to use self.Display(HTML(data))
as the last line to use metakernel's display handler.
Edit: you need to call the parent class Display
method.
@blink1073 The code now reads:
fig = plt.figure()
varimp_series.plot.barh(title="Variable Importances", color="blue")
plt.xlabel("Importance")
plt.ylabel("Predictor Name")
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi='figure')
width = fig.get_figwidth()
data = "<img src='data:image/png;base64,{0}' width={1}/>"
data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
self.Display(HTML(data))
And we now get no output at all, nor useful warnings in the log. Any further thoughts?
Sorry for the confusion, I meant that the last line should be super(MetaKernel, self).Display(data)
to invoke the parent method.
Whoops, one edit: super(MetaKernel, self).Display(HTML(data))
@blink1073 That didn't work. My code now reads as follows:
fig = plt.figure()
varimp_series.plot.barh(title="Variable Importances", color="blue")
plt.xlabel("Importance")
plt.ylabel("Predictor Name")
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi='figure')
width = fig.get_figwidth()
data = "<img src='data:image/png;base64,{0}' width={1}/>"
data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
#self.Display(HTML(data))
super(MetaKernel, self).Display(HTML(data))
And I get:
[MetaKernelApp] ERROR | Exception in message handler:
Traceback (most recent call last):
File "/home/jries/.local/lib/python3.7/site-packages/ipykernel/kernelbase.py", line 267, in dispatch_shell
yield gen.maybe_future(handler(stream, idents, msg))
File "/home/jries/.local/lib/python3.7/site-packages/tornado/gen.py", line 1133, in run
value = future.result()
File "/home/jries/.local/lib/python3.7/site-packages/tornado/gen.py", line 326, in wrapper
yielded = next(result)
File "/home/jries/.local/lib/python3.7/site-packages/ipykernel/kernelbase.py", line 534, in execute_request
user_expressions, allow_stdin,
File "/home/jries/.local/lib/python3.7/site-packages/metakernel/_metakernel.py", line 357, in do_execute
retval = self.do_execute_direct(code)
File "/home/jries/.local/lib/python3.7/site-packages/spm_kernel/kernel.py", line 193, in do_execute_direct
self.display_varimp(doc)
File "/home/jries/.local/lib/python3.7/site-packages/spm_kernel/kernel.py", line 112, in display_varimp
super(MetaKernel, self).Display(HTML(data))
AttributeError: 'super' object has no attribute 'Display'
Any further ideas?
Gah, my apologies, that should be super(<YourKernelClass>, self).Display(HTML(data))
.
@blink1073 So I modify my code as follows:
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi='figure')
msg=Image(fig)
width = fig.get_figwidth()
data = "<img src='data:image/png;base64,{0}' width={1}/>"
data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
super(SPMKernel, self).Display(HTML(data))
And we get no output. But the log looks like this:
[MetaKernelApp] Display Data
[MetaKernelApp] {'header': {'msg_id': '5a7d17ca-3e9577ba0eb7629721e1fd32', 'msg_type': 'execute_reply', 'username': 'jries', 'session': '3fe7fe67-3cc2fe643c47941011e929fd', 'date': datetime.datetime(2019, 5, 14, 17, 49, 44, 940431, tzinfo=datetime.timezone.utc), 'version': '5.3'}, 'msg_id': '5a7d17ca-3e9577ba0eb7629721e1fd32', 'msg_type': 'execute_reply', 'parent_header': {'msg_id': '48c81179a21843c38a635691c49bf0fc', 'username': 'username', 'session': '3d4c87e280c9495798150588fe094e10', 'msg_type': 'execute_request', 'version': '5.2', 'date': datetime.datetime(2019, 5, 14, 17, 49, 35, 176833, tzinfo=tzutc())}, 'content': {'status': 'ok', 'execution_count': 19, 'payload': [], 'user_expressions': {}}, 'metadata': {'started': datetime.datetime(2019, 5, 14, 17, 49, 40, 180709, tzinfo=datetime.timezone.utc)}, 'tracker': <zmq.sugar.tracker.MessageTracker object at 0x7fd5bc36d2e8>}
That code looks right to me. Are you able to share the full work in progress kernel? I'm having a hard time debugging without the full context.
spm_kernel.zip
Since SPM is proprietary, I am not at liberty to provide you a copy; but I can provide the full source code for the package as it stands (attached). The method under discussion is SPMKernel.display_varimp (starting at kernel.py:59).
Thank you once again for your help.
@blink1073 I removed some debug code from kernel.py and am resending the archive.
spm_kernel.zip
I think the only thing you're missing is properly setting the width in pixels (the width returned by get_width()
is in inches). I tested locally by making this change:
diff --git a/metakernel_python/metakernel_python.py b/metakernel_python/metakernel_python.py
index 13ceb79..51ed99f 100644
--- a/metakernel_python/metakernel_python.py
+++ b/metakernel_python/metakernel_python.py
@@ -49,6 +49,22 @@ class MetaKernelPython(MetaKernel):
return python_magic.env.get(name, None)
def do_execute_direct(self, code):
+ import matplotlib.pyplot as plt
+ import io
+ from base64 import b64encode
+ from IPython.display import HTML
+
+ fig = plt.figure()
+ plt.plot([1,2,3])
+ plt.xlabel("Importance")
+ plt.ylabel("Predictor Name")
+ buf = io.BytesIO()
+ fig.savefig(buf, format='png', dpi='figure')
+ width = fig.get_figwidth() * fig.dpi
+ data = "<img src='data:image/png;base64,{0}' width={1}/>"
+ data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
+ self.Display(HTML(data))
+
python_magic = self.line_magics['python']
return python_magic.eval(code.strip())
We have success! My code now reads as follows:
varimp_series.plot.barh(title="Variable Importances", color="blue")
plt.xlabel("Importance")
plt.ylabel("Predictor Name")
img = Image(data=fig)
buf = io.BytesIO()
fig.savefig(buf, format='png', dpi='figure')
width = fig.get_figwidth() * fig.dpi
data = "<img src='data:image/png;base64,{0}' width={1}/>"
data = data.format(b64encode(buf.getvalue()).decode('utf-8'), width)
super(SPMKernel, self).Display(HTML(data))
@blink1073 My profuse thanks for your assistance.
Glad I could help!