backend_inline.py
151 lines
| 5.4 KiB
| text/x-python
|
PythonLexer
MinRK
|
r13167 | """A matplotlib backend for publishing figures via display_data""" | ||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2011 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r2890 | #----------------------------------------------------------------------------- | ||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r2987 | from __future__ import print_function | ||
Fernando Perez
|
r2890 | |||
Fernando Perez
|
r3731 | # Third-party imports | ||
Fernando Perez
|
r2987 | import matplotlib | ||
MinRK
|
r13185 | from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg # analysis: ignore | ||
epatters
|
r2756 | from matplotlib._pylab_helpers import Gcf | ||
MinRK
|
r13167 | # Local imports | ||
from IPython.core.getipython import get_ipython | ||||
MinRK
|
r8157 | from IPython.core.display import display | ||
MinRK
|
r3973 | |||
MinRK
|
r13167 | from .config import InlineBackend | ||
epatters
|
r2756 | |||
Fernando Perez
|
r2890 | #----------------------------------------------------------------------------- | ||
# Functions | ||||
#----------------------------------------------------------------------------- | ||||
epatters
|
r2756 | |||
MinRK
|
r5087 | def show(close=None): | ||
"""Show all figures as SVG/PNG payloads sent to the IPython clients. | ||||
Fernando Perez
|
r2987 | |||
Parameters | ||||
---------- | ||||
close : bool, optional | ||||
If true, a ``plt.close('all')`` call is automatically issued after | ||||
MinRK
|
r5086 | sending all the figures. If this is set, the figures will entirely | ||
Brian Granger
|
r3280 | removed from the internal list of figures. | ||
epatters
|
r2756 | """ | ||
MinRK
|
r5087 | if close is None: | ||
MinRK
|
r5227 | close = InlineBackend.instance().close_figures | ||
MinRK
|
r5346 | try: | ||
for figure_manager in Gcf.get_all_fig_managers(): | ||||
MinRK
|
r8157 | display(figure_manager.canvas.figure) | ||
MinRK
|
r5346 | finally: | ||
show._to_draw = [] | ||||
if close: | ||||
matplotlib.pyplot.close('all') | ||||
MinRK
|
r5087 | |||
Fernando Perez
|
r2987 | |||
# This flag will be reset by draw_if_interactive when called | ||||
show._draw_called = False | ||||
MinRK
|
r5086 | # list of figures to draw when flush_figures is called | ||
show._to_draw = [] | ||||
Fernando Perez
|
r2987 | |||
def draw_if_interactive(): | ||||
""" | ||||
Is called after every pylab drawing command | ||||
""" | ||||
Fernando Perez
|
r6517 | # signal that the current active figure should be sent at the end of | ||
# execution. Also sets the _draw_called flag, signaling that there will be | ||||
# something to send. At the end of the code execution, a separate call to | ||||
# flush_figures() will act upon these values | ||||
MinRK
|
r11344 | manager = Gcf.get_active() | ||
if manager is None: | ||||
return | ||||
fig = manager.canvas.figure | ||||
Fernando Perez
|
r6517 | |||
# Hack: matplotlib FigureManager objects in interacive backends (at least | ||||
# in some of them) monkeypatch the figure object and add a .show() method | ||||
# to it. This applies the same monkeypatch in order to support user code | ||||
# that might expect `.show()` to be part of the official API of figure | ||||
# objects. | ||||
# For further reference: | ||||
# https://github.com/ipython/ipython/issues/1612 | ||||
# https://github.com/matplotlib/matplotlib/issues/835 | ||||
MinRK
|
r5086 | |||
Fernando Perez
|
r6517 | if not hasattr(fig, 'show'): | ||
# Queue up `fig` for display | ||||
MinRK
|
r8157 | fig.show = lambda *a: display(fig) | ||
Fernando Perez
|
r6517 | |||
# If matplotlib was manually set to non-interactive mode, this function | ||||
# should be a no-op (otherwise we'll generate duplicate plots, since a user | ||||
# who set ioff() manually expects to make separate draw/show calls). | ||||
if not matplotlib.is_interactive(): | ||||
return | ||||
MinRK
|
r5086 | # ensure current figure will be drawn, and each subsequent call | ||
# of draw_if_interactive() moves the active figure to ensure it is | ||||
# drawn last | ||||
try: | ||||
show._to_draw.remove(fig) | ||||
except ValueError: | ||||
# ensure it only appears in the draw list once | ||||
pass | ||||
Fernando Perez
|
r6517 | # Queue up the figure for drawing in next show() call | ||
MinRK
|
r5086 | show._to_draw.append(fig) | ||
Fernando Perez
|
r2987 | show._draw_called = True | ||
Fernando Perez
|
r6517 | |||
MinRK
|
r3973 | def flush_figures(): | ||
MinRK
|
r5086 | """Send all figures that changed | ||
Fernando Perez
|
r2987 | |||
This is meant to be called automatically and will call show() if, during | ||||
prior code execution, there had been any calls to draw_if_interactive. | ||||
MinRK
|
r5735 | |||
This function is meant to be used as a post_execute callback in IPython, | ||||
so user-caused errors are handled with showtraceback() instead of being | ||||
allowed to raise. If this function is not called from within IPython, | ||||
then these exceptions will raise. | ||||
Fernando Perez
|
r2987 | """ | ||
MinRK
|
r5086 | if not show._draw_called: | ||
return | ||||
MinRK
|
r5087 | |||
MinRK
|
r5227 | if InlineBackend.instance().close_figures: | ||
MinRK
|
r5087 | # ignore the tracking, just draw and close all figures | ||
MinRK
|
r5735 | try: | ||
return show(True) | ||||
except Exception as e: | ||||
# safely show traceback if in IPython, else raise | ||||
MinRK
|
r13168 | ip = get_ipython() | ||
if ip is None: | ||||
MinRK
|
r5735 | raise e | ||
MinRK
|
r5736 | else: | ||
MinRK
|
r13168 | ip.showtraceback() | ||
MinRK
|
r5736 | return | ||
MinRK
|
r5346 | try: | ||
# exclude any figures that were closed: | ||||
active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()]) | ||||
for fig in [ fig for fig in show._to_draw if fig in active ]: | ||||
MinRK
|
r5735 | try: | ||
MinRK
|
r8157 | display(fig) | ||
MinRK
|
r5735 | except Exception as e: | ||
# safely show traceback if in IPython, else raise | ||||
MinRK
|
r13168 | ip = get_ipython() | ||
if ip is None: | ||||
MinRK
|
r5735 | raise e | ||
MinRK
|
r5736 | else: | ||
MinRK
|
r13168 | ip.showtraceback() | ||
return | ||||
MinRK
|
r5346 | finally: | ||
# clear flags for next round | ||||
show._to_draw = [] | ||||
show._draw_called = False | ||||
Brian Granger
|
r3280 | |||
Jens H. Nielsen
|
r8388 | |||
Jens H. Nielsen
|
r8389 | # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default | ||
# figurecanvas. This is set here to a Agg canvas | ||||
# See https://github.com/matplotlib/matplotlib/pull/1125 | ||||
Jens H. Nielsen
|
r8388 | FigureCanvas = FigureCanvasAgg | ||