##// END OF EJS Templates
Add support for simultaneous interactive and inline matplotlib plots....
Fernando Perez -
Show More
@@ -162,6 +162,7 b' class InteractiveShell(Configurable, Magic):'
162 162 object_info_string_level = Enum((0,1,2), default_value=0,
163 163 config=True)
164 164 pdb = CBool(False, config=True)
165
165 166 pprint = CBool(True, config=True)
166 167 profile = Str('', config=True)
167 168 prompt_in1 = Str('In [\\#]: ', config=True)
@@ -212,6 +213,9 b' class InteractiveShell(Configurable, Magic):'
212 213 plugin_manager = Instance('IPython.core.plugin.PluginManager')
213 214 payload_manager = Instance('IPython.core.payload.PayloadManager')
214 215
216 # Private interface
217 _post_execute = set()
218
215 219 def __init__(self, config=None, ipython_dir=None,
216 220 user_ns=None, user_global_ns=None,
217 221 custom_exceptions=((), None)):
@@ -570,6 +574,13 b' class InteractiveShell(Configurable, Magic):'
570 574
571 575 setattr(self.hooks,name, dp)
572 576
577 def register_post_execute(self, func):
578 """Register a function for calling after code execution.
579 """
580 if not callable(func):
581 raise ValueError('argument %s must be callable' % func)
582 self._post_execute.add(func)
583
573 584 #-------------------------------------------------------------------------
574 585 # Things related to the "main" module
575 586 #-------------------------------------------------------------------------
@@ -2268,6 +2279,21 b' class InteractiveShell(Configurable, Magic):'
2268 2279 outflag = 0
2269 2280 if softspace(sys.stdout, 0):
2270 2281 print
2282
2283 # Execute any registered post-execution functions. Here, any errors
2284 # are reported only minimally and just on the terminal, because the
2285 # main exception channel may be occupied with a user traceback.
2286 # FIXME: we need to think this mechanism a little more carefully.
2287 for func in self._post_execute:
2288 try:
2289 func()
2290 except:
2291 head = '[ ERROR ] Evaluating post_execute function: %s' % func
2292 print >> io.Term.cout, head
2293 print >> io.Term.cout, self._simple_error()
2294 print >> io.Term.cout, 'Removing from post_execute'
2295 self._post_execute.remove(func)
2296
2271 2297 # Flush out code object which has been run (and source)
2272 2298 self.code_to_run = None
2273 2299 return outflag
@@ -114,10 +114,7 b' def main():'
114 114 if args.pure:
115 115 kernel_manager.start_kernel(ipython=False)
116 116 elif args.pylab:
117 if args.rich:
118 kernel_manager.start_kernel(pylab='payload-svg')
119 else:
120 kernel_manager.start_kernel(pylab=args.pylab)
117 kernel_manager.start_kernel(pylab=args.pylab)
121 118 else:
122 119 kernel_manager.start_kernel()
123 120 kernel_manager.start_channels()
@@ -127,7 +124,7 b' def main():'
127 124 if args.pure:
128 125 kind = 'rich' if args.rich else 'plain'
129 126 widget = FrontendWidget(kind=kind, paging=args.paging)
130 elif args.rich:
127 elif args.rich or args.pylab:
131 128 widget = RichIPythonWidget(paging=args.paging)
132 129 else:
133 130 widget = IPythonWidget(paging=args.paging)
@@ -21,6 +21,15 b' Authors'
21 21
22 22 from IPython.utils.decorators import flag_calls
23 23
24 # If user specifies a GUI, that dictates the backend, otherwise we read the
25 # user's mpl default from the mpl rc structure
26 backends = {'tk': 'TkAgg',
27 'gtk': 'GTKAgg',
28 'wx': 'WXAgg',
29 'qt': 'Qt4Agg', # qt3 not supported
30 'qt4': 'Qt4Agg',
31 'payload-svg' : 'module://IPython.zmq.pylab.backend_payload_svg'}
32
24 33 #-----------------------------------------------------------------------------
25 34 # Main classes and functions
26 35 #-----------------------------------------------------------------------------
@@ -42,24 +51,15 b' def find_gui_and_backend(gui=None):'
42 51
43 52 import matplotlib
44 53
45 # If user specifies a GUI, that dictates the backend, otherwise we read the
46 # user's mpl default from the mpl rc structure
47 g2b = {'tk': 'TkAgg',
48 'gtk': 'GTKAgg',
49 'wx': 'WXAgg',
50 'qt': 'Qt4Agg', # qt3 not supported
51 'qt4': 'Qt4Agg',
52 'payload-svg' : \
53 'module://IPython.zmq.pylab.backend_payload_svg'}
54
55 54 if gui:
56 55 # select backend based on requested gui
57 backend = g2b[gui]
56 backend = backends[gui]
58 57 else:
59 58 backend = matplotlib.rcParams['backend']
60 59 # In this case, we need to find what the appropriate gui selection call
61 60 # should be for IPython, so we can activate inputhook accordingly
62 b2g = dict(zip(g2b.values(),g2b.keys()))
61 g2b = backends # maps gui names to mpl backend names
62 b2g = dict(zip(g2b.values(), g2b.keys())) # reverse dict
63 63 gui = b2g.get(backend, None)
64 64 return gui, backend
65 65
@@ -90,7 +90,8 b' def activate_matplotlib(backend):'
90 90 # For this, we wrap it into a decorator which adds a 'called' flag.
91 91 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
92 92
93 def import_pylab(user_ns, import_all=True):
93
94 def import_pylab(user_ns, backend, import_all=True, shell=None):
94 95 """Import the standard pylab symbols into user_ns."""
95 96
96 97 # Import numpy as np/pyplot as plt are conventions we're trying to
@@ -103,6 +104,17 b' def import_pylab(user_ns, import_all=True):'
103 104 "plt = pyplot\n"
104 105 ) in user_ns
105 106
107 if shell is not None:
108 # If using our svg payload backend, register the post-execution
109 # function that will pick up the results for display. This can only be
110 # done with access to the real shell object.
111 if backend == backends['payload-svg']:
112 from IPython.zmq.pylab.backend_payload_svg import flush_svg
113 shell.register_post_execute(flush_svg)
114 else:
115 from IPython.zmq.pylab.backend_payload_svg import paste
116 user_ns['paste'] = paste
117
106 118 if import_all:
107 119 exec("from matplotlib.pylab import *\n"
108 120 "from numpy import *\n") in user_ns
@@ -131,7 +143,7 b' def pylab_activate(user_ns, gui=None, import_all=True):'
131 143 """
132 144 gui, backend = find_gui_and_backend(gui)
133 145 activate_matplotlib(backend)
134 import_pylab(user_ns)
146 import_pylab(user_ns, backend)
135 147
136 148 print """
137 149 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
@@ -192,11 +192,12 b' class Kernel(Configurable):'
192 192 # FIXME: runlines calls the exception handler itself.
193 193 shell._reply_content = None
194 194
195 # For now leave this here until we're sure we can stop using it
196 #shell.runlines(code)
197
195 198 # Experimental: cell mode! Test more before turning into
196 199 # default and removing the hacks around runlines.
197 200 shell.run_cell(code)
198 # For now leave this here until we're sure we can stop using it
199 #shell.runlines(code)
200 201 except:
201 202 status = u'error'
202 203 # FIXME: this code right now isn't being used yet by default,
@@ -210,10 +211,6 b' class Kernel(Configurable):'
210 211 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
211 212 else:
212 213 status = u'ok'
213 reply_content[u'payload'] = shell.payload_manager.read_payload()
214 # Be agressive about clearing the payload because we don't want
215 # it to sit in memory until the next execute_request comes in.
216 shell.payload_manager.clear_payload()
217 214
218 215 reply_content[u'status'] = status
219 216 # Compute the execution counter so clients can display prompts
@@ -236,7 +233,15 b' class Kernel(Configurable):'
236 233 # expressions
237 234 reply_content[u'user_variables'] = {}
238 235 reply_content[u'user_expressions'] = {}
239
236
237 # Payloads should be retrieved regardless of outcome, so we can both
238 # recover partial output (that could have been generated early in a
239 # block, before an error) and clear the payload system always.
240 reply_content[u'payload'] = shell.payload_manager.read_payload()
241 # Be agressive about clearing the payload because we don't want
242 # it to sit in memory until the next execute_request comes in.
243 shell.payload_manager.clear_payload()
244
240 245 # Send the reply.
241 246 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
242 247 io.raw_print(reply_msg)
@@ -571,7 +576,8 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
571 576 kernel = make_kernel(namespace, kernel_class, OutStream)
572 577
573 578 if namespace.pylab:
574 pylabtools.import_pylab(kernel.shell.user_ns)
579 pylabtools.import_pylab(kernel.shell.user_ns, backend,
580 shell=kernel.shell)
575 581
576 582 start_kernel(namespace, kernel)
577 583
@@ -3,11 +3,13 b''
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 from __future__ import print_function
6 7
7 8 # Standard library imports
8 9 from cStringIO import StringIO
9 10
10 11 # System library imports.
12 import matplotlib
11 13 from matplotlib.backends.backend_svg import new_figure_manager
12 14 from matplotlib._pylab_helpers import Gcf
13 15
@@ -18,17 +20,68 b' from backend_payload import add_plot_payload'
18 20 # Functions
19 21 #-----------------------------------------------------------------------------
20 22
21 def show():
22 """ Deliver a SVG payload.
23 def show(close=True):
24 """Show all figures as SVG payloads sent to the IPython clients.
25
26 Parameters
27 ----------
28 close : bool, optional
29 If true, a ``plt.close('all')`` call is automatically issued after
30 sending all the SVG figures.
23 31 """
24 32 for figure_manager in Gcf.get_all_fig_managers():
25 # Make the background transparent.
26 # figure_manager.canvas.figure.patch.set_alpha(0.0)
27 # Set the background to white instead so it looks good on black.
28 figure_manager.canvas.figure.set_facecolor('white')
29 figure_manager.canvas.figure.set_edgecolor('white')
30 data = svg_from_canvas(figure_manager.canvas)
31 add_plot_payload('svg', data)
33 send_svg_canvas(figure_manager.canvas)
34 if close:
35 matplotlib.pyplot.close('all')
36
37 # This flag will be reset by draw_if_interactive when called
38 show._draw_called = False
39
40
41 def paste(*figs):
42 """Paste figures into the console workspace.
43
44 If no arguments are given, all available figures are pasted. If the
45 argument list contains references to invalid figures, a warning is printed
46 but the function continues pasting further figures.
47
48 Parameters
49 ----------
50 figs : tuple
51 A tuple that can contain any mixture of integers and figure objects.
52 """
53 if not figs:
54 show(close=False)
55 else:
56 fig_managers = Gcf.get_all_fig_managers()
57 fig_index = dict( [(fm.canvas.figure, fm.canvas) for fm in fig_managers]
58 + [ (fm.canvas.figure.number, fm.canvas) for fm in fig_managers] )
59
60 for fig in figs:
61 canvas = fig_index.get(fig)
62 if canvas is None:
63 print('Warning: figure %s not available.' % fig)
64 else:
65 send_svg_canvas(canvas)
66
67
68 def send_svg_canvas(canvas):
69 """Draw the current canvas and send it as an SVG payload.
70 """
71 # Make the background transparent.
72 # figure_manager.canvas.figure.patch.set_alpha(0.0)
73
74 # Set the background to white instead so it looks good on black. We store
75 # the current values to restore them at the end.
76 fc = canvas.figure.get_facecolor()
77 ec = canvas.figure.get_edgecolor()
78 canvas.figure.set_facecolor('white')
79 canvas.figure.set_edgecolor('white')
80 try:
81 add_plot_payload('svg', svg_from_canvas(canvas))
82 finally:
83 canvas.figure.set_facecolor(fc)
84 canvas.figure.set_edgecolor(ec)
32 85
33 86
34 87 def svg_from_canvas(canvas):
@@ -37,3 +90,23 b' def svg_from_canvas(canvas):'
37 90 string_io = StringIO()
38 91 canvas.print_svg(string_io)
39 92 return string_io.getvalue()
93
94
95 def draw_if_interactive():
96 """
97 Is called after every pylab drawing command
98 """
99 # We simply flag we were called and otherwise do nothing. At the end of
100 # the code execution, a separate call to show_close() will act upon this.
101 show._draw_called = True
102
103
104 def flush_svg():
105 """Call show, close all open figures, sending all SVG images.
106
107 This is meant to be called automatically and will call show() if, during
108 prior code execution, there had been any calls to draw_if_interactive.
109 """
110 if show._draw_called:
111 show(close=True)
112 show._draw_called = False
General Comments 0
You need to be logged in to leave comments. Login now