Skip to content

Adding png image return for inline backend figures with _repr_html_ #16788

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

Closed
wants to merge 5 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
39 changes: 39 additions & 0 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,45 @@ def __init__(self, figure):
self.toolbar = None # NavigationToolbar2 will set me
self._is_idle_drawing = False

def _repr_html_(self):
# Defer to IPython to handle html output if possible
if 'IPython' in sys.modules:
import IPython
ip = IPython.get_ipython()
# Check whether %matplotlib was run. Is there a better way?
ib_list = [c for c in ip.configurables
if 'InlineBackend' in type(c).__name__]
if ib_list:
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just dived into IPython itself. It looks like we are also storing the sate on a function attribute (!!!).

In [1]: from IPython.core.pylabtools import configure_inline_support

In [2]: configure_inline_support.current_backend
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-eec19e858e54> in <module>
----> 1 configure_inline_support.current_backend

AttributeError: 'function' object has no attribute 'current_backend'

In [3]: %matplotlib qt

In [4]: configure_inline_support.current_backend
Out[4]: 'other'

In [5]: %matplotlib inline

In [6]: configure_inline_support.current_backend
Out[6]: 'inline'

Not sure it is better though.


fmt = self.get_default_filetype()

kw = {
"format":fmt,
"facecolor":self.figure.get_facecolor(),
"edgecolor":self.figure.get_edgecolor(),
"dpi":self.figure.dpi,
"bbox_inches":self.figure.bbox_inches
}

bytes_io = io.BytesIO()
self.print_figure(bytes_io, **kw)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just pass the kwargs here (having a separate dict doesn't seem to buy much)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and you need to force the format here. again, bytes_io could easily contain a postscript image here, for example.

raw_bytes = bytes_io.getvalue()

from base64 import b64encode
data = b64encode(raw_bytes).decode()

if fmt == 'svg':
return raw_bytes.decode()
elif fmt == 'png':
return f'<img src="data:image/png;base64, {data}" />'
elif fmt == 'pdf':
w, h = self.figure.get_size_inches()
w, h = w * self.figure.dpi, h * self.figure.dpi
return f'<embed width="{w}" height="{h}" src="data:application/pdf;base64, {data}">'
elif fmt == 'jpg':
return f'<img src="data:image/jpeg;base64, {data}" />'

@classmethod
@functools.lru_cache()
def _fix_ipython_backend2gui(cls):
Expand Down
3 changes: 3 additions & 0 deletions lib/matplotlib/backends/backend_webagg.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ def run(self):
class FigureCanvasWebAgg(core.FigureCanvasWebAggCore):
_timer_cls = TimerTornado

def _repr_html_(self):
return ipython_inline_display(self.figure)

def show(self):
# show the figure window
global show # placates pyflakes: created by @_Backend.export below
Expand Down
6 changes: 1 addition & 5 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,7 @@ def __init__(self,
# use it, for some reason.

def _repr_html_(self):
# We can't use "isinstance" here, because then we'd end up importing
# webagg unconditionally.
if 'WebAgg' in type(self.canvas).__name__:
from matplotlib.backends import backend_webagg
return backend_webagg.ipython_inline_display(self)
return self.canvas._repr_html_()

def show(self, warn=True):
"""
Expand Down