Skip to content

Commit 20a7d12

Browse files
authored
Merge pull request #591 from martinRenou/use_matplotlib_inline
Use matplotlib-inline
2 parents b83a08c + 6a56612 commit 20a7d12

File tree

5 files changed

+17
-352
lines changed

5 files changed

+17
-352
lines changed

ipykernel/kernelapp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ def init_gui_pylab(self):
512512
# but lower priority than anything else (mpl.use() for instance).
513513
# This only affects matplotlib >= 1.5
514514
if not os.environ.get('MPLBACKEND'):
515-
os.environ['MPLBACKEND'] = 'module://ipykernel.pylab.backend_inline'
515+
os.environ['MPLBACKEND'] = 'module://matplotlib_inline.backend_inline'
516516

517517
# Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
518518
# to ensure that any exception is printed straight to stderr.

ipykernel/pylab/backend_inline.py

Lines changed: 7 additions & 239 deletions
Original file line numberDiff line numberDiff line change
@@ -3,245 +3,13 @@
33
# Copyright (c) IPython Development Team.
44
# Distributed under the terms of the Modified BSD License.
55

6-
import matplotlib
7-
from matplotlib.backends.backend_agg import (
8-
new_figure_manager,
9-
FigureCanvasAgg,
10-
new_figure_manager_given_figure,
11-
) # analysis: ignore
12-
from matplotlib import colors
13-
from matplotlib._pylab_helpers import Gcf
6+
import warnings
147

15-
from IPython.core.getipython import get_ipython
16-
from IPython.core.pylabtools import select_figure_formats
17-
from IPython.display import display
8+
from matplotlib_inline.backend_inline import * # analysis: ignore
189

19-
from .config import InlineBackend
2010

