##// 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 object_info_string_level = Enum((0,1,2), default_value=0,
162 object_info_string_level = Enum((0,1,2), default_value=0,
163 config=True)
163 config=True)
164 pdb = CBool(False, config=True)
164 pdb = CBool(False, config=True)
165
165 pprint = CBool(True, config=True)
166 pprint = CBool(True, config=True)
166 profile = Str('', config=True)
167 profile = Str('', config=True)
167 prompt_in1 = Str('In [\\#]: ', config=True)
168 prompt_in1 = Str('In [\\#]: ', config=True)
@@ -212,6 +213,9 b' class InteractiveShell(Configurable, Magic):'
212 plugin_manager = Instance('IPython.core.plugin.PluginManager')
213 plugin_manager = Instance('IPython.core.plugin.PluginManager')
213 payload_manager = Instance('IPython.core.payload.PayloadManager')
214 payload_manager = Instance('IPython.core.payload.PayloadManager')
214
215
216 # Private interface
217 _post_execute = set()
218
215 def __init__(self, config=None, ipython_dir=None,
219 def __init__(self, config=None, ipython_dir=None,
216 user_ns=None, user_global_ns=None,
220 user_ns=None, user_global_ns=None,
217 custom_exceptions=((), None)):
221 custom_exceptions=((), None)):
@@ -570,6 +574,13 b' class InteractiveShell(Configurable, Magic):'
570
574
571 setattr(self.hooks,name, dp)
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 # Things related to the "main" module
585 # Things related to the "main" module
575 #-------------------------------------------------------------------------
586 #-------------------------------------------------------------------------
@@ -2268,6 +2279,21 b' class InteractiveShell(Configurable, Magic):'
2268 outflag = 0
2279 outflag = 0
2269 if softspace(sys.stdout, 0):
2280 if softspace(sys.stdout, 0):
2270 print
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 # Flush out code object which has been run (and source)
2297 # Flush out code object which has been run (and source)
2272 self.code_to_run = None
2298 self.code_to_run = None
2273 return outflag
2299 return outflag
@@ -114,10 +114,7 b' def main():'
114 if args.pure:
114 if args.pure:
115 kernel_manager.start_kernel(ipython=False)
115 kernel_manager.start_kernel(ipython=False)
116 elif args.pylab:
116 elif args.pylab:
117 if args.rich:
117 kernel_manager.start_kernel(pylab=args.pylab)
118 kernel_manager.start_kernel(pylab='payload-svg')
119 else:
120 kernel_manager.start_kernel(pylab=args.pylab)
121 else:
118 else:
122 kernel_manager.start_kernel()
119 kernel_manager.start_kernel()
123 kernel_manager.start_channels()
120 kernel_manager.start_channels()
@@ -127,7 +124,7 b' def main():'
127 if args.pure:
124 if args.pure:
128 kind = 'rich' if args.rich else 'plain'
125 kind = 'rich' if args.rich else 'plain'
129 widget = FrontendWidget(kind=kind, paging=args.paging)
126 widget = FrontendWidget(kind=kind, paging=args.paging)
130 elif args.rich:
127 elif args.rich or args.pylab:
131 widget = RichIPythonWidget(paging=args.paging)
128 widget = RichIPythonWidget(paging=args.paging)
132 else:
129 else:
133 widget = IPythonWidget(paging=args.paging)
130 widget = IPythonWidget(paging=args.paging)
@@ -21,6 +21,15 b' Authors'
21
21
22 from IPython.utils.decorators import flag_calls
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 # Main classes and functions
34 # Main classes and functions
26 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
@@ -42,24 +51,15 b' def find_gui_and_backend(gui=None):'
42
51
43 import matplotlib
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 if gui:
54 if gui:
56 # select backend based on requested gui
55 # select backend based on requested gui
57 backend = g2b[gui]
56 backend = backends[gui]
58 else:
57 else:
59 backend = matplotlib.rcParams['backend']
58 backend = matplotlib.rcParams['backend']
60 # In this case, we need to find what the appropriate gui selection call
59 # In this case, we need to find what the appropriate gui selection call
61 # should be for IPython, so we can activate inputhook accordingly
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 gui = b2g.get(backend, None)
63 gui = b2g.get(backend, None)
64 return gui, backend
64 return gui, backend
65
65
@@ -90,7 +90,8 b' def activate_matplotlib(backend):'
90 # For this, we wrap it into a decorator which adds a 'called' flag.
90 # For this, we wrap it into a decorator which adds a 'called' flag.
91 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
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 """Import the standard pylab symbols into user_ns."""
95 """Import the standard pylab symbols into user_ns."""
95
96
96 # Import numpy as np/pyplot as plt are conventions we're trying to
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 "plt = pyplot\n"
104 "plt = pyplot\n"
104 ) in user_ns
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 if import_all:
118 if import_all:
107 exec("from matplotlib.pylab import *\n"
119 exec("from matplotlib.pylab import *\n"
108 "from numpy import *\n") in user_ns
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 gui, backend = find_gui_and_backend(gui)
144 gui, backend = find_gui_and_backend(gui)
133 activate_matplotlib(backend)
145 activate_matplotlib(backend)
134 import_pylab(user_ns)
146 import_pylab(user_ns, backend)
135
147
136 print """
148 print """
137 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
149 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
@@ -192,11 +192,12 b' class Kernel(Configurable):'
192 # FIXME: runlines calls the exception handler itself.
192 # FIXME: runlines calls the exception handler itself.
193 shell._reply_content = None
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 # Experimental: cell mode! Test more before turning into
198 # Experimental: cell mode! Test more before turning into
196 # default and removing the hacks around runlines.
199 # default and removing the hacks around runlines.
197 shell.run_cell(code)
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 except:
201 except:
201 status = u'error'
202 status = u'error'
202 # FIXME: this code right now isn't being used yet by default,
203 # FIXME: this code right now isn't being used yet by default,
@@ -210,10 +211,6 b' class Kernel(Configurable):'
210 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
211 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
211 else:
212 else:
212 status = u'ok'
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 reply_content[u'status'] = status
215 reply_content[u'status'] = status
219 # Compute the execution counter so clients can display prompts
216 # Compute the execution counter so clients can display prompts
@@ -236,7 +233,15 b' class Kernel(Configurable):'
236 # expressions
233 # expressions
237 reply_content[u'user_variables'] = {}
234 reply_content[u'user_variables'] = {}
238 reply_content[u'user_expressions'] = {}
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 # Send the reply.
245 # Send the reply.
241 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
246 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
242 io.raw_print(reply_msg)
247 io.raw_print(reply_msg)
@@ -571,7 +576,8 b" given, the GUI backend is matplotlib's, otherwise use one of: \\"
571 kernel = make_kernel(namespace, kernel_class, OutStream)
576 kernel = make_kernel(namespace, kernel_class, OutStream)
572
577
573 if namespace.pylab:
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 start_kernel(namespace, kernel)
582 start_kernel(namespace, kernel)
577
583
@@ -3,11 +3,13 b''
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 from __future__ import print_function
6
7
7 # Standard library imports
8 # Standard library imports
8 from cStringIO import StringIO
9 from cStringIO import StringIO
9
10
10 # System library imports.
11 # System library imports.
12 import matplotlib
11 from matplotlib.backends.backend_svg import new_figure_manager
13 from matplotlib.backends.backend_svg import new_figure_manager
12 from matplotlib._pylab_helpers import Gcf
14 from matplotlib._pylab_helpers import Gcf
13
15
@@ -18,17 +20,68 b' from backend_payload import add_plot_payload'
18 # Functions
20 # Functions
19 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
20
22
21 def show():
23 def show(close=True):
22 """ Deliver a SVG payload.
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 for figure_manager in Gcf.get_all_fig_managers():
32 for figure_manager in Gcf.get_all_fig_managers():
25 # Make the background transparent.
33 send_svg_canvas(figure_manager.canvas)
26 # figure_manager.canvas.figure.patch.set_alpha(0.0)
34 if close:
27 # Set the background to white instead so it looks good on black.
35 matplotlib.pyplot.close('all')
28 figure_manager.canvas.figure.set_facecolor('white')
36
29 figure_manager.canvas.figure.set_edgecolor('white')
37 # This flag will be reset by draw_if_interactive when called
30 data = svg_from_canvas(figure_manager.canvas)
38 show._draw_called = False
31 add_plot_payload('svg', data)
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 def svg_from_canvas(canvas):
87 def svg_from_canvas(canvas):
@@ -37,3 +90,23 b' def svg_from_canvas(canvas):'
37 string_io = StringIO()
90 string_io = StringIO()
38 canvas.print_svg(string_io)
91 canvas.print_svg(string_io)
39 return string_io.getvalue()
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