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