21-
22-
def show(close=None, block=None):
23-
"""Show all figures as SVG/PNG payloads sent to the IPython clients.
24-
25-
Parameters
26-
----------
27-
close : bool, optional
28-
If true, a ``plt.close('all')`` call is automatically issued after
29-
sending all the figures. If this is set, the figures will entirely
30-
removed from the internal list of figures.
31-
block : Not used.
32-
The `block` parameter is a Matplotlib experimental parameter.
33-
We accept it in the function signature for compatibility with other
34-
backends.
35-
"""
36-
if close is None:
37-
close = InlineBackend.instance().close_figures
38-
try:
39-
for figure_manager in Gcf.get_all_fig_managers():
40-
display(
41-
figure_manager.canvas.figure,
42-
metadata=_fetch_figure_metadata(figure_manager.canvas.figure)
43-
)
44-
finally:
45-
show._to_draw = []
46-
# only call close('all') if any to close
47-
# close triggers gc.collect, which can be slow
48-
if close and Gcf.get_all_fig_managers():
49-
matplotlib.pyplot.close('all')
50-
51-
52-
# This flag will be reset by draw_if_interactive when called
53-
show._draw_called = False
54-
# list of figures to draw when flush_figures is called
55-
show._to_draw = []
56-
57-
58-
def draw_if_interactive():
59-
"""
60-
Is called after every pylab drawing command
61-
"""
62-
# signal that the current active figure should be sent at the end of
63-
# execution. Also sets the _draw_called flag, signaling that there will be
64-
# something to send. At the end of the code execution, a separate call to
65-
# flush_figures() will act upon these values
66-
manager = Gcf.get_active()
67-
if manager is None:
68-
return
69-
fig = manager.canvas.figure
70-
71-
# Hack: matplotlib FigureManager objects in interacive backends (at least
72-
# in some of them) monkeypatch the figure object and add a .show() method
73-
# to it. This applies the same monkeypatch in order to support user code
74-
# that might expect `.show()` to be part of the official API of figure
75-
# objects.
76-
# For further reference:
77-
# https://github.com/ipython/ipython/issues/1612
78-
# https://github.com/matplotlib/matplotlib/issues/835
79-
80-
if not hasattr(fig, 'show'):
81-
# Queue up `fig` for display
82-
fig.show = lambda *a: display(fig, metadata=_fetch_figure_metadata(fig))
83-
84-
# If matplotlib was manually set to non-interactive mode, this function
85-
# should be a no-op (otherwise we'll generate duplicate plots, since a user
86-
# who set ioff() manually expects to make separate draw/show calls).
87-
if not matplotlib.is_interactive():
88-
return
89-
90-
# ensure current figure will be drawn, and each subsequent call
91-
# of draw_if_interactive() moves the active figure to ensure it is
92-
# drawn last
93-
try:
94-
show._to_draw.remove(fig)
95-
except ValueError:
96-
# ensure it only appears in the draw list once
97-
pass
98-
# Queue up the figure for drawing in next show() call
99-
show._to_draw.append(fig)
100-
show._draw_called = True
101-
102-
103-
def flush_figures():
104-
"""Send all figures that changed
105-
106-
This is meant to be called automatically and will call show() if, during
107-
prior code execution, there had been any calls to draw_if_interactive.
108-
109-
This function is meant to be used as a post_execute callback in IPython,
110-
so user-caused errors are handled with showtraceback() instead of being
111-
allowed to raise. If this function is not called from within IPython,
112-
then these exceptions will raise.
113-
"""
114-
if not show._draw_called:
115-
return
116-
117-
if InlineBackend.instance().close_figures:
118-
# ignore the tracking, just draw and close all figures
119-
try:
120-
return show(True)
121-
except Exception as e:
122-
# safely show traceback if in IPython, else raise
123-
ip = get_ipython()
124-
if ip is None:
125-
raise e
126-
else:
127-
ip.showtraceback()
128-
return
129-
try:
130-
# exclude any figures that were closed:
131-
active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
132-
for fig in [ fig for fig in show._to_draw if fig in active ]:
133-
try:
134-
display(fig, metadata=_fetch_figure_metadata(fig))
135-
except Exception as e:
136-
# safely show traceback if in IPython, else raise
137-
ip = get_ipython()
138-
if ip is None:
139-
raise e
140-
else:
141-
ip.showtraceback()
142-
return
143-
finally:
144-
# clear flags for next round
145-
show._to_draw = []
146-
show._draw_called = False
147-
148-
149-
# Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
150-
# figurecanvas. This is set here to a Agg canvas
151-
# See https://github.com/matplotlib/matplotlib/pull/1125
152-
FigureCanvas = FigureCanvasAgg
153-
154-
155-
def configure_inline_support(shell, backend):
156-
"""Configure an IPython shell object for matplotlib use.
157-
158-
Parameters
159-
----------
160-
shell : InteractiveShell instance
161-
backend : matplotlib backend
162-
"""
163-
# If using our svg payload backend, register the post-execution
164-
# function that will pick up the results for display. This can only be
165-
# done with access to the real shell object.
166-
167-
cfg = InlineBackend.instance(parent=shell)
168-
cfg.shell = shell
169-
if cfg not in shell.configurables:
170-
shell.configurables.append(cfg)
171-
172-
if backend == 'module://ipykernel.pylab.backend_inline':
173-
shell.events.register('post_execute', flush_figures)
174-
175-
# Save rcParams that will be overwrittern
176-
shell._saved_rcParams = {}
177-
for k in cfg.rc:
178-
shell._saved_rcParams[k] = matplotlib.rcParams[k]
179-
# load inline_rc
180-
matplotlib.rcParams.update(cfg.rc)
181-
new_backend_name = "inline"
182-
else:
183-
try:
184-
shell.events.unregister('post_execute', flush_figures)
185-
except ValueError:
186-
pass
187-
if hasattr(shell, '_saved_rcParams'):
188-
matplotlib.rcParams.update(shell._saved_rcParams)
189-
del shell._saved_rcParams
190-
new_backend_name = "other"
191-
192-
# only enable the formats once -> don't change the enabled formats (which the user may
193-
# has changed) when getting another "%matplotlib inline" call.
194-
# See https://github.com/ipython/ipykernel/issues/29
195-
cur_backend = getattr(configure_inline_support, "current_backend", "unset")
196-
if new_backend_name != cur_backend:
197-
# Setup the default figure format
198-
select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
199-
configure_inline_support.current_backend = new_backend_name
200-
201-
202-
def _enable_matplotlib_integration():
203-
"""Enable extra IPython matplotlib integration when we are loaded as the matplotlib backend."""
204-
from matplotlib import get_backend
205-
ip = get_ipython()
206-
backend = get_backend()
207-
if ip and backend == 'module://%s' % __name__:
208-
from IPython.core.pylabtools import activate_matplotlib
209-
try:
210-
activate_matplotlib(backend)
211-
configure_inline_support(ip, backend)
212-
except (ImportError, AttributeError):
213-
# bugs may cause a circular import on Python 2
214-
def configure_once(*args):
215-
activate_matplotlib(backend)
216-
configure_inline_support(ip, backend)
217-
ip.events.unregister('post_run_cell', configure_once)
218-
ip.events.register('post_run_cell', configure_once)
219-
220-
_enable_matplotlib_integration()
221-
222-
def _fetch_figure_metadata(fig):
223-
"""Get some metadata to help with displaying a figure."""
224-
# determine if a background is needed for legibility
225-
if _is_transparent(fig.get_facecolor()):
226-
# the background is transparent
227-
ticksLight = _is_light([label.get_color()
228-
for axes in fig.axes
229-
for axis in (axes.xaxis, axes.yaxis)
230-
for label in axis.get_ticklabels()])
231-
if ticksLight.size and (ticksLight == ticksLight[0]).all():
232-
# there are one or more tick labels, all with the same lightness
233-
return {'needs_background': 'dark' if ticksLight[0] else 'light'}
234-
235-
return None
236-
237-
def _is_light(color):
238-
"""Determines if a color (or each of a sequence of colors) is light (as
239-
opposed to dark). Based on ITU BT.601 luminance formula (see
240-
https://stackoverflow.com/a/596241)."""
241-
rgbaArr = colors.to_rgba_array(color)
242-
return rgbaArr[:,:3].dot((.299, .587, .114)) > .5
243-
244-
def _is_transparent(color):
245-
"""Determine transparency from alpha."""
246-
rgba = colors.to_rgba(color)
247-
return rgba[3] < .5
11+
warnings.warn(
12+
"`ipykernel.pylab.backend_inline` is deprecated, directly "
13+
"use `matplotlib_inline.backend_inline`",
14+
DeprecationWarning
15+
)

