##// END OF EJS Templates
Add a comment to document why the figurecanvas is needed...
Jens H. Nielsen -
Show More
@@ -1,212 +1,215 b''
1 1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6 from __future__ import print_function
7 7
8 8 # Standard library imports
9 9 import sys
10 10
11 11 # Third-party imports
12 12 import matplotlib
13 13 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
14 14 from matplotlib._pylab_helpers import Gcf
15 15
16 16 # Local imports.
17 17 from IPython.config.configurable import SingletonConfigurable
18 18 from IPython.core.display import display
19 19 from IPython.core.displaypub import publish_display_data
20 20 from IPython.core.pylabtools import print_figure, select_figure_format
21 21 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, CBool
22 22 from IPython.utils.warn import warn
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Configurable for inline backend options
26 26 #-----------------------------------------------------------------------------
27 27 # inherit from InlineBackendConfig for deprecation purposes
28 28 class InlineBackendConfig(SingletonConfigurable):
29 29 pass
30 30
31 31 class InlineBackend(InlineBackendConfig):
32 32 """An object to store configuration of the inline backend."""
33 33
34 34 def _config_changed(self, name, old, new):
35 35 # warn on change of renamed config section
36 36 if new.InlineBackendConfig != old.InlineBackendConfig:
37 37 warn("InlineBackendConfig has been renamed to InlineBackend")
38 38 super(InlineBackend, self)._config_changed(name, old, new)
39 39
40 40 # The typical default figure size is too large for inline use,
41 41 # so we shrink the figure size to 6x4, and tweak fonts to
42 42 # make that fit.
43 43 rc = Dict({'figure.figsize': (6.0,4.0),
44 44 # play nicely with white background in the Qt and notebook frontend
45 45 'figure.facecolor': 'white',
46 46 'figure.edgecolor': 'white',
47 47 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
48 48 'font.size': 10,
49 49 # 72 dpi matches SVG/qtconsole
50 50 # this only affects PNG export, as SVG has no dpi setting
51 51 'savefig.dpi': 72,
52 52 # 10pt still needs a little more room on the xlabel:
53 53 'figure.subplot.bottom' : .125
54 54 }, config=True,
55 55 help="""Subset of matplotlib rcParams that should be different for the
56 56 inline backend."""
57 57 )
58 58
59 59 figure_format = CaselessStrEnum(['svg', 'png'], default_value='png', config=True,
60 60 help="The image format for figures with the inline backend.")
61 61
62 62 def _figure_format_changed(self, name, old, new):
63 63 if self.shell is None:
64 64 return
65 65 else:
66 66 select_figure_format(self.shell, new)
67 67
68 68 close_figures = CBool(True, config=True,
69 69 help="""Close all figures at the end of each cell.
70 70
71 71 When True, ensures that each cell starts with no active figures, but it
72 72 also means that one must keep track of references in order to edit or
73 73 redraw figures in subsequent cells. This mode is ideal for the notebook,
74 74 where residual plots from other cells might be surprising.
75 75
76 76 When False, one must call figure() to create new figures. This means
77 77 that gcf() and getfigs() can reference figures created in other cells,
78 78 and the active figure can continue to be edited with pylab/pyplot
79 79 methods that reference the current active figure. This mode facilitates
80 80 iterative editing of figures, and behaves most consistently with
81 81 other matplotlib backends, but figure barriers between cells must
82 82 be explicit.
83 83 """)
84 84
85 85 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
86 86
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92 def show(close=None):
93 93 """Show all figures as SVG/PNG payloads sent to the IPython clients.
94 94
95 95 Parameters
96 96 ----------
97 97 close : bool, optional
98 98 If true, a ``plt.close('all')`` call is automatically issued after
99 99 sending all the figures. If this is set, the figures will entirely
100 100 removed from the internal list of figures.
101 101 """
102 102 if close is None:
103 103 close = InlineBackend.instance().close_figures
104 104 try:
105 105 for figure_manager in Gcf.get_all_fig_managers():
106 106 display(figure_manager.canvas.figure)
107 107 finally:
108 108 show._to_draw = []
109 109 if close:
110 110 matplotlib.pyplot.close('all')
111 111
112 112
113 113
114 114 # This flag will be reset by draw_if_interactive when called
115 115 show._draw_called = False
116 116 # list of figures to draw when flush_figures is called
117 117 show._to_draw = []
118 118
119 119
120 120 def draw_if_interactive():
121 121 """
122 122 Is called after every pylab drawing command
123 123 """
124 124 # signal that the current active figure should be sent at the end of
125 125 # execution. Also sets the _draw_called flag, signaling that there will be
126 126 # something to send. At the end of the code execution, a separate call to
127 127 # flush_figures() will act upon these values
128 128
129 129 fig = Gcf.get_active().canvas.figure
130 130
131 131 # Hack: matplotlib FigureManager objects in interacive backends (at least
132 132 # in some of them) monkeypatch the figure object and add a .show() method
133 133 # to it. This applies the same monkeypatch in order to support user code
134 134 # that might expect `.show()` to be part of the official API of figure
135 135 # objects.
136 136 # For further reference:
137 137 # https://github.com/ipython/ipython/issues/1612
138 138 # https://github.com/matplotlib/matplotlib/issues/835
139 139
140 140 if not hasattr(fig, 'show'):
141 141 # Queue up `fig` for display
142 142 fig.show = lambda *a: display(fig)
143 143
144 144 # If matplotlib was manually set to non-interactive mode, this function
145 145 # should be a no-op (otherwise we'll generate duplicate plots, since a user
146 146 # who set ioff() manually expects to make separate draw/show calls).
147 147 if not matplotlib.is_interactive():
148 148 return
149 149
150 150 # ensure current figure will be drawn, and each subsequent call
151 151 # of draw_if_interactive() moves the active figure to ensure it is
152 152 # drawn last
153 153 try:
154 154 show._to_draw.remove(fig)
155 155 except ValueError:
156 156 # ensure it only appears in the draw list once
157 157 pass
158 158 # Queue up the figure for drawing in next show() call
159 159 show._to_draw.append(fig)
160 160 show._draw_called = True
161 161
162 162
163 163 def flush_figures():
164 164 """Send all figures that changed
165 165
166 166 This is meant to be called automatically and will call show() if, during
167 167 prior code execution, there had been any calls to draw_if_interactive.
168 168
169 169 This function is meant to be used as a post_execute callback in IPython,
170 170 so user-caused errors are handled with showtraceback() instead of being
171 171 allowed to raise. If this function is not called from within IPython,
172 172 then these exceptions will raise.
173 173 """
174 174 if not show._draw_called:
175 175 return
176 176
177 177 if InlineBackend.instance().close_figures:
178 178 # ignore the tracking, just draw and close all figures
179 179 try:
180 180 return show(True)
181 181 except Exception as e:
182 182 # safely show traceback if in IPython, else raise
183 183 try:
184 184 get_ipython
185 185 except NameError:
186 186 raise e
187 187 else:
188 188 get_ipython().showtraceback()
189 189 return
190 190 try:
191 191 # exclude any figures that were closed:
192 192 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
193 193 for fig in [ fig for fig in show._to_draw if fig in active ]:
194 194 try:
195 195 display(fig)
196 196 except Exception as e:
197 197 # safely show traceback if in IPython, else raise
198 198 try:
199 199 get_ipython
200 200 except NameError:
201 201 raise e
202 202 else:
203 203 get_ipython().showtraceback()
204 204 break
205 205 finally:
206 206 # clear flags for next round
207 207 show._to_draw = []
208 208 show._draw_called = False
209 209
210 210
211 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
212 # figurecanvas. This is set here to a Agg canvas
213 # See https://github.com/matplotlib/matplotlib/pull/1125
211 214 FigureCanvas = FigureCanvasAgg
212 215
General Comments 0
You need to be logged in to leave comments. Login now