##// END OF EJS Templates
Adding clear_output to kernel and HTML notebook.
Brian Granger -
Show More
@@ -0,0 +1,56 b''
1 {
2 "worksheets": [
3 {
4 "cells": [
5 {
6 "source": "A demonstration of the ability to clear the output of a cell during execution.",
7 "cell_type": "markdown"
8 },
9 {
10 "cell_type": "code",
11 "language": "python",
12 "outputs": [],
13 "collapsed": true,
14 "prompt_number": 8,
15 "input": "from IPython.core.display import clear_output, display"
16 },
17 {
18 "cell_type": "code",
19 "language": "python",
20 "outputs": [],
21 "collapsed": false,
22 "prompt_number": 4,
23 "input": "import time"
24 },
25 {
26 "source": "First we show how this works with ``display``:",
27 "cell_type": "markdown"
28 },
29 {
30 "cell_type": "code",
31 "language": "python",
32 "outputs": [],
33 "collapsed": false,
34 "prompt_number": 17,
35 "input": "for i in range(10):\n display(\"Time step: %i\" % i)\n time.sleep(0.5)\n clear_output()"
36 },
37 {
38 "source": "Next, we show that ``clear_output`` can also be used to create a primitive form of animation using\nmatplotlib:",
39 "cell_type": "markdown"
40 },
41 {
42 "cell_type": "code",
43 "language": "python",
44 "outputs": [],
45 "collapsed": false,
46 "prompt_number": 20,
47 "input": "for i in range(10):\n figure()\n plot(rand(100))\n show()\n time.sleep(0.1)\n clear_output()"
48 }
49 ]
50 }
51 ],
52 "metadata": {
53 "name": "clear_output"
54 },
55 "nbformat": 2
56 } No newline at end of file
@@ -1,383 +1,389 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2010 The IPython Development Team
10 # Copyright (C) 2008-2010 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from .displaypub import (
20 from .displaypub import (
21 publish_pretty, publish_html,
21 publish_pretty, publish_html,
22 publish_latex, publish_svg,
22 publish_latex, publish_svg,
23 publish_png, publish_json,
23 publish_png, publish_json,
24 publish_javascript, publish_jpeg
24 publish_javascript, publish_jpeg
25 )
25 )
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Main functions
28 # Main functions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def display(*objs, **kwargs):
31 def display(*objs, **kwargs):
32 """Display a Python object in all frontends.
32 """Display a Python object in all frontends.
33
33
34 By default all representations will be computed and sent to the frontends.
34 By default all representations will be computed and sent to the frontends.
35 Frontends can decide which representation is used and how.
35 Frontends can decide which representation is used and how.
36
36
37 Parameters
37 Parameters
38 ----------
38 ----------
39 objs : tuple of objects
39 objs : tuple of objects
40 The Python objects to display.
40 The Python objects to display.
41 include : list or tuple, optional
41 include : list or tuple, optional
42 A list of format type strings (MIME types) to include in the
42 A list of format type strings (MIME types) to include in the
43 format data dict. If this is set *only* the format types included
43 format data dict. If this is set *only* the format types included
44 in this list will be computed.
44 in this list will be computed.
45 exclude : list or tuple, optional
45 exclude : list or tuple, optional
46 A list of format type string (MIME types) to exclue in the format
46 A list of format type string (MIME types) to exclue in the format
47 data dict. If this is set all format types will be computed,
47 data dict. If this is set all format types will be computed,
48 except for those included in this argument.
48 except for those included in this argument.
49 """
49 """
50 include = kwargs.get('include')
50 include = kwargs.get('include')
51 exclude = kwargs.get('exclude')
51 exclude = kwargs.get('exclude')
52
52
53 from IPython.core.interactiveshell import InteractiveShell
53 from IPython.core.interactiveshell import InteractiveShell
54 inst = InteractiveShell.instance()
54 inst = InteractiveShell.instance()
55 format = inst.display_formatter.format
55 format = inst.display_formatter.format
56 publish = inst.display_pub.publish
56 publish = inst.display_pub.publish
57
57
58 for obj in objs:
58 for obj in objs:
59 format_dict = format(obj, include=include, exclude=exclude)
59 format_dict = format(obj, include=include, exclude=exclude)
60 publish('IPython.core.display.display', format_dict)
60 publish('IPython.core.display.display', format_dict)
61
61
62
62
63 def display_pretty(*objs, **kwargs):
63 def display_pretty(*objs, **kwargs):
64 """Display the pretty (default) representation of an object.
64 """Display the pretty (default) representation of an object.
65
65
66 Parameters
66 Parameters
67 ----------
67 ----------
68 objs : tuple of objects
68 objs : tuple of objects
69 The Python objects to display, or if raw=True raw text data to
69 The Python objects to display, or if raw=True raw text data to
70 display.
70 display.
71 raw : bool
71 raw : bool
72 Are the data objects raw data or Python objects that need to be
72 Are the data objects raw data or Python objects that need to be
73 formatted before display? [default: False]
73 formatted before display? [default: False]
74 """
74 """
75 raw = kwargs.pop('raw',False)
75 raw = kwargs.pop('raw',False)
76 if raw:
76 if raw:
77 for obj in objs:
77 for obj in objs:
78 publish_pretty(obj)
78 publish_pretty(obj)
79 else:
79 else:
80 display(*objs, include=['text/plain'])
80 display(*objs, include=['text/plain'])
81
81
82
82
83 def display_html(*objs, **kwargs):
83 def display_html(*objs, **kwargs):
84 """Display the HTML representation of an object.
84 """Display the HTML representation of an object.
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 objs : tuple of objects
88 objs : tuple of objects
89 The Python objects to display, or if raw=True raw HTML data to
89 The Python objects to display, or if raw=True raw HTML data to
90 display.
90 display.
91 raw : bool
91 raw : bool
92 Are the data objects raw data or Python objects that need to be
92 Are the data objects raw data or Python objects that need to be
93 formatted before display? [default: False]
93 formatted before display? [default: False]
94 """
94 """
95 raw = kwargs.pop('raw',False)
95 raw = kwargs.pop('raw',False)
96 if raw:
96 if raw:
97 for obj in objs:
97 for obj in objs:
98 publish_html(obj)
98 publish_html(obj)
99 else:
99 else:
100 display(*objs, include=['text/plain','text/html'])
100 display(*objs, include=['text/plain','text/html'])
101
101
102
102
103 def display_svg(*objs, **kwargs):
103 def display_svg(*objs, **kwargs):
104 """Display the SVG representation of an object.
104 """Display the SVG representation of an object.
105
105
106 Parameters
106 Parameters
107 ----------
107 ----------
108 objs : tuple of objects
108 objs : tuple of objects
109 The Python objects to display, or if raw=True raw svg data to
109 The Python objects to display, or if raw=True raw svg data to
110 display.
110 display.
111 raw : bool
111 raw : bool
112 Are the data objects raw data or Python objects that need to be
112 Are the data objects raw data or Python objects that need to be
113 formatted before display? [default: False]
113 formatted before display? [default: False]
114 """
114 """
115 raw = kwargs.pop('raw',False)
115 raw = kwargs.pop('raw',False)
116 if raw:
116 if raw:
117 for obj in objs:
117 for obj in objs:
118 publish_svg(obj)
118 publish_svg(obj)
119 else:
119 else:
120 display(*objs, include=['text/plain','image/svg+xml'])
120 display(*objs, include=['text/plain','image/svg+xml'])
121
121
122
122
123 def display_png(*objs, **kwargs):
123 def display_png(*objs, **kwargs):
124 """Display the PNG representation of an object.
124 """Display the PNG representation of an object.
125
125
126 Parameters
126 Parameters
127 ----------
127 ----------
128 objs : tuple of objects
128 objs : tuple of objects
129 The Python objects to display, or if raw=True raw png data to
129 The Python objects to display, or if raw=True raw png data to
130 display.
130 display.
131 raw : bool
131 raw : bool
132 Are the data objects raw data or Python objects that need to be
132 Are the data objects raw data or Python objects that need to be
133 formatted before display? [default: False]
133 formatted before display? [default: False]
134 """
134 """
135 raw = kwargs.pop('raw',False)
135 raw = kwargs.pop('raw',False)
136 if raw:
136 if raw:
137 for obj in objs:
137 for obj in objs:
138 publish_png(obj)
138 publish_png(obj)
139 else:
139 else:
140 display(*objs, include=['text/plain','image/png'])
140 display(*objs, include=['text/plain','image/png'])
141
141
142
142
143 def display_jpeg(*objs, **kwargs):
143 def display_jpeg(*objs, **kwargs):
144 """Display the JPEG representation of an object.
144 """Display the JPEG representation of an object.
145
145
146 Parameters
146 Parameters
147 ----------
147 ----------
148 objs : tuple of objects
148 objs : tuple of objects
149 The Python objects to display, or if raw=True raw JPEG data to
149 The Python objects to display, or if raw=True raw JPEG data to
150 display.
150 display.
151 raw : bool
151 raw : bool
152 Are the data objects raw data or Python objects that need to be
152 Are the data objects raw data or Python objects that need to be
153 formatted before display? [default: False]
153 formatted before display? [default: False]
154 """
154 """
155 raw = kwargs.pop('raw',False)
155 raw = kwargs.pop('raw',False)
156 if raw:
156 if raw:
157 for obj in objs:
157 for obj in objs:
158 publish_jpeg(obj)
158 publish_jpeg(obj)
159 else:
159 else:
160 display(*objs, include=['text/plain','image/jpeg'])
160 display(*objs, include=['text/plain','image/jpeg'])
161
161
162
162
163 def display_latex(*objs, **kwargs):
163 def display_latex(*objs, **kwargs):
164 """Display the LaTeX representation of an object.
164 """Display the LaTeX representation of an object.
165
165
166 Parameters
166 Parameters
167 ----------
167 ----------
168 objs : tuple of objects
168 objs : tuple of objects
169 The Python objects to display, or if raw=True raw latex data to
169 The Python objects to display, or if raw=True raw latex data to
170 display.
170 display.
171 raw : bool
171 raw : bool
172 Are the data objects raw data or Python objects that need to be
172 Are the data objects raw data or Python objects that need to be
173 formatted before display? [default: False]
173 formatted before display? [default: False]
174 """
174 """
175 raw = kwargs.pop('raw',False)
175 raw = kwargs.pop('raw',False)
176 if raw:
176 if raw:
177 for obj in objs:
177 for obj in objs:
178 publish_latex(obj)
178 publish_latex(obj)
179 else:
179 else:
180 display(*objs, include=['text/plain','text/latex'])
180 display(*objs, include=['text/plain','text/latex'])
181
181
182
182
183 def display_json(*objs, **kwargs):
183 def display_json(*objs, **kwargs):
184 """Display the JSON representation of an object.
184 """Display the JSON representation of an object.
185
185
186 Parameters
186 Parameters
187 ----------
187 ----------
188 objs : tuple of objects
188 objs : tuple of objects
189 The Python objects to display, or if raw=True raw json data to
189 The Python objects to display, or if raw=True raw json data to
190 display.
190 display.
191 raw : bool
191 raw : bool
192 Are the data objects raw data or Python objects that need to be
192 Are the data objects raw data or Python objects that need to be
193 formatted before display? [default: False]
193 formatted before display? [default: False]
194 """
194 """
195 raw = kwargs.pop('raw',False)
195 raw = kwargs.pop('raw',False)
196 if raw:
196 if raw:
197 for obj in objs:
197 for obj in objs:
198 publish_json(obj)
198 publish_json(obj)
199 else:
199 else:
200 display(*objs, include=['text/plain','application/json'])
200 display(*objs, include=['text/plain','application/json'])
201
201
202
202
203 def display_javascript(*objs, **kwargs):
203 def display_javascript(*objs, **kwargs):
204 """Display the Javascript representation of an object.
204 """Display the Javascript representation of an object.
205
205
206 Parameters
206 Parameters
207 ----------
207 ----------
208 objs : tuple of objects
208 objs : tuple of objects
209 The Python objects to display, or if raw=True raw javascript data to
209 The Python objects to display, or if raw=True raw javascript data to
210 display.
210 display.
211 raw : bool
211 raw : bool
212 Are the data objects raw data or Python objects that need to be
212 Are the data objects raw data or Python objects that need to be
213 formatted before display? [default: False]
213 formatted before display? [default: False]
214 """
214 """
215 raw = kwargs.pop('raw',False)
215 raw = kwargs.pop('raw',False)
216 if raw:
216 if raw:
217 for obj in objs:
217 for obj in objs:
218 publish_javascript(obj)
218 publish_javascript(obj)
219 else:
219 else:
220 display(*objs, include=['text/plain','application/javascript'])
220 display(*objs, include=['text/plain','application/javascript'])
221
221
222 #-----------------------------------------------------------------------------
222 #-----------------------------------------------------------------------------
223 # Smart classes
223 # Smart classes
224 #-----------------------------------------------------------------------------
224 #-----------------------------------------------------------------------------
225
225
226
226
227 class DisplayObject(object):
227 class DisplayObject(object):
228 """An object that wraps data to be displayed."""
228 """An object that wraps data to be displayed."""
229
229
230 _read_flags = 'r'
230 _read_flags = 'r'
231
231
232 def __init__(self, data=None, url=None, filename=None):
232 def __init__(self, data=None, url=None, filename=None):
233 """Create a display object given raw data.
233 """Create a display object given raw data.
234
234
235 When this object is returned by an expression or passed to the
235 When this object is returned by an expression or passed to the
236 display function, it will result in the data being displayed
236 display function, it will result in the data being displayed
237 in the frontend. The MIME type of the data should match the
237 in the frontend. The MIME type of the data should match the
238 subclasses used, so the Png subclass should be used for 'image/png'
238 subclasses used, so the Png subclass should be used for 'image/png'
239 data. If the data is a URL, the data will first be downloaded
239 data. If the data is a URL, the data will first be downloaded
240 and then displayed. If
240 and then displayed. If
241
241
242 Parameters
242 Parameters
243 ----------
243 ----------
244 data : unicode, str or bytes
244 data : unicode, str or bytes
245 The raw data or a URL to download the data from.
245 The raw data or a URL to download the data from.
246 url : unicode
246 url : unicode
247 A URL to download the data from.
247 A URL to download the data from.
248 filename : unicode
248 filename : unicode
249 Path to a local file to load the data from.
249 Path to a local file to load the data from.
250 """
250 """
251 if data is not None and data.startswith('http'):
251 if data is not None and data.startswith('http'):
252 self.url = data
252 self.url = data
253 self.filename = None
253 self.filename = None
254 self.data = None
254 self.data = None
255 else:
255 else:
256 self.data = data
256 self.data = data
257 self.url = url
257 self.url = url
258 self.filename = None if filename is None else unicode(filename)
258 self.filename = None if filename is None else unicode(filename)
259 self.reload()
259 self.reload()
260
260
261 def reload(self):
261 def reload(self):
262 """Reload the raw data from file or URL."""
262 """Reload the raw data from file or URL."""
263 if self.filename is not None:
263 if self.filename is not None:
264 with open(self.filename, self._read_flags) as f:
264 with open(self.filename, self._read_flags) as f:
265 self.data = f.read()
265 self.data = f.read()
266 elif self.url is not None:
266 elif self.url is not None:
267 try:
267 try:
268 import urllib2
268 import urllib2
269 response = urllib2.urlopen(self.url)
269 response = urllib2.urlopen(self.url)
270 self.data = response.read()
270 self.data = response.read()
271 # extract encoding from header, if there is one:
271 # extract encoding from header, if there is one:
272 encoding = None
272 encoding = None
273 for sub in response.headers['content-type'].split(';'):
273 for sub in response.headers['content-type'].split(';'):
274 sub = sub.strip()
274 sub = sub.strip()
275 if sub.startswith('charset'):
275 if sub.startswith('charset'):
276 encoding = sub.split('=')[-1].strip()
276 encoding = sub.split('=')[-1].strip()
277 break
277 break
278 # decode data, if an encoding was specified
278 # decode data, if an encoding was specified
279 if encoding:
279 if encoding:
280 self.data = self.data.decode(encoding, 'replace')
280 self.data = self.data.decode(encoding, 'replace')
281 except:
281 except:
282 self.data = None
282 self.data = None
283
283
284 class Pretty(DisplayObject):
284 class Pretty(DisplayObject):
285
285
286 def _repr_pretty_(self):
286 def _repr_pretty_(self):
287 return self.data
287 return self.data
288
288
289
289
290 class HTML(DisplayObject):
290 class HTML(DisplayObject):
291
291
292 def _repr_html_(self):
292 def _repr_html_(self):
293 return self.data
293 return self.data
294
294
295
295
296 class Math(DisplayObject):
296 class Math(DisplayObject):
297
297
298 def _repr_latex_(self):
298 def _repr_latex_(self):
299 return self.data
299 return self.data
300
300
301
301
302 class SVG(DisplayObject):
302 class SVG(DisplayObject):
303
303
304 def _repr_svg_(self):
304 def _repr_svg_(self):
305 return self.data
305 return self.data
306
306
307
307
308 class JSON(DisplayObject):
308 class JSON(DisplayObject):
309
309
310 def _repr_json_(self):
310 def _repr_json_(self):
311 return self.data
311 return self.data
312
312
313
313
314 class Javascript(DisplayObject):
314 class Javascript(DisplayObject):
315
315
316 def _repr_javascript_(self):
316 def _repr_javascript_(self):
317 return self.data
317 return self.data
318
318
319
319
320 class Image(DisplayObject):
320 class Image(DisplayObject):
321
321
322 _read_flags = 'rb'
322 _read_flags = 'rb'
323
323
324 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
324 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
325 """Create a display an PNG/JPEG image given raw data.
325 """Create a display an PNG/JPEG image given raw data.
326
326
327 When this object is returned by an expression or passed to the
327 When this object is returned by an expression or passed to the
328 display function, it will result in the image being displayed
328 display function, it will result in the image being displayed
329 in the frontend.
329 in the frontend.
330
330
331 Parameters
331 Parameters
332 ----------
332 ----------
333 data : unicode, str or bytes
333 data : unicode, str or bytes
334 The raw data or a URL to download the data from.
334 The raw data or a URL to download the data from.
335 url : unicode
335 url : unicode
336 A URL to download the data from.
336 A URL to download the data from.
337 filename : unicode
337 filename : unicode
338 Path to a local file to load the data from.
338 Path to a local file to load the data from.
339 format : unicode
339 format : unicode
340 The format of the image data (png/jpeg/jpg). If a filename or URL is given
340 The format of the image data (png/jpeg/jpg). If a filename or URL is given
341 for format will be inferred from the filename extension.
341 for format will be inferred from the filename extension.
342 embed : bool
342 embed : bool
343 Should the image data be embedded in the notebook using a data URI (True)
343 Should the image data be embedded in the notebook using a data URI (True)
344 or be loaded using an <img> tag. Set this to True if you want the image
344 or be loaded using an <img> tag. Set this to True if you want the image
345 to be viewable later with no internet connection. If a filename is given
345 to be viewable later with no internet connection. If a filename is given
346 embed is always set to True.
346 embed is always set to True.
347 """
347 """
348 if filename is not None:
348 if filename is not None:
349 ext = self._find_ext(filename)
349 ext = self._find_ext(filename)
350 elif url is not None:
350 elif url is not None:
351 ext = self._find_ext(url)
351 ext = self._find_ext(url)
352 elif data.startswith('http'):
352 elif data.startswith('http'):
353 ext = self._find_ext(data)
353 ext = self._find_ext(data)
354 else:
354 else:
355 ext = None
355 ext = None
356 if ext is not None:
356 if ext is not None:
357 if ext == u'jpg' or ext == u'jpeg':
357 if ext == u'jpg' or ext == u'jpeg':
358 format = u'jpeg'
358 format = u'jpeg'
359 if ext == u'png':
359 if ext == u'png':
360 format = u'png'
360 format = u'png'
361 self.format = unicode(format).lower()
361 self.format = unicode(format).lower()
362 self.embed = True if filename is not None else embed
362 self.embed = True if filename is not None else embed
363 super(Image, self).__init__(data=data, url=url, filename=filename)
363 super(Image, self).__init__(data=data, url=url, filename=filename)
364
364
365 def reload(self):
365 def reload(self):
366 """Reload the raw data from file or URL."""
366 """Reload the raw data from file or URL."""
367 if self.embed:
367 if self.embed:
368 super(Image,self).reload()
368 super(Image,self).reload()
369
369
370 def _repr_html_(self):
370 def _repr_html_(self):
371 if not self.embed:
371 if not self.embed:
372 return u'<img src="%s" />' % self.url
372 return u'<img src="%s" />' % self.url
373
373
374 def _repr_png_(self):
374 def _repr_png_(self):
375 if self.embed and self.format == u'png':
375 if self.embed and self.format == u'png':
376 return self.data
376 return self.data
377
377
378 def _repr_jpeg_(self):
378 def _repr_jpeg_(self):
379 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
379 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
380 return self.data
380 return self.data
381
381
382 def _find_ext(self, s):
382 def _find_ext(self, s):
383 return unicode(s.split('.')[-1].lower())
383 return unicode(s.split('.')[-1].lower())
384
385
386 def clear_output():
387 """Clear the output of the current cell receiving output."""
388 from IPython.core.interactiveshell import InteractiveShell
389 InteractiveShell.instance().display_pub.clear_output()
@@ -1,298 +1,302 b''
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVg, etc.).
6 representation of the object in various formats (text, HTML, SVg, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13
13
14 Authors:
14 Authors:
15
15
16 * Brian Granger
16 * Brian Granger
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2010 The IPython Development Team
20 # Copyright (C) 2008-2010 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 from __future__ import print_function
30 from __future__ import print_function
31
31
32 from IPython.config.configurable import Configurable
32 from IPython.config.configurable import Configurable
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Main payload class
35 # Main payload class
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class DisplayPublisher(Configurable):
38 class DisplayPublisher(Configurable):
39 """A traited class that publishes display data to frontends.
39 """A traited class that publishes display data to frontends.
40
40
41 Instances of this class are created by the main IPython object and should
41 Instances of this class are created by the main IPython object and should
42 be accessed there.
42 be accessed there.
43 """
43 """
44
44
45 def _validate_data(self, source, data, metadata=None):
45 def _validate_data(self, source, data, metadata=None):
46 """Validate the display data.
46 """Validate the display data.
47
47
48 Parameters
48 Parameters
49 ----------
49 ----------
50 source : str
50 source : str
51 The fully dotted name of the callable that created the data, like
51 The fully dotted name of the callable that created the data, like
52 :func:`foo.bar.my_formatter`.
52 :func:`foo.bar.my_formatter`.
53 data : dict
53 data : dict
54 The formata data dictionary.
54 The formata data dictionary.
55 metadata : dict
55 metadata : dict
56 Any metadata for the data.
56 Any metadata for the data.
57 """
57 """
58
58
59 if not isinstance(source, basestring):
59 if not isinstance(source, basestring):
60 raise TypeError('source must be a str, got: %r' % source)
60 raise TypeError('source must be a str, got: %r' % source)
61 if not isinstance(data, dict):
61 if not isinstance(data, dict):
62 raise TypeError('data must be a dict, got: %r' % data)
62 raise TypeError('data must be a dict, got: %r' % data)
63 if metadata is not None:
63 if metadata is not None:
64 if not isinstance(metadata, dict):
64 if not isinstance(metadata, dict):
65 raise TypeError('metadata must be a dict, got: %r' % data)
65 raise TypeError('metadata must be a dict, got: %r' % data)
66
66
67 def publish(self, source, data, metadata=None):
67 def publish(self, source, data, metadata=None):
68 """Publish data and metadata to all frontends.
68 """Publish data and metadata to all frontends.
69
69
70 See the ``display_data`` message in the messaging documentation for
70 See the ``display_data`` message in the messaging documentation for
71 more details about this message type.
71 more details about this message type.
72
72
73 The following MIME types are currently implemented:
73 The following MIME types are currently implemented:
74
74
75 * text/plain
75 * text/plain
76 * text/html
76 * text/html
77 * text/latex
77 * text/latex
78 * application/json
78 * application/json
79 * application/javascript
79 * application/javascript
80 * image/png
80 * image/png
81 * image/jpeg
81 * image/jpeg
82 * image/svg+xml
82 * image/svg+xml
83
83
84 Parameters
84 Parameters
85 ----------
85 ----------
86 source : str
86 source : str
87 A string that give the function or method that created the data,
87 A string that give the function or method that created the data,
88 such as 'IPython.core.page'.
88 such as 'IPython.core.page'.
89 data : dict
89 data : dict
90 A dictionary having keys that are valid MIME types (like
90 A dictionary having keys that are valid MIME types (like
91 'text/plain' or 'image/svg+xml') and values that are the data for
91 'text/plain' or 'image/svg+xml') and values that are the data for
92 that MIME type. The data itself must be a JSON'able data
92 that MIME type. The data itself must be a JSON'able data
93 structure. Minimally all data should have the 'text/plain' data,
93 structure. Minimally all data should have the 'text/plain' data,
94 which can be displayed by all frontends. If more than the plain
94 which can be displayed by all frontends. If more than the plain
95 text is given, it is up to the frontend to decide which
95 text is given, it is up to the frontend to decide which
96 representation to use.
96 representation to use.
97 metadata : dict
97 metadata : dict
98 A dictionary for metadata related to the data. This can contain
98 A dictionary for metadata related to the data. This can contain
99 arbitrary key, value pairs that frontends can use to interpret
99 arbitrary key, value pairs that frontends can use to interpret
100 the data.
100 the data.
101 """
101 """
102 from IPython.utils import io
102 from IPython.utils import io
103 # The default is to simply write the plain text data using io.stdout.
103 # The default is to simply write the plain text data using io.stdout.
104 if data.has_key('text/plain'):
104 if data.has_key('text/plain'):
105 print(data['text/plain'], file=io.stdout)
105 print(data['text/plain'], file=io.stdout)
106
106
107 def clear_output(self):
108 """Clear the output of the cell receiving output."""
109 pass
110
107
111
108 def publish_display_data(source, data, metadata=None):
112 def publish_display_data(source, data, metadata=None):
109 """Publish data and metadata to all frontends.
113 """Publish data and metadata to all frontends.
110
114
111 See the ``display_data`` message in the messaging documentation for
115 See the ``display_data`` message in the messaging documentation for
112 more details about this message type.
116 more details about this message type.
113
117
114 The following MIME types are currently implemented:
118 The following MIME types are currently implemented:
115
119
116 * text/plain
120 * text/plain
117 * text/html
121 * text/html
118 * text/latex
122 * text/latex
119 * application/json
123 * application/json
120 * application/javascript
124 * application/javascript
121 * image/png
125 * image/png
122 * image/jpeg
126 * image/jpeg
123 * image/svg+xml
127 * image/svg+xml
124
128
125 Parameters
129 Parameters
126 ----------
130 ----------
127 source : str
131 source : str
128 A string that give the function or method that created the data,
132 A string that give the function or method that created the data,
129 such as 'IPython.core.page'.
133 such as 'IPython.core.page'.
130 data : dict
134 data : dict
131 A dictionary having keys that are valid MIME types (like
135 A dictionary having keys that are valid MIME types (like
132 'text/plain' or 'image/svg+xml') and values that are the data for
136 'text/plain' or 'image/svg+xml') and values that are the data for
133 that MIME type. The data itself must be a JSON'able data
137 that MIME type. The data itself must be a JSON'able data
134 structure. Minimally all data should have the 'text/plain' data,
138 structure. Minimally all data should have the 'text/plain' data,
135 which can be displayed by all frontends. If more than the plain
139 which can be displayed by all frontends. If more than the plain
136 text is given, it is up to the frontend to decide which
140 text is given, it is up to the frontend to decide which
137 representation to use.
141 representation to use.
138 metadata : dict
142 metadata : dict
139 A dictionary for metadata related to the data. This can contain
143 A dictionary for metadata related to the data. This can contain
140 arbitrary key, value pairs that frontends can use to interpret
144 arbitrary key, value pairs that frontends can use to interpret
141 the data.
145 the data.
142 """
146 """
143 from IPython.core.interactiveshell import InteractiveShell
147 from IPython.core.interactiveshell import InteractiveShell
144 InteractiveShell.instance().display_pub.publish(
148 InteractiveShell.instance().display_pub.publish(
145 source,
149 source,
146 data,
150 data,
147 metadata
151 metadata
148 )
152 )
149
153
150
154
151 def publish_pretty(data, metadata=None):
155 def publish_pretty(data, metadata=None):
152 """Publish raw text data to all frontends.
156 """Publish raw text data to all frontends.
153
157
154 Parameters
158 Parameters
155 ----------
159 ----------
156 data : unicode
160 data : unicode
157 The raw text data to publish.
161 The raw text data to publish.
158 metadata : dict
162 metadata : dict
159 A dictionary for metadata related to the data. This can contain
163 A dictionary for metadata related to the data. This can contain
160 arbitrary key, value pairs that frontends can use to interpret
164 arbitrary key, value pairs that frontends can use to interpret
161 the data.
165 the data.
162 """
166 """
163 publish_display_data(
167 publish_display_data(
164 u'IPython.core.displaypub.publish_pretty',
168 u'IPython.core.displaypub.publish_pretty',
165 {'text/plain':data},
169 {'text/plain':data},
166 metadata=metadata
170 metadata=metadata
167 )
171 )
168
172
169
173
170 def publish_html(data, metadata=None):
174 def publish_html(data, metadata=None):
171 """Publish raw HTML data to all frontends.
175 """Publish raw HTML data to all frontends.
172
176
173 Parameters
177 Parameters
174 ----------
178 ----------
175 data : unicode
179 data : unicode
176 The raw HTML data to publish.
180 The raw HTML data to publish.
177 metadata : dict
181 metadata : dict
178 A dictionary for metadata related to the data. This can contain
182 A dictionary for metadata related to the data. This can contain
179 arbitrary key, value pairs that frontends can use to interpret
183 arbitrary key, value pairs that frontends can use to interpret
180 the data.
184 the data.
181 """
185 """
182 publish_display_data(
186 publish_display_data(
183 u'IPython.core.displaypub.publish_html',
187 u'IPython.core.displaypub.publish_html',
184 {'text/html':data},
188 {'text/html':data},
185 metadata=metadata
189 metadata=metadata
186 )
190 )
187
191
188
192
189 def publish_latex(data, metadata=None):
193 def publish_latex(data, metadata=None):
190 """Publish raw LaTeX data to all frontends.
194 """Publish raw LaTeX data to all frontends.
191
195
192 Parameters
196 Parameters
193 ----------
197 ----------
194 data : unicode
198 data : unicode
195 The raw LaTeX data to publish.
199 The raw LaTeX data to publish.
196 metadata : dict
200 metadata : dict
197 A dictionary for metadata related to the data. This can contain
201 A dictionary for metadata related to the data. This can contain
198 arbitrary key, value pairs that frontends can use to interpret
202 arbitrary key, value pairs that frontends can use to interpret
199 the data.
203 the data.
200 """
204 """
201 publish_display_data(
205 publish_display_data(
202 u'IPython.core.displaypub.publish_latex',
206 u'IPython.core.displaypub.publish_latex',
203 {'text/latex':data},
207 {'text/latex':data},
204 metadata=metadata
208 metadata=metadata
205 )
209 )
206
210
207 def publish_png(data, metadata=None):
211 def publish_png(data, metadata=None):
208 """Publish raw binary PNG data to all frontends.
212 """Publish raw binary PNG data to all frontends.
209
213
210 Parameters
214 Parameters
211 ----------
215 ----------
212 data : str/bytes
216 data : str/bytes
213 The raw binary PNG data to publish.
217 The raw binary PNG data to publish.
214 metadata : dict
218 metadata : dict
215 A dictionary for metadata related to the data. This can contain
219 A dictionary for metadata related to the data. This can contain
216 arbitrary key, value pairs that frontends can use to interpret
220 arbitrary key, value pairs that frontends can use to interpret
217 the data.
221 the data.
218 """
222 """
219 publish_display_data(
223 publish_display_data(
220 u'IPython.core.displaypub.publish_png',
224 u'IPython.core.displaypub.publish_png',
221 {'image/png':data},
225 {'image/png':data},
222 metadata=metadata
226 metadata=metadata
223 )
227 )
224
228
225
229
226 def publish_jpeg(data, metadata=None):
230 def publish_jpeg(data, metadata=None):
227 """Publish raw binary JPEG data to all frontends.
231 """Publish raw binary JPEG data to all frontends.
228
232
229 Parameters
233 Parameters
230 ----------
234 ----------
231 data : str/bytes
235 data : str/bytes
232 The raw binary JPEG data to publish.
236 The raw binary JPEG data to publish.
233 metadata : dict
237 metadata : dict
234 A dictionary for metadata related to the data. This can contain
238 A dictionary for metadata related to the data. This can contain
235 arbitrary key, value pairs that frontends can use to interpret
239 arbitrary key, value pairs that frontends can use to interpret
236 the data.
240 the data.
237 """
241 """
238 publish_display_data(
242 publish_display_data(
239 u'IPython.core.displaypub.publish_jpeg',
243 u'IPython.core.displaypub.publish_jpeg',
240 {'image/jpeg':data},
244 {'image/jpeg':data},
241 metadata=metadata
245 metadata=metadata
242 )
246 )
243
247
244
248
245 def publish_svg(data, metadata=None):
249 def publish_svg(data, metadata=None):
246 """Publish raw SVG data to all frontends.
250 """Publish raw SVG data to all frontends.
247
251
248 Parameters
252 Parameters
249 ----------
253 ----------
250 data : unicode
254 data : unicode
251 The raw SVG data to publish.
255 The raw SVG data to publish.
252 metadata : dict
256 metadata : dict
253 A dictionary for metadata related to the data. This can contain
257 A dictionary for metadata related to the data. This can contain
254 arbitrary key, value pairs that frontends can use to interpret
258 arbitrary key, value pairs that frontends can use to interpret
255 the data.
259 the data.
256 """
260 """
257 publish_display_data(
261 publish_display_data(
258 u'IPython.core.displaypub.publish_svg',
262 u'IPython.core.displaypub.publish_svg',
259 {'image/svg+xml':data},
263 {'image/svg+xml':data},
260 metadata=metadata
264 metadata=metadata
261 )
265 )
262
266
263 def publish_json(data, metadata=None):
267 def publish_json(data, metadata=None):
264 """Publish raw JSON data to all frontends.
268 """Publish raw JSON data to all frontends.
265
269
266 Parameters
270 Parameters
267 ----------
271 ----------
268 data : unicode
272 data : unicode
269 The raw JSON data to publish.
273 The raw JSON data to publish.
270 metadata : dict
274 metadata : dict
271 A dictionary for metadata related to the data. This can contain
275 A dictionary for metadata related to the data. This can contain
272 arbitrary key, value pairs that frontends can use to interpret
276 arbitrary key, value pairs that frontends can use to interpret
273 the data.
277 the data.
274 """
278 """
275 publish_display_data(
279 publish_display_data(
276 u'IPython.core.displaypub.publish_json',
280 u'IPython.core.displaypub.publish_json',
277 {'application/json':data},
281 {'application/json':data},
278 metadata=metadata
282 metadata=metadata
279 )
283 )
280
284
281 def publish_javascript(data, metadata=None):
285 def publish_javascript(data, metadata=None):
282 """Publish raw Javascript data to all frontends.
286 """Publish raw Javascript data to all frontends.
283
287
284 Parameters
288 Parameters
285 ----------
289 ----------
286 data : unicode
290 data : unicode
287 The raw Javascript data to publish.
291 The raw Javascript data to publish.
288 metadata : dict
292 metadata : dict
289 A dictionary for metadata related to the data. This can contain
293 A dictionary for metadata related to the data. This can contain
290 arbitrary key, value pairs that frontends can use to interpret
294 arbitrary key, value pairs that frontends can use to interpret
291 the data.
295 the data.
292 """
296 """
293 publish_display_data(
297 publish_display_data(
294 u'IPython.core.displaypub.publish_javascript',
298 u'IPython.core.displaypub.publish_javascript',
295 {'application/javascript':data},
299 {'application/javascript':data},
296 metadata=metadata
300 metadata=metadata
297 )
301 )
298
302
@@ -1,995 +1,997 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var Notebook = function (selector) {
16 var Notebook = function (selector) {
17 this.element = $(selector);
17 this.element = $(selector);
18 this.element.scroll();
18 this.element.scroll();
19 this.element.data("notebook", this);
19 this.element.data("notebook", this);
20 this.next_prompt_number = 1;
20 this.next_prompt_number = 1;
21 this.kernel = null;
21 this.kernel = null;
22 this.dirty = false;
22 this.dirty = false;
23 this.msg_cell_map = {};
23 this.msg_cell_map = {};
24 this.metadata = {};
24 this.metadata = {};
25 this.control_key_active = false;
25 this.control_key_active = false;
26 this.style();
26 this.style();
27 this.create_elements();
27 this.create_elements();
28 this.bind_events();
28 this.bind_events();
29 };
29 };
30
30
31
31
32 Notebook.prototype.style = function () {
32 Notebook.prototype.style = function () {
33 $('div#notebook').addClass('border-box-sizing');
33 $('div#notebook').addClass('border-box-sizing');
34 };
34 };
35
35
36
36
37 Notebook.prototype.create_elements = function () {
37 Notebook.prototype.create_elements = function () {
38 // We add this end_space div to the end of the notebook div to:
38 // We add this end_space div to the end of the notebook div to:
39 // i) provide a margin between the last cell and the end of the notebook
39 // i) provide a margin between the last cell and the end of the notebook
40 // ii) to prevent the div from scrolling up when the last cell is being
40 // ii) to prevent the div from scrolling up when the last cell is being
41 // edited, but is too low on the page, which browsers will do automatically.
41 // edited, but is too low on the page, which browsers will do automatically.
42 var that = this;
42 var that = this;
43 var end_space = $('<div class="end_space"></div>').height(150);
43 var end_space = $('<div class="end_space"></div>').height(150);
44 end_space.dblclick(function (e) {
44 end_space.dblclick(function (e) {
45 var ncells = that.ncells();
45 var ncells = that.ncells();
46 that.insert_code_cell_below(ncells-1);
46 that.insert_code_cell_below(ncells-1);
47 });
47 });
48 this.element.append(end_space);
48 this.element.append(end_space);
49 $('div#notebook').addClass('border-box-sizing');
49 $('div#notebook').addClass('border-box-sizing');
50 };
50 };
51
51
52
52
53 Notebook.prototype.bind_events = function () {
53 Notebook.prototype.bind_events = function () {
54 var that = this;
54 var that = this;
55 $(document).keydown(function (event) {
55 $(document).keydown(function (event) {
56 // console.log(event);
56 // console.log(event);
57 if (event.which === 38) {
57 if (event.which === 38) {
58 var cell = that.selected_cell();
58 var cell = that.selected_cell();
59 if (cell.at_top()) {
59 if (cell.at_top()) {
60 event.preventDefault();
60 event.preventDefault();
61 that.select_prev();
61 that.select_prev();
62 };
62 };
63 } else if (event.which === 40) {
63 } else if (event.which === 40) {
64 var cell = that.selected_cell();
64 var cell = that.selected_cell();
65 if (cell.at_bottom()) {
65 if (cell.at_bottom()) {
66 event.preventDefault();
66 event.preventDefault();
67 that.select_next();
67 that.select_next();
68 };
68 };
69 } else if (event.which === 13 && event.shiftKey) {
69 } else if (event.which === 13 && event.shiftKey) {
70 that.execute_selected_cell();
70 that.execute_selected_cell();
71 return false;
71 return false;
72 } else if (event.which === 13 && event.ctrlKey) {
72 } else if (event.which === 13 && event.ctrlKey) {
73 that.execute_selected_cell({terminal:true});
73 that.execute_selected_cell({terminal:true});
74 return false;
74 return false;
75 } else if (event.which === 77 && event.ctrlKey) {
75 } else if (event.which === 77 && event.ctrlKey) {
76 that.control_key_active = true;
76 that.control_key_active = true;
77 return false;
77 return false;
78 } else if (event.which === 68 && that.control_key_active) {
78 } else if (event.which === 68 && that.control_key_active) {
79 // Delete selected cell = d
79 // Delete selected cell = d
80 that.delete_cell();
80 that.delete_cell();
81 that.control_key_active = false;
81 that.control_key_active = false;
82 return false;
82 return false;
83 } else if (event.which === 65 && that.control_key_active) {
83 } else if (event.which === 65 && that.control_key_active) {
84 // Insert code cell above selected = a
84 // Insert code cell above selected = a
85 that.insert_code_cell_above();
85 that.insert_code_cell_above();
86 that.control_key_active = false;
86 that.control_key_active = false;
87 return false;
87 return false;
88 } else if (event.which === 66 && that.control_key_active) {
88 } else if (event.which === 66 && that.control_key_active) {
89 // Insert code cell below selected = b
89 // Insert code cell below selected = b
90 that.insert_code_cell_below();
90 that.insert_code_cell_below();
91 that.control_key_active = false;
91 that.control_key_active = false;
92 return false;
92 return false;
93 } else if (event.which === 67 && that.control_key_active) {
93 } else if (event.which === 67 && that.control_key_active) {
94 // To code = c
94 // To code = c
95 that.to_code();
95 that.to_code();
96 that.control_key_active = false;
96 that.control_key_active = false;
97 return false;
97 return false;
98 } else if (event.which === 77 && that.control_key_active) {
98 } else if (event.which === 77 && that.control_key_active) {
99 // To markdown = m
99 // To markdown = m
100 that.to_markdown();
100 that.to_markdown();
101 that.control_key_active = false;
101 that.control_key_active = false;
102 return false;
102 return false;
103 } else if (event.which === 84 && that.control_key_active) {
103 } else if (event.which === 84 && that.control_key_active) {
104 // Toggle output = t
104 // Toggle output = t
105 that.toggle_output();
105 that.toggle_output();
106 that.control_key_active = false;
106 that.control_key_active = false;
107 return false;
107 return false;
108 } else if (event.which === 83 && that.control_key_active) {
108 } else if (event.which === 83 && that.control_key_active) {
109 // Save notebook = s
109 // Save notebook = s
110 IPython.save_widget.save_notebook();
110 IPython.save_widget.save_notebook();
111 that.control_key_active = false;
111 that.control_key_active = false;
112 return false;
112 return false;
113 } else if (event.which === 74 && that.control_key_active) {
113 } else if (event.which === 74 && that.control_key_active) {
114 // Move cell down = j
114 // Move cell down = j
115 that.move_cell_down();
115 that.move_cell_down();
116 that.control_key_active = false;
116 that.control_key_active = false;
117 return false;
117 return false;
118 } else if (event.which === 75 && that.control_key_active) {
118 } else if (event.which === 75 && that.control_key_active) {
119 // Move cell up = k
119 // Move cell up = k
120 that.move_cell_up();
120 that.move_cell_up();
121 that.control_key_active = false;
121 that.control_key_active = false;
122 return false;
122 return false;
123 } else if (event.which === 80 && that.control_key_active) {
123 } else if (event.which === 80 && that.control_key_active) {
124 // Select previous = p
124 // Select previous = p
125 that.select_prev();
125 that.select_prev();
126 that.control_key_active = false;
126 that.control_key_active = false;
127 return false;
127 return false;
128 } else if (event.which === 78 && that.control_key_active) {
128 } else if (event.which === 78 && that.control_key_active) {
129 // Select next = n
129 // Select next = n
130 that.select_next();
130 that.select_next();
131 that.control_key_active = false;
131 that.control_key_active = false;
132 return false;
132 return false;
133 } else if (event.which === 76 && that.control_key_active) {
133 } else if (event.which === 76 && that.control_key_active) {
134 // Toggle line numbers = l
134 // Toggle line numbers = l
135 that.cell_toggle_line_numbers();
135 that.cell_toggle_line_numbers();
136 that.control_key_active = false;
136 that.control_key_active = false;
137 return false;
137 return false;
138 } else if (event.which === 73 && that.control_key_active) {
138 } else if (event.which === 73 && that.control_key_active) {
139 // Interrupt kernel = i
139 // Interrupt kernel = i
140 IPython.notebook.kernel.interrupt();
140 IPython.notebook.kernel.interrupt();
141 that.control_key_active = false;
141 that.control_key_active = false;
142 return false;
142 return false;
143 } else if (event.which === 190 && that.control_key_active) {
143 } else if (event.which === 190 && that.control_key_active) {
144 // Restart kernel = . # matches qt console
144 // Restart kernel = . # matches qt console
145 IPython.notebook.restart_kernel();
145 IPython.notebook.restart_kernel();
146 that.control_key_active = false;
146 that.control_key_active = false;
147 return false;
147 return false;
148 } else if (event.which === 72 && that.control_key_active) {
148 } else if (event.which === 72 && that.control_key_active) {
149 // Show keyboard shortcuts = h
149 // Show keyboard shortcuts = h
150 that.toggle_keyboard_shortcuts();
150 that.toggle_keyboard_shortcuts();
151 that.control_key_active = false;
151 that.control_key_active = false;
152 return false;
152 return false;
153 } else if (that.control_key_active) {
153 } else if (that.control_key_active) {
154 that.control_key_active = false;
154 that.control_key_active = false;
155 return true;
155 return true;
156 };
156 };
157 });
157 });
158
158
159 this.element.bind('collapse_pager', function () {
159 this.element.bind('collapse_pager', function () {
160 var app_height = $('div#main_app').height(); // content height
160 var app_height = $('div#main_app').height(); // content height
161 var splitter_height = $('div#pager_splitter').outerHeight(true);
161 var splitter_height = $('div#pager_splitter').outerHeight(true);
162 var new_height = app_height - splitter_height;
162 var new_height = app_height - splitter_height;
163 that.element.animate({height : new_height + 'px'}, 'fast');
163 that.element.animate({height : new_height + 'px'}, 'fast');
164 });
164 });
165
165
166 this.element.bind('expand_pager', function () {
166 this.element.bind('expand_pager', function () {
167 var app_height = $('div#main_app').height(); // content height
167 var app_height = $('div#main_app').height(); // content height
168 var splitter_height = $('div#pager_splitter').outerHeight(true);
168 var splitter_height = $('div#pager_splitter').outerHeight(true);
169 var pager_height = $('div#pager').outerHeight(true);
169 var pager_height = $('div#pager').outerHeight(true);
170 var new_height = app_height - pager_height - splitter_height;
170 var new_height = app_height - pager_height - splitter_height;
171 that.element.animate({height : new_height + 'px'}, 'fast');
171 that.element.animate({height : new_height + 'px'}, 'fast');
172 });
172 });
173
173
174 this.element.bind('collapse_left_panel', function () {
174 this.element.bind('collapse_left_panel', function () {
175 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
175 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
176 var new_margin = splitter_width;
176 var new_margin = splitter_width;
177 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
177 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
178 });
178 });
179
179
180 this.element.bind('expand_left_panel', function () {
180 this.element.bind('expand_left_panel', function () {
181 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
181 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
182 var left_panel_width = IPython.left_panel.width;
182 var left_panel_width = IPython.left_panel.width;
183 var new_margin = splitter_width + left_panel_width;
183 var new_margin = splitter_width + left_panel_width;
184 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
184 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
185 });
185 });
186
186
187 $(window).bind('beforeunload', function () {
187 $(window).bind('beforeunload', function () {
188 var kill_kernel = $('#kill_kernel').prop('checked');
188 var kill_kernel = $('#kill_kernel').prop('checked');
189 if (kill_kernel) {
189 if (kill_kernel) {
190 that.kernel.kill();
190 that.kernel.kill();
191 }
191 }
192 if (that.dirty) {
192 if (that.dirty) {
193 return "You have unsaved changes that will be lost if you leave this page.";
193 return "You have unsaved changes that will be lost if you leave this page.";
194 };
194 };
195 });
195 });
196 };
196 };
197
197
198
198
199 Notebook.prototype.toggle_keyboard_shortcuts = function () {
199 Notebook.prototype.toggle_keyboard_shortcuts = function () {
200 // toggles display of keyboard shortcut dialog
200 // toggles display of keyboard shortcut dialog
201 var that = this;
201 var that = this;
202 if ( this.shortcut_dialog ){
202 if ( this.shortcut_dialog ){
203 // if dialog is already shown, close it
203 // if dialog is already shown, close it
204 this.shortcut_dialog.dialog("close");
204 this.shortcut_dialog.dialog("close");
205 this.shortcut_dialog = null;
205 this.shortcut_dialog = null;
206 return;
206 return;
207 }
207 }
208 var dialog = $('<div/>');
208 var dialog = $('<div/>');
209 this.shortcut_dialog = dialog;
209 this.shortcut_dialog = dialog;
210 var shortcuts = [
210 var shortcuts = [
211 {key: 'Shift-Enter', help: 'run cell'},
211 {key: 'Shift-Enter', help: 'run cell'},
212 {key: 'Ctrl-Enter', help: 'run cell in-place'},
212 {key: 'Ctrl-Enter', help: 'run cell in-place'},
213 {key: 'Ctrl-m d', help: 'delete cell'},
213 {key: 'Ctrl-m d', help: 'delete cell'},
214 {key: 'Ctrl-m a', help: 'insert cell above'},
214 {key: 'Ctrl-m a', help: 'insert cell above'},
215 {key: 'Ctrl-m b', help: 'insert cell below'},
215 {key: 'Ctrl-m b', help: 'insert cell below'},
216 {key: 'Ctrl-m t', help: 'toggle output'},
216 {key: 'Ctrl-m t', help: 'toggle output'},
217 {key: 'Ctrl-m l', help: 'toggle line numbers'},
217 {key: 'Ctrl-m l', help: 'toggle line numbers'},
218 {key: 'Ctrl-m s', help: 'save notebook'},
218 {key: 'Ctrl-m s', help: 'save notebook'},
219 {key: 'Ctrl-m j', help: 'move cell down'},
219 {key: 'Ctrl-m j', help: 'move cell down'},
220 {key: 'Ctrl-m k', help: 'move cell up'},
220 {key: 'Ctrl-m k', help: 'move cell up'},
221 {key: 'Ctrl-m c', help: 'code cell'},
221 {key: 'Ctrl-m c', help: 'code cell'},
222 {key: 'Ctrl-m m', help: 'markdown cell'},
222 {key: 'Ctrl-m m', help: 'markdown cell'},
223 {key: 'Ctrl-m p', help: 'select previous'},
223 {key: 'Ctrl-m p', help: 'select previous'},
224 {key: 'Ctrl-m n', help: 'select next'},
224 {key: 'Ctrl-m n', help: 'select next'},
225 {key: 'Ctrl-m i', help: 'interrupt kernel'},
225 {key: 'Ctrl-m i', help: 'interrupt kernel'},
226 {key: 'Ctrl-m .', help: 'restart kernel'},
226 {key: 'Ctrl-m .', help: 'restart kernel'},
227 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
227 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
228 ];
228 ];
229 for (var i=0; i<shortcuts.length; i++) {
229 for (var i=0; i<shortcuts.length; i++) {
230 dialog.append($('<div>').
230 dialog.append($('<div>').
231 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
231 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
232 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
232 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
233 );
233 );
234 };
234 };
235 dialog.bind('dialogclose', function(event) {
235 dialog.bind('dialogclose', function(event) {
236 // dialog has been closed, allow it to be drawn again.
236 // dialog has been closed, allow it to be drawn again.
237 that.shortcut_dialog = null;
237 that.shortcut_dialog = null;
238 });
238 });
239 dialog.dialog({title: 'Keyboard shortcuts'});
239 dialog.dialog({title: 'Keyboard shortcuts'});
240 };
240 };
241
241
242
242
243 Notebook.prototype.scroll_to_bottom = function () {
243 Notebook.prototype.scroll_to_bottom = function () {
244 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
244 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
245 };
245 };
246
246
247
247
248 Notebook.prototype.scroll_to_top = function () {
248 Notebook.prototype.scroll_to_top = function () {
249 this.element.animate({scrollTop:0}, 0);
249 this.element.animate({scrollTop:0}, 0);
250 };
250 };
251
251
252
252
253 // Cell indexing, retrieval, etc.
253 // Cell indexing, retrieval, etc.
254
254
255
255
256 Notebook.prototype.cell_elements = function () {
256 Notebook.prototype.cell_elements = function () {
257 return this.element.children("div.cell");
257 return this.element.children("div.cell");
258 }
258 }
259
259
260
260
261 Notebook.prototype.ncells = function (cell) {
261 Notebook.prototype.ncells = function (cell) {
262 return this.cell_elements().length;
262 return this.cell_elements().length;
263 }
263 }
264
264
265
265
266 // TODO: we are often calling cells as cells()[i], which we should optimize
266 // TODO: we are often calling cells as cells()[i], which we should optimize
267 // to cells(i) or a new method.
267 // to cells(i) or a new method.
268 Notebook.prototype.cells = function () {
268 Notebook.prototype.cells = function () {
269 return this.cell_elements().toArray().map(function (e) {
269 return this.cell_elements().toArray().map(function (e) {
270 return $(e).data("cell");
270 return $(e).data("cell");
271 });
271 });
272 }
272 }
273
273
274
274
275 Notebook.prototype.find_cell_index = function (cell) {
275 Notebook.prototype.find_cell_index = function (cell) {
276 var result = null;
276 var result = null;
277 this.cell_elements().filter(function (index) {
277 this.cell_elements().filter(function (index) {
278 if ($(this).data("cell") === cell) {
278 if ($(this).data("cell") === cell) {
279 result = index;
279 result = index;
280 };
280 };
281 });
281 });
282 return result;
282 return result;
283 };
283 };
284
284
285
285
286 Notebook.prototype.index_or_selected = function (index) {
286 Notebook.prototype.index_or_selected = function (index) {
287 return index || this.selected_index() || 0;
287 return index || this.selected_index() || 0;
288 }
288 }
289
289
290
290
291 Notebook.prototype.select = function (index) {
291 Notebook.prototype.select = function (index) {
292 if (index !== undefined && index >= 0 && index < this.ncells()) {
292 if (index !== undefined && index >= 0 && index < this.ncells()) {
293 if (this.selected_index() !== null) {
293 if (this.selected_index() !== null) {
294 this.selected_cell().unselect();
294 this.selected_cell().unselect();
295 };
295 };
296 this.cells()[index].select();
296 this.cells()[index].select();
297 };
297 };
298 return this;
298 return this;
299 };
299 };
300
300
301
301
302 Notebook.prototype.select_next = function () {
302 Notebook.prototype.select_next = function () {
303 var index = this.selected_index();
303 var index = this.selected_index();
304 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
304 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
305 this.select(index+1);
305 this.select(index+1);
306 };
306 };
307 return this;
307 return this;
308 };
308 };
309
309
310
310
311 Notebook.prototype.select_prev = function () {
311 Notebook.prototype.select_prev = function () {
312 var index = this.selected_index();
312 var index = this.selected_index();
313 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
313 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
314 this.select(index-1);
314 this.select(index-1);
315 };
315 };
316 return this;
316 return this;
317 };
317 };
318
318
319
319
320 Notebook.prototype.selected_index = function () {
320 Notebook.prototype.selected_index = function () {
321 var result = null;
321 var result = null;
322 this.cell_elements().filter(function (index) {
322 this.cell_elements().filter(function (index) {
323 if ($(this).data("cell").selected === true) {
323 if ($(this).data("cell").selected === true) {
324 result = index;
324 result = index;
325 };
325 };
326 });
326 });
327 return result;
327 return result;
328 };
328 };
329
329
330
330
331 Notebook.prototype.cell_for_msg = function (msg_id) {
331 Notebook.prototype.cell_for_msg = function (msg_id) {
332 var cell_id = this.msg_cell_map[msg_id];
332 var cell_id = this.msg_cell_map[msg_id];
333 var result = null;
333 var result = null;
334 this.cell_elements().filter(function (index) {
334 this.cell_elements().filter(function (index) {
335 cell = $(this).data("cell");
335 cell = $(this).data("cell");
336 if (cell.cell_id === cell_id) {
336 if (cell.cell_id === cell_id) {
337 result = cell;
337 result = cell;
338 };
338 };
339 });
339 });
340 return result;
340 return result;
341 };
341 };
342
342
343
343
344 Notebook.prototype.selected_cell = function () {
344 Notebook.prototype.selected_cell = function () {
345 return this.cell_elements().eq(this.selected_index()).data("cell");
345 return this.cell_elements().eq(this.selected_index()).data("cell");
346 }
346 }
347
347
348
348
349 // Cell insertion, deletion and moving.
349 // Cell insertion, deletion and moving.
350
350
351
351
352 Notebook.prototype.delete_cell = function (index) {
352 Notebook.prototype.delete_cell = function (index) {
353 var i = index || this.selected_index();
353 var i = index || this.selected_index();
354 if (i !== null && i >= 0 && i < this.ncells()) {
354 if (i !== null && i >= 0 && i < this.ncells()) {
355 this.cell_elements().eq(i).remove();
355 this.cell_elements().eq(i).remove();
356 if (i === (this.ncells())) {
356 if (i === (this.ncells())) {
357 this.select(i-1);
357 this.select(i-1);
358 } else {
358 } else {
359 this.select(i);
359 this.select(i);
360 };
360 };
361 };
361 };
362 this.dirty = true;
362 this.dirty = true;
363 return this;
363 return this;
364 };
364 };
365
365
366
366
367 Notebook.prototype.append_cell = function (cell) {
367 Notebook.prototype.append_cell = function (cell) {
368 this.element.find('div.end_space').before(cell.element);
368 this.element.find('div.end_space').before(cell.element);
369 this.dirty = true;
369 this.dirty = true;
370 return this;
370 return this;
371 };
371 };
372
372
373
373
374 Notebook.prototype.insert_cell_below = function (cell, index) {
374 Notebook.prototype.insert_cell_below = function (cell, index) {
375 var ncells = this.ncells();
375 var ncells = this.ncells();
376 if (ncells === 0) {
376 if (ncells === 0) {
377 this.append_cell(cell);
377 this.append_cell(cell);
378 return this;
378 return this;
379 };
379 };
380 if (index >= 0 && index < ncells) {
380 if (index >= 0 && index < ncells) {
381 this.cell_elements().eq(index).after(cell.element);
381 this.cell_elements().eq(index).after(cell.element);
382 };
382 };
383 this.dirty = true;
383 this.dirty = true;
384 return this
384 return this
385 };
385 };
386
386
387
387
388 Notebook.prototype.insert_cell_above = function (cell, index) {
388 Notebook.prototype.insert_cell_above = function (cell, index) {
389 var ncells = this.ncells();
389 var ncells = this.ncells();
390 if (ncells === 0) {
390 if (ncells === 0) {
391 this.append_cell(cell);
391 this.append_cell(cell);
392 return this;
392 return this;
393 };
393 };
394 if (index >= 0 && index < ncells) {
394 if (index >= 0 && index < ncells) {
395 this.cell_elements().eq(index).before(cell.element);
395 this.cell_elements().eq(index).before(cell.element);
396 };
396 };
397 this.dirty = true;
397 this.dirty = true;
398 return this;
398 return this;
399 };
399 };
400
400
401
401
402 Notebook.prototype.move_cell_up = function (index) {
402 Notebook.prototype.move_cell_up = function (index) {
403 var i = index || this.selected_index();
403 var i = index || this.selected_index();
404 if (i !== null && i < this.ncells() && i > 0) {
404 if (i !== null && i < this.ncells() && i > 0) {
405 var pivot = this.cell_elements().eq(i-1);
405 var pivot = this.cell_elements().eq(i-1);
406 var tomove = this.cell_elements().eq(i);
406 var tomove = this.cell_elements().eq(i);
407 if (pivot !== null && tomove !== null) {
407 if (pivot !== null && tomove !== null) {
408 tomove.detach();
408 tomove.detach();
409 pivot.before(tomove);
409 pivot.before(tomove);
410 this.select(i-1);
410 this.select(i-1);
411 };
411 };
412 };
412 };
413 this.dirty = true;
413 this.dirty = true;
414 return this;
414 return this;
415 }
415 }
416
416
417
417
418 Notebook.prototype.move_cell_down = function (index) {
418 Notebook.prototype.move_cell_down = function (index) {
419 var i = index || this.selected_index();
419 var i = index || this.selected_index();
420 if (i !== null && i < (this.ncells()-1) && i >= 0) {
420 if (i !== null && i < (this.ncells()-1) && i >= 0) {
421 var pivot = this.cell_elements().eq(i+1)
421 var pivot = this.cell_elements().eq(i+1)
422 var tomove = this.cell_elements().eq(i)
422 var tomove = this.cell_elements().eq(i)
423 if (pivot !== null && tomove !== null) {
423 if (pivot !== null && tomove !== null) {
424 tomove.detach();
424 tomove.detach();
425 pivot.after(tomove);
425 pivot.after(tomove);
426 this.select(i+1);
426 this.select(i+1);
427 };
427 };
428 };
428 };
429 this.dirty = true;
429 this.dirty = true;
430 return this;
430 return this;
431 }
431 }
432
432
433
433
434 Notebook.prototype.sort_cells = function () {
434 Notebook.prototype.sort_cells = function () {
435 var ncells = this.ncells();
435 var ncells = this.ncells();
436 var sindex = this.selected_index();
436 var sindex = this.selected_index();
437 var swapped;
437 var swapped;
438 do {
438 do {
439 swapped = false
439 swapped = false
440 for (var i=1; i<ncells; i++) {
440 for (var i=1; i<ncells; i++) {
441 current = this.cell_elements().eq(i).data("cell");
441 current = this.cell_elements().eq(i).data("cell");
442 previous = this.cell_elements().eq(i-1).data("cell");
442 previous = this.cell_elements().eq(i-1).data("cell");
443 if (previous.input_prompt_number > current.input_prompt_number) {
443 if (previous.input_prompt_number > current.input_prompt_number) {
444 this.move_cell_up(i);
444 this.move_cell_up(i);
445 swapped = true;
445 swapped = true;
446 };
446 };
447 };
447 };
448 } while (swapped);
448 } while (swapped);
449 this.select(sindex);
449 this.select(sindex);
450 return this;
450 return this;
451 };
451 };
452
452
453
453
454 Notebook.prototype.insert_code_cell_above = function (index) {
454 Notebook.prototype.insert_code_cell_above = function (index) {
455 // TODO: Bounds check for i
455 // TODO: Bounds check for i
456 var i = this.index_or_selected(index);
456 var i = this.index_or_selected(index);
457 var cell = new IPython.CodeCell(this);
457 var cell = new IPython.CodeCell(this);
458 cell.set_input_prompt();
458 cell.set_input_prompt();
459 this.insert_cell_above(cell, i);
459 this.insert_cell_above(cell, i);
460 this.select(this.find_cell_index(cell));
460 this.select(this.find_cell_index(cell));
461 return cell;
461 return cell;
462 }
462 }
463
463
464
464
465 Notebook.prototype.insert_code_cell_below = function (index) {
465 Notebook.prototype.insert_code_cell_below = function (index) {
466 // TODO: Bounds check for i
466 // TODO: Bounds check for i
467 var i = this.index_or_selected(index);
467 var i = this.index_or_selected(index);
468 var cell = new IPython.CodeCell(this);
468 var cell = new IPython.CodeCell(this);
469 cell.set_input_prompt();
469 cell.set_input_prompt();
470 this.insert_cell_below(cell, i);
470 this.insert_cell_below(cell, i);
471 this.select(this.find_cell_index(cell));
471 this.select(this.find_cell_index(cell));
472 return cell;
472 return cell;
473 }
473 }
474
474
475
475
476 Notebook.prototype.insert_html_cell_above = function (index) {
476 Notebook.prototype.insert_html_cell_above = function (index) {
477 // TODO: Bounds check for i
477 // TODO: Bounds check for i
478 var i = this.index_or_selected(index);
478 var i = this.index_or_selected(index);
479 var cell = new IPython.HTMLCell(this);
479 var cell = new IPython.HTMLCell(this);
480 cell.config_mathjax();
480 cell.config_mathjax();
481 this.insert_cell_above(cell, i);
481 this.insert_cell_above(cell, i);
482 this.select(this.find_cell_index(cell));
482 this.select(this.find_cell_index(cell));
483 return cell;
483 return cell;
484 }
484 }
485
485
486
486
487 Notebook.prototype.insert_html_cell_below = function (index) {
487 Notebook.prototype.insert_html_cell_below = function (index) {
488 // TODO: Bounds check for i
488 // TODO: Bounds check for i
489 var i = this.index_or_selected(index);
489 var i = this.index_or_selected(index);
490 var cell = new IPython.HTMLCell(this);
490 var cell = new IPython.HTMLCell(this);
491 cell.config_mathjax();
491 cell.config_mathjax();
492 this.insert_cell_below(cell, i);
492 this.insert_cell_below(cell, i);
493 this.select(this.find_cell_index(cell));
493 this.select(this.find_cell_index(cell));
494 return cell;
494 return cell;
495 }
495 }
496
496
497
497
498 Notebook.prototype.insert_markdown_cell_above = function (index) {
498 Notebook.prototype.insert_markdown_cell_above = function (index) {
499 // TODO: Bounds check for i
499 // TODO: Bounds check for i
500 var i = this.index_or_selected(index);
500 var i = this.index_or_selected(index);
501 var cell = new IPython.MarkdownCell(this);
501 var cell = new IPython.MarkdownCell(this);
502 cell.config_mathjax();
502 cell.config_mathjax();
503 this.insert_cell_above(cell, i);
503 this.insert_cell_above(cell, i);
504 this.select(this.find_cell_index(cell));
504 this.select(this.find_cell_index(cell));
505 return cell;
505 return cell;
506 }
506 }
507
507
508
508
509 Notebook.prototype.insert_markdown_cell_below = function (index) {
509 Notebook.prototype.insert_markdown_cell_below = function (index) {
510 // TODO: Bounds check for i
510 // TODO: Bounds check for i
511 var i = this.index_or_selected(index);
511 var i = this.index_or_selected(index);
512 var cell = new IPython.MarkdownCell(this);
512 var cell = new IPython.MarkdownCell(this);
513 cell.config_mathjax();
513 cell.config_mathjax();
514 this.insert_cell_below(cell, i);
514 this.insert_cell_below(cell, i);
515 this.select(this.find_cell_index(cell));
515 this.select(this.find_cell_index(cell));
516 return cell;
516 return cell;
517 }
517 }
518
518
519
519
520 Notebook.prototype.to_code = function (index) {
520 Notebook.prototype.to_code = function (index) {
521 // TODO: Bounds check for i
521 // TODO: Bounds check for i
522 var i = this.index_or_selected(index);
522 var i = this.index_or_selected(index);
523 var source_element = this.cell_elements().eq(i);
523 var source_element = this.cell_elements().eq(i);
524 var source_cell = source_element.data("cell");
524 var source_cell = source_element.data("cell");
525 if (source_cell instanceof IPython.HTMLCell ||
525 if (source_cell instanceof IPython.HTMLCell ||
526 source_cell instanceof IPython.MarkdownCell) {
526 source_cell instanceof IPython.MarkdownCell) {
527 this.insert_code_cell_below(i);
527 this.insert_code_cell_below(i);
528 var target_cell = this.cells()[i+1];
528 var target_cell = this.cells()[i+1];
529 target_cell.set_code(source_cell.get_source());
529 target_cell.set_code(source_cell.get_source());
530 source_element.remove();
530 source_element.remove();
531 target_cell.select();
531 target_cell.select();
532 };
532 };
533 this.dirty = true;
533 this.dirty = true;
534 };
534 };
535
535
536
536
537 Notebook.prototype.to_markdown = function (index) {
537 Notebook.prototype.to_markdown = function (index) {
538 // TODO: Bounds check for i
538 // TODO: Bounds check for i
539 var i = this.index_or_selected(index);
539 var i = this.index_or_selected(index);
540 var source_element = this.cell_elements().eq(i);
540 var source_element = this.cell_elements().eq(i);
541 var source_cell = source_element.data("cell");
541 var source_cell = source_element.data("cell");
542 var target_cell = null;
542 var target_cell = null;
543 if (source_cell instanceof IPython.CodeCell) {
543 if (source_cell instanceof IPython.CodeCell) {
544 this.insert_markdown_cell_below(i);
544 this.insert_markdown_cell_below(i);
545 var target_cell = this.cells()[i+1];
545 var target_cell = this.cells()[i+1];
546 var text = source_cell.get_code();
546 var text = source_cell.get_code();
547 } else if (source_cell instanceof IPython.HTMLCell) {
547 } else if (source_cell instanceof IPython.HTMLCell) {
548 this.insert_markdown_cell_below(i);
548 this.insert_markdown_cell_below(i);
549 var target_cell = this.cells()[i+1];
549 var target_cell = this.cells()[i+1];
550 var text = source_cell.get_source();
550 var text = source_cell.get_source();
551 if (text === source_cell.placeholder) {
551 if (text === source_cell.placeholder) {
552 text = target_cell.placeholder;
552 text = target_cell.placeholder;
553 }
553 }
554 }
554 }
555 if (target_cell !== null) {
555 if (target_cell !== null) {
556 if (text === "") {text = target_cell.placeholder;};
556 if (text === "") {text = target_cell.placeholder;};
557 target_cell.set_source(text);
557 target_cell.set_source(text);
558 source_element.remove();
558 source_element.remove();
559 target_cell.edit();
559 target_cell.edit();
560 }
560 }
561 this.dirty = true;
561 this.dirty = true;
562 };
562 };
563
563
564
564
565 Notebook.prototype.to_html = function (index) {
565 Notebook.prototype.to_html = function (index) {
566 // TODO: Bounds check for i
566 // TODO: Bounds check for i
567 var i = this.index_or_selected(index);
567 var i = this.index_or_selected(index);
568 var source_element = this.cell_elements().eq(i);
568 var source_element = this.cell_elements().eq(i);
569 var source_cell = source_element.data("cell");
569 var source_cell = source_element.data("cell");
570 var target_cell = null;
570 var target_cell = null;
571 if (source_cell instanceof IPython.CodeCell) {
571 if (source_cell instanceof IPython.CodeCell) {
572 this.insert_html_cell_below(i);
572 this.insert_html_cell_below(i);
573 var target_cell = this.cells()[i+1];
573 var target_cell = this.cells()[i+1];
574 var text = source_cell.get_code();
574 var text = source_cell.get_code();
575 } else if (source_cell instanceof IPython.MarkdownCell) {
575 } else if (source_cell instanceof IPython.MarkdownCell) {
576 this.insert_html_cell_below(i);
576 this.insert_html_cell_below(i);
577 var target_cell = this.cells()[i+1];
577 var target_cell = this.cells()[i+1];
578 var text = source_cell.get_source();
578 var text = source_cell.get_source();
579 if (text === source_cell.placeholder) {
579 if (text === source_cell.placeholder) {
580 text = target_cell.placeholder;
580 text = target_cell.placeholder;
581 }
581 }
582 }
582 }
583 if (target_cell !== null) {
583 if (target_cell !== null) {
584 if (text === "") {text = target_cell.placeholder;};
584 if (text === "") {text = target_cell.placeholder;};
585 target_cell.set_source(text);
585 target_cell.set_source(text);
586 source_element.remove();
586 source_element.remove();
587 target_cell.edit();
587 target_cell.edit();
588 }
588 }
589 this.dirty = true;
589 this.dirty = true;
590 };
590 };
591
591
592
592
593 // Cell collapsing and output clearing
593 // Cell collapsing and output clearing
594
594
595 Notebook.prototype.collapse = function (index) {
595 Notebook.prototype.collapse = function (index) {
596 var i = this.index_or_selected(index);
596 var i = this.index_or_selected(index);
597 this.cells()[i].collapse();
597 this.cells()[i].collapse();
598 this.dirty = true;
598 this.dirty = true;
599 };
599 };
600
600
601
601
602 Notebook.prototype.expand = function (index) {
602 Notebook.prototype.expand = function (index) {
603 var i = this.index_or_selected(index);
603 var i = this.index_or_selected(index);
604 this.cells()[i].expand();
604 this.cells()[i].expand();
605 this.dirty = true;
605 this.dirty = true;
606 };
606 };
607
607
608
608
609 Notebook.prototype.toggle_output = function (index) {
609 Notebook.prototype.toggle_output = function (index) {
610 var i = this.index_or_selected(index);
610 var i = this.index_or_selected(index);
611 this.cells()[i].toggle_output();
611 this.cells()[i].toggle_output();
612 this.dirty = true;
612 this.dirty = true;
613 };
613 };
614
614
615
615
616 Notebook.prototype.set_autoindent = function (state) {
616 Notebook.prototype.set_autoindent = function (state) {
617 var cells = this.cells();
617 var cells = this.cells();
618 len = cells.length;
618 len = cells.length;
619 for (var i=0; i<len; i++) {
619 for (var i=0; i<len; i++) {
620 cells[i].set_autoindent(state)
620 cells[i].set_autoindent(state)
621 };
621 };
622 };
622 };
623
623
624
624
625 Notebook.prototype.clear_all_output = function () {
625 Notebook.prototype.clear_all_output = function () {
626 var ncells = this.ncells();
626 var ncells = this.ncells();
627 var cells = this.cells();
627 var cells = this.cells();
628 for (var i=0; i<ncells; i++) {
628 for (var i=0; i<ncells; i++) {
629 if (cells[i] instanceof IPython.CodeCell) {
629 if (cells[i] instanceof IPython.CodeCell) {
630 cells[i].clear_output();
630 cells[i].clear_output();
631 }
631 }
632 };
632 };
633 this.dirty = true;
633 this.dirty = true;
634 };
634 };
635
635
636 // Other cell functions: line numbers, ...
636 // Other cell functions: line numbers, ...
637
637
638 Notebook.prototype.cell_toggle_line_numbers = function() {
638 Notebook.prototype.cell_toggle_line_numbers = function() {
639 this.selected_cell().toggle_line_numbers()
639 this.selected_cell().toggle_line_numbers()
640 };
640 };
641
641
642 // Kernel related things
642 // Kernel related things
643
643
644 Notebook.prototype.start_kernel = function () {
644 Notebook.prototype.start_kernel = function () {
645 this.kernel = new IPython.Kernel();
645 this.kernel = new IPython.Kernel();
646 var notebook_id = IPython.save_widget.get_notebook_id();
646 var notebook_id = IPython.save_widget.get_notebook_id();
647 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
647 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
648 };
648 };
649
649
650
650
651 Notebook.prototype.restart_kernel = function () {
651 Notebook.prototype.restart_kernel = function () {
652 var that = this;
652 var that = this;
653 var notebook_id = IPython.save_widget.get_notebook_id();
653 var notebook_id = IPython.save_widget.get_notebook_id();
654
654
655 var dialog = $('<div/>');
655 var dialog = $('<div/>');
656 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
656 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
657 $(document).append(dialog);
657 $(document).append(dialog);
658 dialog.dialog({
658 dialog.dialog({
659 resizable: false,
659 resizable: false,
660 modal: true,
660 modal: true,
661 title: "Restart kernel or continue running?",
661 title: "Restart kernel or continue running?",
662 buttons : {
662 buttons : {
663 "Restart": function () {
663 "Restart": function () {
664 that.kernel.restart($.proxy(that.kernel_started, that));
664 that.kernel.restart($.proxy(that.kernel_started, that));
665 $(this).dialog('close');
665 $(this).dialog('close');
666 },
666 },
667 "Continue running": function () {
667 "Continue running": function () {
668 $(this).dialog('close');
668 $(this).dialog('close');
669 }
669 }
670 }
670 }
671 });
671 });
672 };
672 };
673
673
674
674
675 Notebook.prototype.kernel_started = function () {
675 Notebook.prototype.kernel_started = function () {
676 console.log("Kernel started: ", this.kernel.kernel_id);
676 console.log("Kernel started: ", this.kernel.kernel_id);
677 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
677 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
678 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
678 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
679 };
679 };
680
680
681
681
682 Notebook.prototype.handle_shell_reply = function (e) {
682 Notebook.prototype.handle_shell_reply = function (e) {
683 reply = $.parseJSON(e.data);
683 reply = $.parseJSON(e.data);
684 var header = reply.header;
684 var header = reply.header;
685 var content = reply.content;
685 var content = reply.content;
686 var msg_type = header.msg_type;
686 var msg_type = header.msg_type;
687 // console.log(reply);
687 // console.log(reply);
688 var cell = this.cell_for_msg(reply.parent_header.msg_id);
688 var cell = this.cell_for_msg(reply.parent_header.msg_id);
689 if (msg_type === "execute_reply") {
689 if (msg_type === "execute_reply") {
690 cell.set_input_prompt(content.execution_count);
690 cell.set_input_prompt(content.execution_count);
691 this.dirty = true;
691 this.dirty = true;
692 } else if (msg_type === "complete_reply") {
692 } else if (msg_type === "complete_reply") {
693 cell.finish_completing(content.matched_text, content.matches);
693 cell.finish_completing(content.matched_text, content.matches);
694 };
694 };
695 var payload = content.payload || [];
695 var payload = content.payload || [];
696 this.handle_payload(cell, payload);
696 this.handle_payload(cell, payload);
697 };
697 };
698
698
699
699
700 Notebook.prototype.handle_payload = function (cell, payload) {
700 Notebook.prototype.handle_payload = function (cell, payload) {
701 var l = payload.length;
701 var l = payload.length;
702 for (var i=0; i<l; i++) {
702 for (var i=0; i<l; i++) {
703 if (payload[i].source === 'IPython.zmq.page.page') {
703 if (payload[i].source === 'IPython.zmq.page.page') {
704 if (payload[i].text.trim() !== '') {
704 if (payload[i].text.trim() !== '') {
705 IPython.pager.clear();
705 IPython.pager.clear();
706 IPython.pager.expand();
706 IPython.pager.expand();
707 IPython.pager.append_text(payload[i].text);
707 IPython.pager.append_text(payload[i].text);
708 }
708 }
709 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
709 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
710 var index = this.find_cell_index(cell);
710 var index = this.find_cell_index(cell);
711 var new_cell = this.insert_code_cell_below(index);
711 var new_cell = this.insert_code_cell_below(index);
712 new_cell.set_code(payload[i].text);
712 new_cell.set_code(payload[i].text);
713 this.dirty = true;
713 this.dirty = true;
714 }
714 }
715 };
715 };
716 };
716 };
717
717
718
718
719 Notebook.prototype.handle_iopub_reply = function (e) {
719 Notebook.prototype.handle_iopub_reply = function (e) {
720 reply = $.parseJSON(e.data);
720 reply = $.parseJSON(e.data);
721 var content = reply.content;
721 var content = reply.content;
722 // console.log(reply);
722 // console.log(reply);
723 var msg_type = reply.header.msg_type;
723 var msg_type = reply.header.msg_type;
724 var cell = this.cell_for_msg(reply.parent_header.msg_id);
724 var cell = this.cell_for_msg(reply.parent_header.msg_id);
725 var output_types = ['stream','display_data','pyout','pyerr'];
725 var output_types = ['stream','display_data','pyout','pyerr'];
726 if (output_types.indexOf(msg_type) >= 0) {
726 if (output_types.indexOf(msg_type) >= 0) {
727 this.handle_output(cell, msg_type, content);
727 this.handle_output(cell, msg_type, content);
728 } else if (msg_type === 'status') {
728 } else if (msg_type === 'status') {
729 if (content.execution_state === 'busy') {
729 if (content.execution_state === 'busy') {
730 IPython.kernel_status_widget.status_busy();
730 IPython.kernel_status_widget.status_busy();
731 } else if (content.execution_state === 'idle') {
731 } else if (content.execution_state === 'idle') {
732 IPython.kernel_status_widget.status_idle();
732 IPython.kernel_status_widget.status_idle();
733 } else if (content.execution_state === 'dead') {
733 } else if (content.execution_state === 'dead') {
734 this.handle_status_dead();
734 this.handle_status_dead();
735 };
735 };
736 }
736 } else if (msg_type === 'clear_output') {
737 cell.clear_output();
738 };
737 };
739 };
738
740
739
741
740 Notebook.prototype.handle_status_dead = function () {
742 Notebook.prototype.handle_status_dead = function () {
741 var that = this;
743 var that = this;
742 this.kernel.stop_channels();
744 this.kernel.stop_channels();
743 var dialog = $('<div/>');
745 var dialog = $('<div/>');
744 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
746 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
745 $(document).append(dialog);
747 $(document).append(dialog);
746 dialog.dialog({
748 dialog.dialog({
747 resizable: false,
749 resizable: false,
748 modal: true,
750 modal: true,
749 title: "Dead kernel",
751 title: "Dead kernel",
750 buttons : {
752 buttons : {
751 "Restart": function () {
753 "Restart": function () {
752 that.start_kernel();
754 that.start_kernel();
753 $(this).dialog('close');
755 $(this).dialog('close');
754 },
756 },
755 "Continue running": function () {
757 "Continue running": function () {
756 $(this).dialog('close');
758 $(this).dialog('close');
757 }
759 }
758 }
760 }
759 });
761 });
760 };
762 };
761
763
762
764
763 Notebook.prototype.handle_output = function (cell, msg_type, content) {
765 Notebook.prototype.handle_output = function (cell, msg_type, content) {
764 var json = {};
766 var json = {};
765 json.output_type = msg_type;
767 json.output_type = msg_type;
766 if (msg_type === "stream") {
768 if (msg_type === "stream") {
767 json.text = utils.fixConsole(content.data);
769 json.text = utils.fixConsole(content.data);
768 json.stream = content.name;
770 json.stream = content.name;
769 } else if (msg_type === "display_data") {
771 } else if (msg_type === "display_data") {
770 json = this.convert_mime_types(json, content.data);
772 json = this.convert_mime_types(json, content.data);
771 } else if (msg_type === "pyout") {
773 } else if (msg_type === "pyout") {
772 json.prompt_number = content.execution_count;
774 json.prompt_number = content.execution_count;
773 json = this.convert_mime_types(json, content.data);
775 json = this.convert_mime_types(json, content.data);
774 } else if (msg_type === "pyerr") {
776 } else if (msg_type === "pyerr") {
775 json.ename = content.ename;
777 json.ename = content.ename;
776 json.evalue = content.evalue;
778 json.evalue = content.evalue;
777 var traceback = [];
779 var traceback = [];
778 for (var i=0; i<content.traceback.length; i++) {
780 for (var i=0; i<content.traceback.length; i++) {
779 traceback.push(utils.fixConsole(content.traceback[i]));
781 traceback.push(utils.fixConsole(content.traceback[i]));
780 }
782 }
781 json.traceback = traceback;
783 json.traceback = traceback;
782 };
784 };
783 cell.append_output(json);
785 cell.append_output(json);
784 this.dirty = true;
786 this.dirty = true;
785 };
787 };
786
788
787
789
788 Notebook.prototype.convert_mime_types = function (json, data) {
790 Notebook.prototype.convert_mime_types = function (json, data) {
789 if (data['text/plain'] !== undefined) {
791 if (data['text/plain'] !== undefined) {
790 json.text = utils.fixConsole(data['text/plain']);
792 json.text = utils.fixConsole(data['text/plain']);
791 };
793 };
792 if (data['text/html'] !== undefined) {
794 if (data['text/html'] !== undefined) {
793 json.html = data['text/html'];
795 json.html = data['text/html'];
794 };
796 };
795 if (data['image/svg+xml'] !== undefined) {
797 if (data['image/svg+xml'] !== undefined) {
796 json.svg = data['image/svg+xml'];
798 json.svg = data['image/svg+xml'];
797 };
799 };
798 if (data['image/png'] !== undefined) {
800 if (data['image/png'] !== undefined) {
799 json.png = data['image/png'];
801 json.png = data['image/png'];
800 };
802 };
801 if (data['image/jpeg'] !== undefined) {
803 if (data['image/jpeg'] !== undefined) {
802 json.jpeg = data['image/jpeg'];
804 json.jpeg = data['image/jpeg'];
803 };
805 };
804 if (data['text/latex'] !== undefined) {
806 if (data['text/latex'] !== undefined) {
805 json.latex = data['text/latex'];
807 json.latex = data['text/latex'];
806 };
808 };
807 if (data['application/json'] !== undefined) {
809 if (data['application/json'] !== undefined) {
808 json.json = data['application/json'];
810 json.json = data['application/json'];
809 };
811 };
810 if (data['application/javascript'] !== undefined) {
812 if (data['application/javascript'] !== undefined) {
811 json.javascript = data['application/javascript'];
813 json.javascript = data['application/javascript'];
812 }
814 }
813 return json;
815 return json;
814 };
816 };
815
817
816
818
817 Notebook.prototype.execute_selected_cell = function (options) {
819 Notebook.prototype.execute_selected_cell = function (options) {
818 // add_new: should a new cell be added if we are at the end of the nb
820 // add_new: should a new cell be added if we are at the end of the nb
819 // terminal: execute in terminal mode, which stays in the current cell
821 // terminal: execute in terminal mode, which stays in the current cell
820 default_options = {terminal: false, add_new: true}
822 default_options = {terminal: false, add_new: true}
821 $.extend(default_options, options)
823 $.extend(default_options, options)
822 var that = this;
824 var that = this;
823 var cell = that.selected_cell();
825 var cell = that.selected_cell();
824 var cell_index = that.find_cell_index(cell);
826 var cell_index = that.find_cell_index(cell);
825 if (cell instanceof IPython.CodeCell) {
827 if (cell instanceof IPython.CodeCell) {
826 cell.clear_output();
828 cell.clear_output();
827 var code = cell.get_code();
829 var code = cell.get_code();
828 var msg_id = that.kernel.execute(cell.get_code());
830 var msg_id = that.kernel.execute(cell.get_code());
829 that.msg_cell_map[msg_id] = cell.cell_id;
831 that.msg_cell_map[msg_id] = cell.cell_id;
830 } else if (cell instanceof IPython.HTMLCell) {
832 } else if (cell instanceof IPython.HTMLCell) {
831 cell.render();
833 cell.render();
832 }
834 }
833 if (default_options.terminal) {
835 if (default_options.terminal) {
834 cell.select_all();
836 cell.select_all();
835 } else {
837 } else {
836 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
838 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
837 that.insert_code_cell_below();
839 that.insert_code_cell_below();
838 // If we are adding a new cell at the end, scroll down to show it.
840 // If we are adding a new cell at the end, scroll down to show it.
839 that.scroll_to_bottom();
841 that.scroll_to_bottom();
840 } else {
842 } else {
841 that.select(cell_index+1);
843 that.select(cell_index+1);
842 };
844 };
843 };
845 };
844 this.dirty = true;
846 this.dirty = true;
845 };
847 };
846
848
847
849
848 Notebook.prototype.execute_all_cells = function () {
850 Notebook.prototype.execute_all_cells = function () {
849 var ncells = this.ncells();
851 var ncells = this.ncells();
850 for (var i=0; i<ncells; i++) {
852 for (var i=0; i<ncells; i++) {
851 this.select(i);
853 this.select(i);
852 this.execute_selected_cell({add_new:false});
854 this.execute_selected_cell({add_new:false});
853 };
855 };
854 this.scroll_to_bottom();
856 this.scroll_to_bottom();
855 };
857 };
856
858
857
859
858 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
860 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
859 var msg_id = this.kernel.complete(line, cursor_pos);
861 var msg_id = this.kernel.complete(line, cursor_pos);
860 this.msg_cell_map[msg_id] = cell.cell_id;
862 this.msg_cell_map[msg_id] = cell.cell_id;
861 };
863 };
862
864
863 // Persistance and loading
865 // Persistance and loading
864
866
865
867
866 Notebook.prototype.fromJSON = function (data) {
868 Notebook.prototype.fromJSON = function (data) {
867 var ncells = this.ncells();
869 var ncells = this.ncells();
868 for (var i=0; i<ncells; i++) {
870 for (var i=0; i<ncells; i++) {
869 // Always delete cell 0 as they get renumbered as they are deleted.
871 // Always delete cell 0 as they get renumbered as they are deleted.
870 this.delete_cell(0);
872 this.delete_cell(0);
871 };
873 };
872 // Save the metadata
874 // Save the metadata
873 this.metadata = data.metadata;
875 this.metadata = data.metadata;
874 // Only handle 1 worksheet for now.
876 // Only handle 1 worksheet for now.
875 var worksheet = data.worksheets[0];
877 var worksheet = data.worksheets[0];
876 if (worksheet !== undefined) {
878 if (worksheet !== undefined) {
877 var new_cells = worksheet.cells;
879 var new_cells = worksheet.cells;
878 ncells = new_cells.length;
880 ncells = new_cells.length;
879 var cell_data = null;
881 var cell_data = null;
880 var new_cell = null;
882 var new_cell = null;
881 for (var i=0; i<ncells; i++) {
883 for (var i=0; i<ncells; i++) {
882 cell_data = new_cells[i];
884 cell_data = new_cells[i];
883 if (cell_data.cell_type == 'code') {
885 if (cell_data.cell_type == 'code') {
884 new_cell = this.insert_code_cell_below();
886 new_cell = this.insert_code_cell_below();
885 new_cell.fromJSON(cell_data);
887 new_cell.fromJSON(cell_data);
886 } else if (cell_data.cell_type === 'html') {
888 } else if (cell_data.cell_type === 'html') {
887 new_cell = this.insert_html_cell_below();
889 new_cell = this.insert_html_cell_below();
888 new_cell.fromJSON(cell_data);
890 new_cell.fromJSON(cell_data);
889 } else if (cell_data.cell_type === 'markdown') {
891 } else if (cell_data.cell_type === 'markdown') {
890 new_cell = this.insert_markdown_cell_below();
892 new_cell = this.insert_markdown_cell_below();
891 new_cell.fromJSON(cell_data);
893 new_cell.fromJSON(cell_data);
892 };
894 };
893 };
895 };
894 };
896 };
895 };
897 };
896
898
897
899
898 Notebook.prototype.toJSON = function () {
900 Notebook.prototype.toJSON = function () {
899 var cells = this.cells();
901 var cells = this.cells();
900 var ncells = cells.length;
902 var ncells = cells.length;
901 cell_array = new Array(ncells);
903 cell_array = new Array(ncells);
902 for (var i=0; i<ncells; i++) {
904 for (var i=0; i<ncells; i++) {
903 cell_array[i] = cells[i].toJSON();
905 cell_array[i] = cells[i].toJSON();
904 };
906 };
905 data = {
907 data = {
906 // Only handle 1 worksheet for now.
908 // Only handle 1 worksheet for now.
907 worksheets : [{cells:cell_array}],
909 worksheets : [{cells:cell_array}],
908 metadata : this.metadata
910 metadata : this.metadata
909 }
911 }
910 return data
912 return data
911 };
913 };
912
914
913 Notebook.prototype.save_notebook = function () {
915 Notebook.prototype.save_notebook = function () {
914 if (IPython.save_widget.test_notebook_name()) {
916 if (IPython.save_widget.test_notebook_name()) {
915 var notebook_id = IPython.save_widget.get_notebook_id();
917 var notebook_id = IPython.save_widget.get_notebook_id();
916 var nbname = IPython.save_widget.get_notebook_name();
918 var nbname = IPython.save_widget.get_notebook_name();
917 // We may want to move the name/id/nbformat logic inside toJSON?
919 // We may want to move the name/id/nbformat logic inside toJSON?
918 var data = this.toJSON();
920 var data = this.toJSON();
919 data.metadata.name = nbname;
921 data.metadata.name = nbname;
920 data.nbformat = 2;
922 data.nbformat = 2;
921 // We do the call with settings so we can set cache to false.
923 // We do the call with settings so we can set cache to false.
922 var settings = {
924 var settings = {
923 processData : false,
925 processData : false,
924 cache : false,
926 cache : false,
925 type : "PUT",
927 type : "PUT",
926 data : JSON.stringify(data),
928 data : JSON.stringify(data),
927 headers : {'Content-Type': 'application/json'},
929 headers : {'Content-Type': 'application/json'},
928 success : $.proxy(this.notebook_saved,this),
930 success : $.proxy(this.notebook_saved,this),
929 error : $.proxy(this.notebook_save_failed,this)
931 error : $.proxy(this.notebook_save_failed,this)
930 };
932 };
931 IPython.save_widget.status_saving();
933 IPython.save_widget.status_saving();
932 $.ajax("/notebooks/" + notebook_id, settings);
934 $.ajax("/notebooks/" + notebook_id, settings);
933 };
935 };
934 };
936 };
935
937
936
938
937 Notebook.prototype.notebook_saved = function (data, status, xhr) {
939 Notebook.prototype.notebook_saved = function (data, status, xhr) {
938 this.dirty = false;
940 this.dirty = false;
939 IPython.save_widget.notebook_saved();
941 IPython.save_widget.notebook_saved();
940 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
942 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
941 }
943 }
942
944
943
945
944 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
946 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
945 // Notify the user and reset the save button
947 // Notify the user and reset the save button
946 // TODO: Handle different types of errors (timeout etc.)
948 // TODO: Handle different types of errors (timeout etc.)
947 alert('An unexpected error occured while saving the notebook.');
949 alert('An unexpected error occured while saving the notebook.');
948 setTimeout($.proxy(IPython.save_widget.reset_status,IPython.save_widget),500);
950 setTimeout($.proxy(IPython.save_widget.reset_status,IPython.save_widget),500);
949 }
951 }
950
952
951
953
952 Notebook.prototype.load_notebook = function (callback) {
954 Notebook.prototype.load_notebook = function (callback) {
953 var that = this;
955 var that = this;
954 var notebook_id = IPython.save_widget.get_notebook_id();
956 var notebook_id = IPython.save_widget.get_notebook_id();
955 // We do the call with settings so we can set cache to false.
957 // We do the call with settings so we can set cache to false.
956 var settings = {
958 var settings = {
957 processData : false,
959 processData : false,
958 cache : false,
960 cache : false,
959 type : "GET",
961 type : "GET",
960 dataType : "json",
962 dataType : "json",
961 success : function (data, status, xhr) {
963 success : function (data, status, xhr) {
962 that.notebook_loaded(data, status, xhr);
964 that.notebook_loaded(data, status, xhr);
963 if (callback !== undefined) {
965 if (callback !== undefined) {
964 callback();
966 callback();
965 };
967 };
966 }
968 }
967 };
969 };
968 IPython.save_widget.status_loading();
970 IPython.save_widget.status_loading();
969 $.ajax("/notebooks/" + notebook_id, settings);
971 $.ajax("/notebooks/" + notebook_id, settings);
970 }
972 }
971
973
972
974
973 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
975 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
974 this.fromJSON(data);
976 this.fromJSON(data);
975 if (this.ncells() === 0) {
977 if (this.ncells() === 0) {
976 this.insert_code_cell_below();
978 this.insert_code_cell_below();
977 };
979 };
978 IPython.save_widget.status_save();
980 IPython.save_widget.status_save();
979 IPython.save_widget.set_notebook_name(data.metadata.name);
981 IPython.save_widget.set_notebook_name(data.metadata.name);
980 this.start_kernel();
982 this.start_kernel();
981 this.dirty = false;
983 this.dirty = false;
982 // fromJSON always selects the last cell inserted. We need to wait
984 // fromJSON always selects the last cell inserted. We need to wait
983 // until that is done before scrolling to the top.
985 // until that is done before scrolling to the top.
984 setTimeout(function () {
986 setTimeout(function () {
985 IPython.notebook.select(0);
987 IPython.notebook.select(0);
986 IPython.notebook.scroll_to_top();
988 IPython.notebook.scroll_to_top();
987 }, 50);
989 }, 50);
988 };
990 };
989
991
990 IPython.Notebook = Notebook;
992 IPython.Notebook = Notebook;
991
993
992 return IPython;
994 return IPython;
993
995
994 }(IPython));
996 }(IPython));
995
997
@@ -1,489 +1,494 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import inspect
19 import inspect
20 import os
20 import os
21 import sys
21 import sys
22 from subprocess import Popen, PIPE
22 from subprocess import Popen, PIPE
23
23
24 # Our own
24 # Our own
25 from IPython.core.interactiveshell import (
25 from IPython.core.interactiveshell import (
26 InteractiveShell, InteractiveShellABC
26 InteractiveShell, InteractiveShellABC
27 )
27 )
28 from IPython.core import page
28 from IPython.core import page
29 from IPython.core.autocall import ZMQExitAutocall
29 from IPython.core.autocall import ZMQExitAutocall
30 from IPython.core.displaypub import DisplayPublisher
30 from IPython.core.displaypub import DisplayPublisher
31 from IPython.core.macro import Macro
31 from IPython.core.macro import Macro
32 from IPython.core.magic import MacroToEdit
32 from IPython.core.magic import MacroToEdit
33 from IPython.core.payloadpage import install_payload_page
33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib.kernel import (
34 from IPython.lib.kernel import (
35 get_connection_file, get_connection_info, connect_qtconsole
35 get_connection_file, get_connection_info, connect_qtconsole
36 )
36 )
37 from IPython.utils import io
37 from IPython.utils import io
38 from IPython.utils.jsonutil import json_clean
38 from IPython.utils.jsonutil import json_clean
39 from IPython.utils.path import get_py_filename
39 from IPython.utils.path import get_py_filename
40 from IPython.utils.process import arg_split
40 from IPython.utils.process import arg_split
41 from IPython.utils.traitlets import Instance, Type, Dict, CBool
41 from IPython.utils.traitlets import Instance, Type, Dict, CBool
42 from IPython.utils.warn import warn, error
42 from IPython.utils.warn import warn, error
43 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
43 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
44 from IPython.zmq.session import extract_header
44 from IPython.zmq.session import extract_header
45 from session import Session
45 from session import Session
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Globals and side-effects
48 # Globals and side-effects
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 # Install the payload version of page.
51 # Install the payload version of page.
52 install_payload_page()
52 install_payload_page()
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Functions and classes
55 # Functions and classes
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 class ZMQDisplayPublisher(DisplayPublisher):
58 class ZMQDisplayPublisher(DisplayPublisher):
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60
60
61 session = Instance(Session)
61 session = Instance(Session)
62 pub_socket = Instance('zmq.Socket')
62 pub_socket = Instance('zmq.Socket')
63 parent_header = Dict({})
63 parent_header = Dict({})
64
64
65 def set_parent(self, parent):
65 def set_parent(self, parent):
66 """Set the parent for outbound messages."""
66 """Set the parent for outbound messages."""
67 self.parent_header = extract_header(parent)
67 self.parent_header = extract_header(parent)
68
68
69 def publish(self, source, data, metadata=None):
69 def publish(self, source, data, metadata=None):
70 if metadata is None:
70 if metadata is None:
71 metadata = {}
71 metadata = {}
72 self._validate_data(source, data, metadata)
72 self._validate_data(source, data, metadata)
73 content = {}
73 content = {}
74 content['source'] = source
74 content['source'] = source
75 _encode_binary(data)
75 _encode_binary(data)
76 content['data'] = data
76 content['data'] = data
77 content['metadata'] = metadata
77 content['metadata'] = metadata
78 self.session.send(
78 self.session.send(
79 self.pub_socket, u'display_data', json_clean(content),
79 self.pub_socket, u'display_data', json_clean(content),
80 parent=self.parent_header
80 parent=self.parent_header
81 )
81 )
82
82
83 def clear_output(self):
84 self.session.send(
85 self.pub_socket, u'clear_output', {},
86 parent=self.parent_header
87 )
83
88
84 class ZMQInteractiveShell(InteractiveShell):
89 class ZMQInteractiveShell(InteractiveShell):
85 """A subclass of InteractiveShell for ZMQ."""
90 """A subclass of InteractiveShell for ZMQ."""
86
91
87 displayhook_class = Type(ZMQShellDisplayHook)
92 displayhook_class = Type(ZMQShellDisplayHook)
88 display_pub_class = Type(ZMQDisplayPublisher)
93 display_pub_class = Type(ZMQDisplayPublisher)
89
94
90 # Override the traitlet in the parent class, because there's no point using
95 # Override the traitlet in the parent class, because there's no point using
91 # readline for the kernel. Can be removed when the readline code is moved
96 # readline for the kernel. Can be removed when the readline code is moved
92 # to the terminal frontend.
97 # to the terminal frontend.
93 colors_force = CBool(True)
98 colors_force = CBool(True)
94 readline_use = CBool(False)
99 readline_use = CBool(False)
95 # autoindent has no meaning in a zmqshell, and attempting to enable it
100 # autoindent has no meaning in a zmqshell, and attempting to enable it
96 # will print a warning in the absence of readline.
101 # will print a warning in the absence of readline.
97 autoindent = CBool(False)
102 autoindent = CBool(False)
98
103
99 exiter = Instance(ZMQExitAutocall)
104 exiter = Instance(ZMQExitAutocall)
100 def _exiter_default(self):
105 def _exiter_default(self):
101 return ZMQExitAutocall(self)
106 return ZMQExitAutocall(self)
102
107
103 keepkernel_on_exit = None
108 keepkernel_on_exit = None
104
109
105 def init_environment(self):
110 def init_environment(self):
106 """Configure the user's environment.
111 """Configure the user's environment.
107
112
108 """
113 """
109 env = os.environ
114 env = os.environ
110 # These two ensure 'ls' produces nice coloring on BSD-derived systems
115 # These two ensure 'ls' produces nice coloring on BSD-derived systems
111 env['TERM'] = 'xterm-color'
116 env['TERM'] = 'xterm-color'
112 env['CLICOLOR'] = '1'
117 env['CLICOLOR'] = '1'
113 # Since normal pagers don't work at all (over pexpect we don't have
118 # Since normal pagers don't work at all (over pexpect we don't have
114 # single-key control of the subprocess), try to disable paging in
119 # single-key control of the subprocess), try to disable paging in
115 # subprocesses as much as possible.
120 # subprocesses as much as possible.
116 env['PAGER'] = 'cat'
121 env['PAGER'] = 'cat'
117 env['GIT_PAGER'] = 'cat'
122 env['GIT_PAGER'] = 'cat'
118
123
119 def auto_rewrite_input(self, cmd):
124 def auto_rewrite_input(self, cmd):
120 """Called to show the auto-rewritten input for autocall and friends.
125 """Called to show the auto-rewritten input for autocall and friends.
121
126
122 FIXME: this payload is currently not correctly processed by the
127 FIXME: this payload is currently not correctly processed by the
123 frontend.
128 frontend.
124 """
129 """
125 new = self.displayhook.prompt1.auto_rewrite() + cmd
130 new = self.displayhook.prompt1.auto_rewrite() + cmd
126 payload = dict(
131 payload = dict(
127 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
132 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
128 transformed_input=new,
133 transformed_input=new,
129 )
134 )
130 self.payload_manager.write_payload(payload)
135 self.payload_manager.write_payload(payload)
131
136
132 def ask_exit(self):
137 def ask_exit(self):
133 """Engage the exit actions."""
138 """Engage the exit actions."""
134 payload = dict(
139 payload = dict(
135 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
140 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
136 exit=True,
141 exit=True,
137 keepkernel=self.keepkernel_on_exit,
142 keepkernel=self.keepkernel_on_exit,
138 )
143 )
139 self.payload_manager.write_payload(payload)
144 self.payload_manager.write_payload(payload)
140
145
141 def _showtraceback(self, etype, evalue, stb):
146 def _showtraceback(self, etype, evalue, stb):
142
147
143 exc_content = {
148 exc_content = {
144 u'traceback' : stb,
149 u'traceback' : stb,
145 u'ename' : unicode(etype.__name__),
150 u'ename' : unicode(etype.__name__),
146 u'evalue' : unicode(evalue)
151 u'evalue' : unicode(evalue)
147 }
152 }
148
153
149 dh = self.displayhook
154 dh = self.displayhook
150 # Send exception info over pub socket for other clients than the caller
155 # Send exception info over pub socket for other clients than the caller
151 # to pick up
156 # to pick up
152 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
157 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
153
158
154 # FIXME - Hack: store exception info in shell object. Right now, the
159 # FIXME - Hack: store exception info in shell object. Right now, the
155 # caller is reading this info after the fact, we need to fix this logic
160 # caller is reading this info after the fact, we need to fix this logic
156 # to remove this hack. Even uglier, we need to store the error status
161 # to remove this hack. Even uglier, we need to store the error status
157 # here, because in the main loop, the logic that sets it is being
162 # here, because in the main loop, the logic that sets it is being
158 # skipped because runlines swallows the exceptions.
163 # skipped because runlines swallows the exceptions.
159 exc_content[u'status'] = u'error'
164 exc_content[u'status'] = u'error'
160 self._reply_content = exc_content
165 self._reply_content = exc_content
161 # /FIXME
166 # /FIXME
162
167
163 return exc_content
168 return exc_content
164
169
165 #------------------------------------------------------------------------
170 #------------------------------------------------------------------------
166 # Magic overrides
171 # Magic overrides
167 #------------------------------------------------------------------------
172 #------------------------------------------------------------------------
168 # Once the base class stops inheriting from magic, this code needs to be
173 # Once the base class stops inheriting from magic, this code needs to be
169 # moved into a separate machinery as well. For now, at least isolate here
174 # moved into a separate machinery as well. For now, at least isolate here
170 # the magics which this class needs to implement differently from the base
175 # the magics which this class needs to implement differently from the base
171 # class, or that are unique to it.
176 # class, or that are unique to it.
172
177
173 def magic_doctest_mode(self,parameter_s=''):
178 def magic_doctest_mode(self,parameter_s=''):
174 """Toggle doctest mode on and off.
179 """Toggle doctest mode on and off.
175
180
176 This mode is intended to make IPython behave as much as possible like a
181 This mode is intended to make IPython behave as much as possible like a
177 plain Python shell, from the perspective of how its prompts, exceptions
182 plain Python shell, from the perspective of how its prompts, exceptions
178 and output look. This makes it easy to copy and paste parts of a
183 and output look. This makes it easy to copy and paste parts of a
179 session into doctests. It does so by:
184 session into doctests. It does so by:
180
185
181 - Changing the prompts to the classic ``>>>`` ones.
186 - Changing the prompts to the classic ``>>>`` ones.
182 - Changing the exception reporting mode to 'Plain'.
187 - Changing the exception reporting mode to 'Plain'.
183 - Disabling pretty-printing of output.
188 - Disabling pretty-printing of output.
184
189
185 Note that IPython also supports the pasting of code snippets that have
190 Note that IPython also supports the pasting of code snippets that have
186 leading '>>>' and '...' prompts in them. This means that you can paste
191 leading '>>>' and '...' prompts in them. This means that you can paste
187 doctests from files or docstrings (even if they have leading
192 doctests from files or docstrings (even if they have leading
188 whitespace), and the code will execute correctly. You can then use
193 whitespace), and the code will execute correctly. You can then use
189 '%history -t' to see the translated history; this will give you the
194 '%history -t' to see the translated history; this will give you the
190 input after removal of all the leading prompts and whitespace, which
195 input after removal of all the leading prompts and whitespace, which
191 can be pasted back into an editor.
196 can be pasted back into an editor.
192
197
193 With these features, you can switch into this mode easily whenever you
198 With these features, you can switch into this mode easily whenever you
194 need to do testing and changes to doctests, without having to leave
199 need to do testing and changes to doctests, without having to leave
195 your existing IPython session.
200 your existing IPython session.
196 """
201 """
197
202
198 from IPython.utils.ipstruct import Struct
203 from IPython.utils.ipstruct import Struct
199
204
200 # Shorthands
205 # Shorthands
201 shell = self.shell
206 shell = self.shell
202 disp_formatter = self.shell.display_formatter
207 disp_formatter = self.shell.display_formatter
203 ptformatter = disp_formatter.formatters['text/plain']
208 ptformatter = disp_formatter.formatters['text/plain']
204 # dstore is a data store kept in the instance metadata bag to track any
209 # dstore is a data store kept in the instance metadata bag to track any
205 # changes we make, so we can undo them later.
210 # changes we make, so we can undo them later.
206 dstore = shell.meta.setdefault('doctest_mode', Struct())
211 dstore = shell.meta.setdefault('doctest_mode', Struct())
207 save_dstore = dstore.setdefault
212 save_dstore = dstore.setdefault
208
213
209 # save a few values we'll need to recover later
214 # save a few values we'll need to recover later
210 mode = save_dstore('mode', False)
215 mode = save_dstore('mode', False)
211 save_dstore('rc_pprint', ptformatter.pprint)
216 save_dstore('rc_pprint', ptformatter.pprint)
212 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
217 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
213 save_dstore('xmode', shell.InteractiveTB.mode)
218 save_dstore('xmode', shell.InteractiveTB.mode)
214
219
215 if mode == False:
220 if mode == False:
216 # turn on
221 # turn on
217 ptformatter.pprint = False
222 ptformatter.pprint = False
218 disp_formatter.plain_text_only = True
223 disp_formatter.plain_text_only = True
219 shell.magic_xmode('Plain')
224 shell.magic_xmode('Plain')
220 else:
225 else:
221 # turn off
226 # turn off
222 ptformatter.pprint = dstore.rc_pprint
227 ptformatter.pprint = dstore.rc_pprint
223 disp_formatter.plain_text_only = dstore.rc_plain_text_only
228 disp_formatter.plain_text_only = dstore.rc_plain_text_only
224 shell.magic_xmode(dstore.xmode)
229 shell.magic_xmode(dstore.xmode)
225
230
226 # Store new mode and inform on console
231 # Store new mode and inform on console
227 dstore.mode = bool(1-int(mode))
232 dstore.mode = bool(1-int(mode))
228 mode_label = ['OFF','ON'][dstore.mode]
233 mode_label = ['OFF','ON'][dstore.mode]
229 print('Doctest mode is:', mode_label)
234 print('Doctest mode is:', mode_label)
230
235
231 # Send the payload back so that clients can modify their prompt display
236 # Send the payload back so that clients can modify their prompt display
232 payload = dict(
237 payload = dict(
233 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
238 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
234 mode=dstore.mode)
239 mode=dstore.mode)
235 self.payload_manager.write_payload(payload)
240 self.payload_manager.write_payload(payload)
236
241
237 def magic_edit(self,parameter_s='',last_call=['','']):
242 def magic_edit(self,parameter_s='',last_call=['','']):
238 """Bring up an editor and execute the resulting code.
243 """Bring up an editor and execute the resulting code.
239
244
240 Usage:
245 Usage:
241 %edit [options] [args]
246 %edit [options] [args]
242
247
243 %edit runs an external text editor. You will need to set the command for
248 %edit runs an external text editor. You will need to set the command for
244 this editor via the ``TerminalInteractiveShell.editor`` option in your
249 this editor via the ``TerminalInteractiveShell.editor`` option in your
245 configuration file before it will work.
250 configuration file before it will work.
246
251
247 This command allows you to conveniently edit multi-line code right in
252 This command allows you to conveniently edit multi-line code right in
248 your IPython session.
253 your IPython session.
249
254
250 If called without arguments, %edit opens up an empty editor with a
255 If called without arguments, %edit opens up an empty editor with a
251 temporary file and will execute the contents of this file when you
256 temporary file and will execute the contents of this file when you
252 close it (don't forget to save it!).
257 close it (don't forget to save it!).
253
258
254
259
255 Options:
260 Options:
256
261
257 -n <number>: open the editor at a specified line number. By default,
262 -n <number>: open the editor at a specified line number. By default,
258 the IPython editor hook uses the unix syntax 'editor +N filename', but
263 the IPython editor hook uses the unix syntax 'editor +N filename', but
259 you can configure this by providing your own modified hook if your
264 you can configure this by providing your own modified hook if your
260 favorite editor supports line-number specifications with a different
265 favorite editor supports line-number specifications with a different
261 syntax.
266 syntax.
262
267
263 -p: this will call the editor with the same data as the previous time
268 -p: this will call the editor with the same data as the previous time
264 it was used, regardless of how long ago (in your current session) it
269 it was used, regardless of how long ago (in your current session) it
265 was.
270 was.
266
271
267 -r: use 'raw' input. This option only applies to input taken from the
272 -r: use 'raw' input. This option only applies to input taken from the
268 user's history. By default, the 'processed' history is used, so that
273 user's history. By default, the 'processed' history is used, so that
269 magics are loaded in their transformed version to valid Python. If
274 magics are loaded in their transformed version to valid Python. If
270 this option is given, the raw input as typed as the command line is
275 this option is given, the raw input as typed as the command line is
271 used instead. When you exit the editor, it will be executed by
276 used instead. When you exit the editor, it will be executed by
272 IPython's own processor.
277 IPython's own processor.
273
278
274 -x: do not execute the edited code immediately upon exit. This is
279 -x: do not execute the edited code immediately upon exit. This is
275 mainly useful if you are editing programs which need to be called with
280 mainly useful if you are editing programs which need to be called with
276 command line arguments, which you can then do using %run.
281 command line arguments, which you can then do using %run.
277
282
278
283
279 Arguments:
284 Arguments:
280
285
281 If arguments are given, the following possibilites exist:
286 If arguments are given, the following possibilites exist:
282
287
283 - The arguments are numbers or pairs of colon-separated numbers (like
288 - The arguments are numbers or pairs of colon-separated numbers (like
284 1 4:8 9). These are interpreted as lines of previous input to be
289 1 4:8 9). These are interpreted as lines of previous input to be
285 loaded into the editor. The syntax is the same of the %macro command.
290 loaded into the editor. The syntax is the same of the %macro command.
286
291
287 - If the argument doesn't start with a number, it is evaluated as a
292 - If the argument doesn't start with a number, it is evaluated as a
288 variable and its contents loaded into the editor. You can thus edit
293 variable and its contents loaded into the editor. You can thus edit
289 any string which contains python code (including the result of
294 any string which contains python code (including the result of
290 previous edits).
295 previous edits).
291
296
292 - If the argument is the name of an object (other than a string),
297 - If the argument is the name of an object (other than a string),
293 IPython will try to locate the file where it was defined and open the
298 IPython will try to locate the file where it was defined and open the
294 editor at the point where it is defined. You can use `%edit function`
299 editor at the point where it is defined. You can use `%edit function`
295 to load an editor exactly at the point where 'function' is defined,
300 to load an editor exactly at the point where 'function' is defined,
296 edit it and have the file be executed automatically.
301 edit it and have the file be executed automatically.
297
302
298 If the object is a macro (see %macro for details), this opens up your
303 If the object is a macro (see %macro for details), this opens up your
299 specified editor with a temporary file containing the macro's data.
304 specified editor with a temporary file containing the macro's data.
300 Upon exit, the macro is reloaded with the contents of the file.
305 Upon exit, the macro is reloaded with the contents of the file.
301
306
302 Note: opening at an exact line is only supported under Unix, and some
307 Note: opening at an exact line is only supported under Unix, and some
303 editors (like kedit and gedit up to Gnome 2.8) do not understand the
308 editors (like kedit and gedit up to Gnome 2.8) do not understand the
304 '+NUMBER' parameter necessary for this feature. Good editors like
309 '+NUMBER' parameter necessary for this feature. Good editors like
305 (X)Emacs, vi, jed, pico and joe all do.
310 (X)Emacs, vi, jed, pico and joe all do.
306
311
307 - If the argument is not found as a variable, IPython will look for a
312 - If the argument is not found as a variable, IPython will look for a
308 file with that name (adding .py if necessary) and load it into the
313 file with that name (adding .py if necessary) and load it into the
309 editor. It will execute its contents with execfile() when you exit,
314 editor. It will execute its contents with execfile() when you exit,
310 loading any code in the file into your interactive namespace.
315 loading any code in the file into your interactive namespace.
311
316
312 After executing your code, %edit will return as output the code you
317 After executing your code, %edit will return as output the code you
313 typed in the editor (except when it was an existing file). This way
318 typed in the editor (except when it was an existing file). This way
314 you can reload the code in further invocations of %edit as a variable,
319 you can reload the code in further invocations of %edit as a variable,
315 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
320 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
316 the output.
321 the output.
317
322
318 Note that %edit is also available through the alias %ed.
323 Note that %edit is also available through the alias %ed.
319
324
320 This is an example of creating a simple function inside the editor and
325 This is an example of creating a simple function inside the editor and
321 then modifying it. First, start up the editor:
326 then modifying it. First, start up the editor:
322
327
323 In [1]: ed
328 In [1]: ed
324 Editing... done. Executing edited code...
329 Editing... done. Executing edited code...
325 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
330 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
326
331
327 We can then call the function foo():
332 We can then call the function foo():
328
333
329 In [2]: foo()
334 In [2]: foo()
330 foo() was defined in an editing session
335 foo() was defined in an editing session
331
336
332 Now we edit foo. IPython automatically loads the editor with the
337 Now we edit foo. IPython automatically loads the editor with the
333 (temporary) file where foo() was previously defined:
338 (temporary) file where foo() was previously defined:
334
339
335 In [3]: ed foo
340 In [3]: ed foo
336 Editing... done. Executing edited code...
341 Editing... done. Executing edited code...
337
342
338 And if we call foo() again we get the modified version:
343 And if we call foo() again we get the modified version:
339
344
340 In [4]: foo()
345 In [4]: foo()
341 foo() has now been changed!
346 foo() has now been changed!
342
347
343 Here is an example of how to edit a code snippet successive
348 Here is an example of how to edit a code snippet successive
344 times. First we call the editor:
349 times. First we call the editor:
345
350
346 In [5]: ed
351 In [5]: ed
347 Editing... done. Executing edited code...
352 Editing... done. Executing edited code...
348 hello
353 hello
349 Out[5]: "print 'hello'n"
354 Out[5]: "print 'hello'n"
350
355
351 Now we call it again with the previous output (stored in _):
356 Now we call it again with the previous output (stored in _):
352
357
353 In [6]: ed _
358 In [6]: ed _
354 Editing... done. Executing edited code...
359 Editing... done. Executing edited code...
355 hello world
360 hello world
356 Out[6]: "print 'hello world'n"
361 Out[6]: "print 'hello world'n"
357
362
358 Now we call it with the output #8 (stored in _8, also as Out[8]):
363 Now we call it with the output #8 (stored in _8, also as Out[8]):
359
364
360 In [7]: ed _8
365 In [7]: ed _8
361 Editing... done. Executing edited code...
366 Editing... done. Executing edited code...
362 hello again
367 hello again
363 Out[7]: "print 'hello again'n"
368 Out[7]: "print 'hello again'n"
364 """
369 """
365
370
366 opts,args = self.parse_options(parameter_s,'prn:')
371 opts,args = self.parse_options(parameter_s,'prn:')
367
372
368 try:
373 try:
369 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
374 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
370 except MacroToEdit as e:
375 except MacroToEdit as e:
371 # TODO: Implement macro editing over 2 processes.
376 # TODO: Implement macro editing over 2 processes.
372 print("Macro editing not yet implemented in 2-process model.")
377 print("Macro editing not yet implemented in 2-process model.")
373 return
378 return
374
379
375 # Make sure we send to the client an absolute path, in case the working
380 # Make sure we send to the client an absolute path, in case the working
376 # directory of client and kernel don't match
381 # directory of client and kernel don't match
377 filename = os.path.abspath(filename)
382 filename = os.path.abspath(filename)
378
383
379 payload = {
384 payload = {
380 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
385 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
381 'filename' : filename,
386 'filename' : filename,
382 'line_number' : lineno
387 'line_number' : lineno
383 }
388 }
384 self.payload_manager.write_payload(payload)
389 self.payload_manager.write_payload(payload)
385
390
386 def magic_gui(self, *args, **kwargs):
391 def magic_gui(self, *args, **kwargs):
387 raise NotImplementedError(
392 raise NotImplementedError(
388 'Kernel GUI support is not implemented yet, except for --pylab.')
393 'Kernel GUI support is not implemented yet, except for --pylab.')
389
394
390 def magic_pylab(self, *args, **kwargs):
395 def magic_pylab(self, *args, **kwargs):
391 raise NotImplementedError(
396 raise NotImplementedError(
392 'pylab support must be enabled in command line options.')
397 'pylab support must be enabled in command line options.')
393
398
394 # A few magics that are adapted to the specifics of using pexpect and a
399 # A few magics that are adapted to the specifics of using pexpect and a
395 # remote terminal
400 # remote terminal
396
401
397 def magic_clear(self, arg_s):
402 def magic_clear(self, arg_s):
398 """Clear the terminal."""
403 """Clear the terminal."""
399 if os.name == 'posix':
404 if os.name == 'posix':
400 self.shell.system("clear")
405 self.shell.system("clear")
401 else:
406 else:
402 self.shell.system("cls")
407 self.shell.system("cls")
403
408
404 if os.name == 'nt':
409 if os.name == 'nt':
405 # This is the usual name in windows
410 # This is the usual name in windows
406 magic_cls = magic_clear
411 magic_cls = magic_clear
407
412
408 # Terminal pagers won't work over pexpect, but we do have our own pager
413 # Terminal pagers won't work over pexpect, but we do have our own pager
409
414
410 def magic_less(self, arg_s):
415 def magic_less(self, arg_s):
411 """Show a file through the pager.
416 """Show a file through the pager.
412
417
413 Files ending in .py are syntax-highlighted."""
418 Files ending in .py are syntax-highlighted."""
414 cont = open(arg_s).read()
419 cont = open(arg_s).read()
415 if arg_s.endswith('.py'):
420 if arg_s.endswith('.py'):
416 cont = self.shell.pycolorize(cont)
421 cont = self.shell.pycolorize(cont)
417 page.page(cont)
422 page.page(cont)
418
423
419 magic_more = magic_less
424 magic_more = magic_less
420
425
421 # Man calls a pager, so we also need to redefine it
426 # Man calls a pager, so we also need to redefine it
422 if os.name == 'posix':
427 if os.name == 'posix':
423 def magic_man(self, arg_s):
428 def magic_man(self, arg_s):
424 """Find the man page for the given command and display in pager."""
429 """Find the man page for the given command and display in pager."""
425 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
430 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
426 split=False))
431 split=False))
427
432
428 # FIXME: this is specific to the GUI, so we should let the gui app load
433 # FIXME: this is specific to the GUI, so we should let the gui app load
429 # magics at startup that are only for the gui. Once the gui app has proper
434 # magics at startup that are only for the gui. Once the gui app has proper
430 # profile and configuration management, we can have it initialize a kernel
435 # profile and configuration management, we can have it initialize a kernel
431 # with a special config file that provides these.
436 # with a special config file that provides these.
432 def magic_guiref(self, arg_s):
437 def magic_guiref(self, arg_s):
433 """Show a basic reference about the GUI console."""
438 """Show a basic reference about the GUI console."""
434 from IPython.core.usage import gui_reference
439 from IPython.core.usage import gui_reference
435 page.page(gui_reference, auto_html=True)
440 page.page(gui_reference, auto_html=True)
436
441
437 def magic_connect_info(self, arg_s):
442 def magic_connect_info(self, arg_s):
438 """Print information for connecting other clients to this kernel
443 """Print information for connecting other clients to this kernel
439
444
440 It will print the contents of this session's connection file, as well as
445 It will print the contents of this session's connection file, as well as
441 shortcuts for local clients.
446 shortcuts for local clients.
442
447
443 In the simplest case, when called from the most recently launched kernel,
448 In the simplest case, when called from the most recently launched kernel,
444 secondary clients can be connected, simply with:
449 secondary clients can be connected, simply with:
445
450
446 $> ipython <app> --existing
451 $> ipython <app> --existing
447
452
448 """
453 """
449 try:
454 try:
450 connection_file = get_connection_file()
455 connection_file = get_connection_file()
451 info = get_connection_info(unpack=False)
456 info = get_connection_info(unpack=False)
452 except Exception as e:
457 except Exception as e:
453 error("Could not get connection info: %r" % e)
458 error("Could not get connection info: %r" % e)
454 return
459 return
455
460
456 print (info + '\n')
461 print (info + '\n')
457 print ("Paste the above JSON into a file, and connect with:\n"
462 print ("Paste the above JSON into a file, and connect with:\n"
458 " $> ipython <app> --existing <file>\n"
463 " $> ipython <app> --existing <file>\n"
459 "or, if you are local, you can connect with just:\n"
464 "or, if you are local, you can connect with just:\n"
460 " $> ipython <app> --existing %s\n"
465 " $> ipython <app> --existing %s\n"
461 "or even just:\n"
466 "or even just:\n"
462 " $> ipython <app> --existing\n"
467 " $> ipython <app> --existing\n"
463 "if this is the most recent IPython session you have started."
468 "if this is the most recent IPython session you have started."
464 % os.path.basename(connection_file)
469 % os.path.basename(connection_file)
465 )
470 )
466
471
467 def magic_qtconsole(self, arg_s):
472 def magic_qtconsole(self, arg_s):
468 """Open a qtconsole connected to this kernel.
473 """Open a qtconsole connected to this kernel.
469
474
470 Useful for connecting a qtconsole to running notebooks, for better
475 Useful for connecting a qtconsole to running notebooks, for better
471 debugging.
476 debugging.
472 """
477 """
473 try:
478 try:
474 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
479 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
475 except Exception as e:
480 except Exception as e:
476 error("Could not start qtconsole: %r" % e)
481 error("Could not start qtconsole: %r" % e)
477 return
482 return
478
483
479
484
480 def set_next_input(self, text):
485 def set_next_input(self, text):
481 """Send the specified text to the frontend to be presented at the next
486 """Send the specified text to the frontend to be presented at the next
482 input cell."""
487 input cell."""
483 payload = dict(
488 payload = dict(
484 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
489 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
485 text=text
490 text=text
486 )
491 )
487 self.payload_manager.write_payload(payload)
492 self.payload_manager.write_payload(payload)
488
493
489 InteractiveShellABC.register(ZMQInteractiveShell)
494 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now