ipykernel/pylab/config.py

Lines changed: 7 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,109 +2,14 @@
22
33
This module does not import anything from matplotlib.
44
"""
5-
#-----------------------------------------------------------------------------
6-
# Copyright (C) 2011 The IPython Development Team
7-
#
8-
# Distributed under the terms of the BSD License. The full license is in
9-
# the file COPYING, distributed as part of this software.
10-
#-----------------------------------------------------------------------------
115

12-
#-----------------------------------------------------------------------------
13-
# Imports
14-
#-----------------------------------------------------------------------------
6+
import warnings
157

16-
from traitlets.config.configurable import SingletonConfigurable
17-
from traitlets import (
18-
Dict, Instance, Set, Bool, TraitError, Unicode
19-
)
20-
21-
#-----------------------------------------------------------------------------
22-
# Configurable for inline backend options
23-
#-----------------------------------------------------------------------------
24-
25-
def pil_available():
26-
"""Test if PIL/Pillow is available"""
27-
out = False
28-
try:
29-
from PIL import Image
30-
out = True
31-
except:
32-
pass
33-
return out
34-
35-
# inherit from InlineBackendConfig for deprecation purposes
36-
class InlineBackendConfig(SingletonConfigurable):
37-
pass
38-
39-
class InlineBackend(InlineBackendConfig):
40-
"""An object to store configuration of the inline backend."""
41-
42-
# The typical default figure size is too large for inline use,
43-
# so we shrink the figure size to 6x4, and tweak fonts to
44-
# make that fit.
45-
rc = Dict({'figure.figsize': (6.0,4.0),
46-
# play nicely with white background in the Qt and notebook frontend
47-
'figure.facecolor': (1,1,1,0),
48-
'figure.edgecolor': (1,1,1,0),
49-
# 12pt labels get cutoff on 6x4 logplots, so use 10pt.
50-
'font.size': 10,
51-
# 72 dpi matches SVG/qtconsole
52-
# this only affects PNG export, as SVG has no dpi setting
53-
'figure.dpi': 72,
54-
# 10pt still needs a little more room on the xlabel:
55-
'figure.subplot.bottom' : .125
56-
},
57-
help="""Subset of matplotlib rcParams that should be different for the
58-
inline backend."""
59-
).tag(config=True)
60-
61-
figure_formats = Set({'png'},
62-
help="""A set of figure formats to enable: 'png',
63-
'retina', 'jpeg', 'svg', 'pdf'.""").tag(config=True)
64-
65-
def _update_figure_formatters(self):
66-
if self.shell is not None:
67-
from IPython.core.pylabtools import select_figure_formats
68-
select_figure_formats(self.shell, self.figure_formats, **self.print_figure_kwargs)
8+
from matplotlib_inline.config import * # analysis: ignore
699

70-
def _figure_formats_changed(self, name, old, new):
71-
if 'jpg' in new or 'jpeg' in new:
72-
if not pil_available():
73-
raise TraitError("Requires PIL/Pillow for JPG figures")
74-
self._update_figure_formatters()
75-
76-
figure_format = Unicode(help="""The figure format to enable (deprecated
77-
use `figure_formats` instead)""").tag(config=True)
78-
79-
def _figure_format_changed(self, name, old, new):
80-
if new:
81-
self.figure_formats = {new}
82-
83-
print_figure_kwargs = Dict({'bbox_inches' : 'tight'},
84-
help="""Extra kwargs to be passed to fig.canvas.print_figure.
85-
86-
Logical examples include: bbox_inches, quality (for jpeg figures), etc.
87-
"""
88-
).tag(config=True)
89-
_print_figure_kwargs_changed = _update_figure_formatters
90-
91-
close_figures = Bool(True,
92-
help="""Close all figures at the end of each cell.
93-
94-
When True, ensures that each cell starts with no active figures, but it
95-
also means that one must keep track of references in order to edit or
96-
redraw figures in subsequent cells. This mode is ideal for the notebook,
97-
where residual plots from other cells might be surprising.
98-
99-
When False, one must call figure() to create new figures. This means
100-
that gcf() and getfigs() can reference figures created in other cells,
101-
and the active figure can continue to be edited with pylab/pyplot
102-
methods that reference the current active figure. This mode facilitates
103-
iterative editing of figures, and behaves most consistently with
104-
other matplotlib backends, but figure barriers between cells must
105-
be explicit.
106-
""").tag(config=True)
107-
108-
shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
109-
allow_none=True)
11010

11+
warnings.warn(
12+
"`ipykernel.pylab.config` is deprecated, directly "
13+
"use `matplotlib_inline.config`",
14+
DeprecationWarning
15+
)

ipykernel/zmqshell.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -621,15 +621,6 @@ def init_magics(self):
621621
self.register_magics(KernelMagics)
622622
self.magics_manager.register_alias('ed', 'edit')
623623

624-
def enable_matplotlib(self, gui=None):
625-
gui, backend = super(ZMQInteractiveShell, self).enable_matplotlib(gui)
626-
627-
from ipykernel.pylab.backend_inline import configure_inline_support
628-
629-
configure_inline_support(self, backend)
630-
631-
return gui, backend
632-
633624
def init_virtualenv(self):
634625
# Overridden not to do virtualenv detection, because it's probably
635626
# not appropriate in a kernel. To use a kernel in a virtualenv, install

0 commit comments

Comments
 (0)