diff --git a/IPython/core/pylabtools.py b/IPython/core/pylabtools.py index a53a56e..957dbbf 100644 --- a/IPython/core/pylabtools.py +++ b/IPython/core/pylabtools.py @@ -94,8 +94,8 @@ def figsize(sizex, sizey): matplotlib.rcParams['figure.figsize'] = [sizex, sizey] -def print_figure(fig, fmt='png'): - """Convert a figure to svg or png for inline display.""" +def print_figure(fig, fmt='png', quality=90): + """Convert a figure to svg, jpg (if PIL is installed) or png for inline display.""" from matplotlib import rcParams # When there's an empty figure, we shouldn't return anything, otherwise we # get big blank areas in the qt console. @@ -110,7 +110,7 @@ def print_figure(fig, fmt='png'): dpi = dpi * 2 fmt = 'png' fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight', - facecolor=fc, edgecolor=ec, dpi=dpi) + facecolor=fc, edgecolor=ec, dpi=dpi, quality=quality) data = bytes_io.getvalue() return data @@ -163,7 +163,7 @@ def mpl_runner(safe_execfile): return mpl_execfile -def select_figure_format(shell, fmt): +def select_figure_format(shell, fmt, quality): """Select figure format for inline backend, can be 'png', 'retina', or 'svg'. Using this method ensures only one figure format is active at a time. @@ -173,10 +173,14 @@ def select_figure_format(shell, fmt): svg_formatter = shell.display_formatter.formatters['image/svg+xml'] png_formatter = shell.display_formatter.formatters['image/png'] + jpg_formatter = shell.display_formatter.formatters['image/jpeg'] if fmt == 'png': svg_formatter.pop(Figure, None) png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png')) + elif fmt in ('jpg', 'jpeg'): + svg_formatter.type_printers.pop(Figure, None) + jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', quality)) elif fmt in ('png2x', 'retina'): svg_formatter.pop(Figure, None) png_formatter.for_type(Figure, retina_figure) @@ -338,5 +342,5 @@ def configure_inline_support(shell, backend): del shell._saved_rcParams # Setup the default figure format - select_figure_format(shell, cfg.figure_format) + select_figure_format(shell, cfg.figure_format, cfg.quality) diff --git a/IPython/kernel/zmq/pylab/config.py b/IPython/kernel/zmq/pylab/config.py index ea83bfc..f7859bc 100644 --- a/IPython/kernel/zmq/pylab/config.py +++ b/IPython/kernel/zmq/pylab/config.py @@ -14,13 +14,19 @@ This module does not import anything from matplotlib. #----------------------------------------------------------------------------- from IPython.config.configurable import SingletonConfigurable -from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool +from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool, Int from IPython.utils.warn import warn #----------------------------------------------------------------------------- # Configurable for inline backend options #----------------------------------------------------------------------------- +try: + from PIL import Image + has_pil = True +except: + has_pil = False + # inherit from InlineBackendConfig for deprecation purposes class InlineBackendConfig(SingletonConfigurable): pass @@ -53,7 +59,20 @@ class InlineBackend(InlineBackendConfig): inline backend.""" ) - figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True, + fmts = ['svg', 'png', 'retina'] + + if has_pil: + # If we have PIL using jpeg as inline image format can save some bytes. + fmts.append('jpg') + + # Matplotlib's JPEG printer supports a quality option that can be tweaked. + # We expose it only if PIL is available so the user isn't confused. But it + # isn't guarded by "has_pil" test because core/pylabtools.py expects this + # field OR we need to propagate the has_pil test to that module too. + quality = Int(default_value=90, config=has_pil, + help="Quality of compression [0-100], currently for lossy JPEG only.") + + figure_format = CaselessStrEnum(fmts, default_value='png', config=True, help="The image format for figures with the inline backend.") def _figure_format_changed(self, name, old, new):