From 1e156c8a2d0a58a4b80297d80c9f6acb6c5ee9d7 Mon Sep 17 00:00:00 2001 From: tdpetrou Date: Mon, 16 Mar 2020 11:49:30 -0400 Subject: [PATCH 1/5] added png return for _repr_html_ for inline figures --- lib/matplotlib/figure.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b4141c8668f7..560d0ba41e15 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -375,6 +375,15 @@ def _repr_html_(self): from matplotlib.backends import backend_webagg return backend_webagg.ipython_inline_display(self) + if mpl.rcParams['backend'] == 'module://ipykernel.pylab.backend_inline': + from io import BytesIO + from base64 import b64encode + png_bytes = BytesIO() + self.canvas.print_figure(png_bytes, format='png') + s = png_bytes.getvalue() + s1 = b64encode(s).decode() + return f'' + def show(self, warn=True): """ If using a GUI backend with pyplot, display the figure window. From 2a82c3228128ff4d89091f94b85d8c791175d461 Mon Sep 17 00:00:00 2001 From: tdpetrou Date: Mon, 16 Mar 2020 19:21:12 -0400 Subject: [PATCH 2/5] created _repr_html_ for FigureCanvasBase --- lib/matplotlib/backend_bases.py | 37 +++++++++++++++++++++++ lib/matplotlib/backends/backend_webagg.py | 3 ++ lib/matplotlib/figure.py | 15 +-------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 18a750c9f46d..579cd889fbbe 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1595,6 +1595,43 @@ def __init__(self, figure): self.toolbar = None # NavigationToolbar2 will set me self._is_idle_drawing = False + def _repr_html_(self): + from base64 import b64encode + + fmt = self.get_default_filetype() + dpi = self.figure.dpi + if fmt == 'retina': + dpi = dpi * 2 + fmt = 'png' + + 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) + raw_bytes = bytes_io.getvalue() + + mimetype_dict = {'png': 'image/png', 'svg': 'image/svg+xml', + 'jpg': 'image/jpeg', 'pdf': 'application/pdf'} + if fmt not in mimetype_dict: + fmt = 'png' + mimetype = mimetype_dict[fmt] + + if fmt == 'svg': + return raw_bytes.decode() + elif fmt == 'pdf': + data = b64encode(raw_bytes).decode() + w, h = self.figure.get_size_inches() + return f'' + else: + data = b64encode(raw_bytes).decode() + return f'' + @classmethod @functools.lru_cache() def _fix_ipython_backend2gui(cls): diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index cd5ada8cd073..b7b2601c948a 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -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 diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 560d0ba41e15..ea624eb1638f 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -369,20 +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) - - if mpl.rcParams['backend'] == 'module://ipykernel.pylab.backend_inline': - from io import BytesIO - from base64 import b64encode - png_bytes = BytesIO() - self.canvas.print_figure(png_bytes, format='png') - s = png_bytes.getvalue() - s1 = b64encode(s).decode() - return f'' + return self.canvas._repr_html_() def show(self, warn=True): """ From 71d4c190b832825de32ead392c82622d2bc0d1ea Mon Sep 17 00:00:00 2001 From: tdpetrou Date: Mon, 16 Mar 2020 19:31:30 -0400 Subject: [PATCH 3/5] using calculated dpi --- lib/matplotlib/backend_bases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 579cd889fbbe..9fb5a73a7fad 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1608,7 +1608,7 @@ def _repr_html_(self): "format":fmt, "facecolor":self.figure.get_facecolor(), "edgecolor":self.figure.get_edgecolor(), - "dpi":self.figure.dpi, + "dpi":dpi, "bbox_inches":self.figure.bbox_inches, } From efd3168c991bf8351582a0951067b994e6534fb7 Mon Sep 17 00:00:00 2001 From: tdpetrou Date: Tue, 17 Mar 2020 15:58:14 -0400 Subject: [PATCH 4/5] corrected for retina --- lib/matplotlib/backend_bases.py | 62 ++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 9fb5a73a7fad..99cf1c29787c 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1596,41 +1596,71 @@ def __init__(self, figure): self._is_idle_drawing = False def _repr_html_(self): - from base64 import b64encode + if not self.figure.axes and not self.figure.lines: + return - fmt = self.get_default_filetype() dpi = self.figure.dpi - if fmt == 'retina': - dpi = dpi * 2 - fmt = 'png' + is_retina = False + + import IPython + ip = IPython.get_ipython() + ib_list = [c for c in ip.configurables + if 'InlineBackend' in type(c).__name__] + + # having an 'inline' backend doesn't mean '%matplotlib inline' has been run + # Running %matplotlib inline runs pylabtools.configure_inline_support + # which appends the InlineBackend to the list of configurables + if get_backend() == 'module://ipykernel.pylab.backend_inline' and ib_list: + ib = ib_list[0] + bbox_inches = ib.print_figure_kwargs['bbox_inches'] + fmt = next(iter(ib.figure_formats)) + if fmt == 'retina': + is_retina = True + dpi = dpi * 2 + fmt = self.get_default_filetype() + else: + bbox_inches = 'tight' # how to let user choose self.figure.bbox_inches? + fmt = self.get_default_filetype() + + if fmt not in {'png', 'svg', 'jpg', 'pdf'}: + fmt = 'png' kw = { "format":fmt, "facecolor":self.figure.get_facecolor(), "edgecolor":self.figure.get_edgecolor(), "dpi":dpi, - "bbox_inches":self.figure.bbox_inches, + "bbox_inches":bbox_inches, } bytes_io = io.BytesIO() self.print_figure(bytes_io, **kw) raw_bytes = bytes_io.getvalue() - mimetype_dict = {'png': 'image/png', 'svg': 'image/svg+xml', - 'jpg': 'image/jpeg', 'pdf': 'application/pdf'} - if fmt not in mimetype_dict: - fmt = 'png' - mimetype = mimetype_dict[fmt] + from IPython.core.display import _pngxy, _jpegxy if fmt == 'svg': return raw_bytes.decode() + elif fmt == 'png': + w, h = _pngxy(raw_bytes) + elif fmt == 'jpg': + w, h = _jpegxy(raw_bytes) elif fmt == 'pdf': - data = b64encode(raw_bytes).decode() w, h = self.figure.get_size_inches() - return f'' - else: - data = b64encode(raw_bytes).decode() - return f'' + w, h = w * dpi, h * dpi + + if is_retina: + w, h = w // 2, h // 2 + + from base64 import b64encode + data = b64encode(raw_bytes).decode() + + if fmt == 'png': + return f'' + elif fmt == 'pdf': + return f'' + elif fmt == 'jpg': + return f'' @classmethod @functools.lru_cache() From b09de9c9bb0cf8f325778f8c1ca641ccc0c12e1b Mon Sep 17 00:00:00 2001 From: tdpetrou Date: Wed, 18 Mar 2020 10:09:17 -0400 Subject: [PATCH 5/5] defer to ipython for html whenever possible --- lib/matplotlib/backend_bases.py | 66 ++++++++++----------------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 99cf1c29787c..bf87c9c43796 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1596,71 +1596,43 @@ def __init__(self, figure): self._is_idle_drawing = False def _repr_html_(self): - if not self.figure.axes and not self.figure.lines: - return - - dpi = self.figure.dpi - is_retina = False - - import IPython - ip = IPython.get_ipython() - ib_list = [c for c in ip.configurables - if 'InlineBackend' in type(c).__name__] - - # having an 'inline' backend doesn't mean '%matplotlib inline' has been run - # Running %matplotlib inline runs pylabtools.configure_inline_support - # which appends the InlineBackend to the list of configurables - if get_backend() == 'module://ipykernel.pylab.backend_inline' and ib_list: - ib = ib_list[0] - bbox_inches = ib.print_figure_kwargs['bbox_inches'] - fmt = next(iter(ib.figure_formats)) - if fmt == 'retina': - is_retina = True - dpi = dpi * 2 - fmt = self.get_default_filetype() - else: - bbox_inches = 'tight' # how to let user choose self.figure.bbox_inches? - fmt = self.get_default_filetype() - - if fmt not in {'png', 'svg', 'jpg', 'pdf'}: - fmt = 'png' + # 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 + + fmt = self.get_default_filetype() kw = { "format":fmt, "facecolor":self.figure.get_facecolor(), "edgecolor":self.figure.get_edgecolor(), - "dpi":dpi, - "bbox_inches":bbox_inches, + "dpi":self.figure.dpi, + "bbox_inches":self.figure.bbox_inches } bytes_io = io.BytesIO() self.print_figure(bytes_io, **kw) raw_bytes = bytes_io.getvalue() - from IPython.core.display import _pngxy, _jpegxy - + from base64 import b64encode + data = b64encode(raw_bytes).decode() + if fmt == 'svg': return raw_bytes.decode() elif fmt == 'png': - w, h = _pngxy(raw_bytes) - elif fmt == 'jpg': - w, h = _jpegxy(raw_bytes) + return f'' elif fmt == 'pdf': w, h = self.figure.get_size_inches() - w, h = w * dpi, h * dpi - - if is_retina: - w, h = w // 2, h // 2 - - from base64 import b64encode - data = b64encode(raw_bytes).decode() - - if fmt == 'png': - return f'' - elif fmt == 'pdf': + w, h = w * self.figure.dpi, h * self.figure.dpi return f'' elif fmt == 'jpg': - return f'' + return f'' @classmethod @functools.lru_cache()