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