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