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