##// END OF EJS Templates
Merge pull request #893 from minrk/clearoutput...
Fernando Perez -
r5099:c99b9fd7 merge
parent child Browse files
Show More
@@ -0,0 +1,109 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": true,
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 {
34 "output_type": "stream",
35 "stream": "stdout",
36 "text": "\nTime step: 9"
37 },
38 {
39 "output_type": "stream",
40 "stream": "stdout",
41 "text": "\n"
42 }
43 ],
44 "collapsed": false,
45 "prompt_number": 20,
46 "input": "for i in range(10):\n clear_output()\n print \"Time step: %i\" % i\n time.sleep(0.5)\n"
47 },
48 {
49 "source": "Next, we show that ``clear_output`` can also be used to create a primitive form of animation using\nmatplotlib:",
50 "cell_type": "markdown"
51 },
52 {
53 "cell_type": "code",
54 "language": "python",
55 "outputs": [
56 {
57 "output_type": "display_data",
58 "png": "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\n8P8BdhDzr6dIpNsAAAAASUVORK5CYII=\n"
59 }
60 ],
61 "collapsed": false,
62 "prompt_number": 18,
63 "input": "for i in range(10):\n figure()\n plot(rand(100))\n clear_output()\n show()\n time.sleep(0.25)\n"
64 },
65 {
66 "source": "And we can even selectively clear only a subset of stdout/stderr/other display such as figures\n(all are cleared by default).",
67 "cell_type": "markdown"
68 },
69 {
70 "cell_type": "code",
71 "language": "python",
72 "outputs": [
73 {
74 "output_type": "stream",
75 "stream": "stdout",
76 "text": "Time step: 0\n"
77 },
78 {
79 "output_type": "stream",
80 "stream": "stdout",
81 "text": "Time step: 1\n"
82 },
83 {
84 "output_type": "stream",
85 "stream": "stdout",
86 "text": "Time step: 2\n"
87 },
88 {
89 "output_type": "stream",
90 "stream": "stdout",
91 "text": "Time step: 3\n"
92 },
93 {
94 "output_type": "display_data",
95 "png": "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\nZjzrn0OUXq0AAAAASUVORK5CYII=\n"
96 }
97 ],
98 "collapsed": false,
99 "prompt_number": 19,
100 "input": "for i in range(4):\n print \"Time step: %i\" % i\n figure()\n plot(rand(100))\n # clear plots, but not stdout:\n clear_output(stdout=False)\n show()\n time.sleep(0.25)\n"
101 }
102 ]
103 }
104 ],
105 "metadata": {
106 "name": "clear_output"
107 },
108 "nbformat": 2
109 } No newline at end of file
@@ -1,383 +1,407 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(stdout=True, stderr=True, other=True):
387 """Clear the output of the current cell receiving output.
388
389 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
390 produced by display()) can be excluded from the clear event.
391
392 By default, everything is cleared.
393
394 Parameters
395 ----------
396 stdout : bool [default: True]
397 Whether to clear stdout.
398 stderr : bool [default: True]
399 Whether to clear stderr.
400 other : bool [default: True]
401 Whether to clear everything else that is not stdout/stderr
402 (e.g. figures,images,HTML, any result of display()).
403 """
404 from IPython.core.interactiveshell import InteractiveShell
405 InteractiveShell.instance().display_pub.clear_output(
406 stdout=stdout, stderr=stderr, other=other,
407 )
@@ -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, stdout=True, stderr=True, other=True):
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,486 +1,518 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 // CodeCell
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var CodeCell = function (notebook) {
17 17 this.code_mirror = null;
18 18 this.input_prompt_number = ' ';
19 19 this.is_completing = false;
20 20 this.completion_cursor = null;
21 21 this.outputs = [];
22 22 this.collapsed = false;
23 23 IPython.Cell.apply(this, arguments);
24 24 };
25 25
26 26
27 27 CodeCell.prototype = new IPython.Cell();
28 28
29 29
30 30 CodeCell.prototype.create_element = function () {
31 31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
32 32 cell.attr('tabindex','2');
33 33 var input = $('<div></div>').addClass('input hbox');
34 34 input.append($('<div/>').addClass('prompt input_prompt'));
35 35 var input_area = $('<div/>').addClass('input_area box-flex1');
36 36 this.code_mirror = CodeMirror(input_area.get(0), {
37 37 indentUnit : 4,
38 38 mode: 'python',
39 39 theme: 'ipython',
40 40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
41 41 });
42 42 input.append(input_area);
43 43 var output = $('<div></div>').addClass('output vbox');
44 44 cell.append(input).append(output);
45 45 this.element = cell;
46 46 this.collapse()
47 47 };
48 48
49 49
50 50 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
51 51 // This method gets called in CodeMirror's onKeyDown/onKeyPress
52 52 // handlers and is used to provide custom key handling. Its return
53 53 // value is used to determine if CodeMirror should ignore the event:
54 54 // true = ignore, false = don't ignore.
55 55 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
56 56 // Always ignore shift-enter in CodeMirror as we handle it.
57 57 return true;
58 58 } else if (event.keyCode === 9 && event.type == 'keydown') {
59 59 // Tab completion.
60 60 var cur = editor.getCursor();
61 61 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
62 62 if (pre_cursor === "") {
63 63 // Don't autocomplete if the part of the line before the cursor
64 64 // is empty. In this case, let CodeMirror handle indentation.
65 65 return false;
66 66 } else {
67 67 // Autocomplete the current line.
68 68 event.stop();
69 69 var line = editor.getLine(cur.line);
70 70 this.is_completing = true;
71 71 this.completion_cursor = cur;
72 72 IPython.notebook.complete_cell(this, line, cur.ch);
73 73 return true;
74 74 }
75 75 } else if (event.keyCode === 8 && event.type == 'keydown') {
76 76 // If backspace and the line ends with 4 spaces, remove them.
77 77 var cur = editor.getCursor();
78 78 var line = editor.getLine(cur.line);
79 79 var ending = line.slice(-4);
80 80 if (ending === ' ') {
81 81 editor.replaceRange('',
82 82 {line: cur.line, ch: cur.ch-4},
83 83 {line: cur.line, ch: cur.ch}
84 84 );
85 85 event.stop();
86 86 return true;
87 87 } else {
88 88 return false;
89 89 };
90 90 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
91 91 && event.type == 'keydown') {
92 92 // toggle line numbers with Ctrl-Shift-L
93 93 this.toggle_line_numbers();
94 94 }
95 95 else {
96 96 // keypress/keyup also trigger on TAB press, and we don't want to
97 97 // use those to disable tab completion.
98 98 if (this.is_completing && event.keyCode !== 9) {
99 99 var ed_cur = editor.getCursor();
100 100 var cc_cur = this.completion_cursor;
101 101 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
102 102 this.is_completing = false;
103 103 this.completion_cursor = null;
104 104 };
105 105 };
106 106 return false;
107 107 };
108 108 };
109 109
110 110
111 111 CodeCell.prototype.finish_completing = function (matched_text, matches) {
112 112 // console.log("Got matches", matched_text, matches);
113 113 if (!this.is_completing || matches.length === 0) {return;}
114 114
115 115 var that = this;
116 116 var cur = this.completion_cursor;
117 117
118 118 var insert = function (selected_text) {
119 119 that.code_mirror.replaceRange(
120 120 selected_text,
121 121 {line: cur.line, ch: (cur.ch-matched_text.length)},
122 122 {line: cur.line, ch: cur.ch}
123 123 );
124 124 };
125 125
126 126 if (matches.length === 1) {
127 127 insert(matches[0]);
128 128 setTimeout(function(){that.code_mirror.focus();}, 50);
129 129 return;
130 130 };
131 131
132 132 var complete = $('<div/>').addClass('completions');
133 133 var select = $('<select/>').attr('multiple','true');
134 134 for (var i=0; i<matches.length; ++i) {
135 135 select.append($('<option/>').text(matches[i]));
136 136 }
137 137 select.children().first().attr('selected','true');
138 138 select.attr('size',Math.min(10,matches.length));
139 139 var pos = this.code_mirror.cursorCoords();
140 140 complete.css('left',pos.x+'px');
141 141 complete.css('top',pos.yBot+'px');
142 142 complete.append(select);
143 143
144 144 $('body').append(complete);
145 145 var done = false;
146 146
147 147 var close = function () {
148 148 if (done) return;
149 149 done = true;
150 150 complete.remove();
151 151 that.is_completing = false;
152 152 that.completion_cursor = null;
153 153 };
154 154
155 155 var pick = function () {
156 156 insert(select.val()[0]);
157 157 close();
158 158 setTimeout(function(){that.code_mirror.focus();}, 50);
159 159 };
160 160
161 161 select.blur(close);
162 162 select.keydown(function (event) {
163 163 var code = event.which;
164 164 if (code === 13 || code === 32) {
165 165 // Pressing SPACE or ENTER will cause a pick
166 166 event.stopPropagation();
167 167 event.preventDefault();
168 168 pick();
169 169 } else if (code === 38 || code === 40) {
170 170 // We don't want the document keydown handler to handle UP/DOWN,
171 171 // but we want the default action.
172 172 event.stopPropagation();
173 173 } else {
174 174 // All other key presses exit completion.
175 175 event.stopPropagation();
176 176 event.preventDefault();
177 177 close();
178 178 that.code_mirror.focus();
179 179 }
180 180 });
181 181 // Double click also causes a pick.
182 182 select.dblclick(pick);
183 183 select.focus();
184 184 };
185 185
186 186 CodeCell.prototype.toggle_line_numbers = function () {
187 187 if (this.code_mirror.getOption('lineNumbers') == false) {
188 188 this.code_mirror.setOption('lineNumbers', true);
189 189 } else {
190 190 this.code_mirror.setOption('lineNumbers', false);
191 191 }
192 192 this.code_mirror.refresh()
193 193 };
194 194
195 195 CodeCell.prototype.select = function () {
196 196 IPython.Cell.prototype.select.apply(this);
197 197 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
198 198 // not causing the cursor to blink if the editor is empty initially.
199 199 // While this seems to fix the issue, this should be fixed
200 200 // in CodeMirror proper.
201 201 var s = this.code_mirror.getValue();
202 202 this.code_mirror.focus();
203 203 if (s === '') this.code_mirror.setValue('');
204 204 };
205 205
206 206
207 207 CodeCell.prototype.select_all = function () {
208 208 var start = {line: 0, ch: 0};
209 209 var nlines = this.code_mirror.lineCount();
210 210 var last_line = this.code_mirror.getLine(nlines-1);
211 211 var end = {line: nlines-1, ch: last_line.length};
212 212 this.code_mirror.setSelection(start, end);
213 213 };
214 214
215 215
216 216 CodeCell.prototype.append_output = function (json) {
217 217 this.expand();
218 218 if (json.output_type === 'pyout') {
219 219 this.append_pyout(json);
220 220 } else if (json.output_type === 'pyerr') {
221 221 this.append_pyerr(json);
222 222 } else if (json.output_type === 'display_data') {
223 223 this.append_display_data(json);
224 224 } else if (json.output_type === 'stream') {
225 225 this.append_stream(json);
226 226 };
227 227 this.outputs.push(json);
228 228 };
229 229
230 230
231 231 CodeCell.prototype.create_output_area = function () {
232 232 var oa = $("<div/>").addClass("hbox output_area");
233 233 oa.append($('<div/>').addClass('prompt'));
234 234 return oa;
235 235 };
236 236
237 237
238 238 CodeCell.prototype.append_pyout = function (json) {
239 239 n = json.prompt_number || ' ';
240 240 var toinsert = this.create_output_area();
241 241 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
242 242 this.append_mime_type(json, toinsert);
243 243 this.element.find('div.output').append(toinsert);
244 244 // If we just output latex, typeset it.
245 245 if (json.latex !== undefined) {
246 246 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
247 247 };
248 248 };
249 249
250 250
251 251 CodeCell.prototype.append_pyerr = function (json) {
252 252 var tb = json.traceback;
253 253 if (tb !== undefined && tb.length > 0) {
254 254 var s = '';
255 255 var len = tb.length;
256 256 for (var i=0; i<len; i++) {
257 257 s = s + tb[i] + '\n';
258 258 }
259 259 s = s + '\n';
260 260 var toinsert = this.create_output_area();
261 261 this.append_text(s, toinsert);
262 262 this.element.find('div.output').append(toinsert);
263 263 };
264 264 };
265 265
266 266
267 267 CodeCell.prototype.append_stream = function (json) {
268 268 // temporary fix: if stream undefined (json file written prior to this patch),
269 269 // default to most likely stdout:
270 270 if (json.stream == undefined){
271 271 json.stream = 'stdout';
272 272 }
273 273 var subclass = "output_"+json.stream;
274 274 if (this.outputs.length > 0){
275 275 // have at least one output to consider
276 276 var last = this.outputs[this.outputs.length-1];
277 277 if (last.output_type == 'stream' && json.stream == last.stream){
278 278 // latest output was in the same stream,
279 279 // so append directly into its pre tag
280 280 this.element.find('div.'+subclass).last().find('pre').append(json.text);
281 281 return;
282 282 }
283 283 }
284 284
285 285 // If we got here, attach a new div
286 286 var toinsert = this.create_output_area();
287 287 this.append_text(json.text, toinsert, "output_stream "+subclass);
288 288 this.element.find('div.output').append(toinsert);
289 289 };
290 290
291 291
292 292 CodeCell.prototype.append_display_data = function (json) {
293 293 var toinsert = this.create_output_area();
294 294 this.append_mime_type(json, toinsert)
295 295 this.element.find('div.output').append(toinsert);
296 296 // If we just output latex, typeset it.
297 297 if (json.latex !== undefined) {
298 298 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
299 299 };
300 300 };
301 301
302 302
303 303 CodeCell.prototype.append_mime_type = function (json, element) {
304 304 if (json.html !== undefined) {
305 305 this.append_html(json.html, element);
306 306 } else if (json.latex !== undefined) {
307 307 this.append_latex(json.latex, element);
308 308 } else if (json.svg !== undefined) {
309 309 this.append_svg(json.svg, element);
310 310 } else if (json.png !== undefined) {
311 311 this.append_png(json.png, element);
312 312 } else if (json.jpeg !== undefined) {
313 313 this.append_jpeg(json.jpeg, element);
314 314 } else if (json.text !== undefined) {
315 315 this.append_text(json.text, element);
316 316 };
317 317 };
318 318
319 319
320 320 CodeCell.prototype.append_html = function (html, element) {
321 321 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
322 322 toinsert.append(html);
323 323 element.append(toinsert);
324 324 }
325 325
326 326
327 327 CodeCell.prototype.append_text = function (data, element, extra_class) {
328 328 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
329 329 if (extra_class){
330 330 toinsert.addClass(extra_class);
331 331 }
332 332 toinsert.append($("<pre/>").html(data));
333 333 element.append(toinsert);
334 334 };
335 335
336 336
337 337 CodeCell.prototype.append_svg = function (svg, element) {
338 338 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
339 339 toinsert.append(svg);
340 340 element.append(toinsert);
341 341 };
342 342
343 343
344 344 CodeCell.prototype.append_png = function (png, element) {
345 345 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
346 346 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
347 347 element.append(toinsert);
348 348 };
349 349
350 350
351 351 CodeCell.prototype.append_jpeg = function (jpeg, element) {
352 352 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
353 353 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
354 354 element.append(toinsert);
355 355 };
356 356
357 357
358 358 CodeCell.prototype.append_latex = function (latex, element) {
359 359 // This method cannot do the typesetting because the latex first has to
360 360 // be on the page.
361 361 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
362 362 toinsert.append(latex);
363 363 element.append(toinsert);
364 364 }
365 365
366 366
367 CodeCell.prototype.clear_output = function () {
368 this.element.find("div.output").html("");
369 this.outputs = [];
367 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
368 var output_div = this.element.find("div.output");
369 if (stdout && stderr && other){
370 // clear all, no need for logic
371 output_div.html("");
372 this.outputs = [];
373 return;
374 }
375 // remove html output
376 // each output_subarea that has an identifying class is in an output_area
377 // which is the element to be removed.
378 if (stdout){
379 output_div.find("div.output_stdout").parent().remove();
380 }
381 if (stderr){
382 output_div.find("div.output_stderr").parent().remove();
383 }
384 if (other){
385 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
386 }
387
388 // remove cleared outputs from JSON list:
389 for (var i = this.outputs.length - 1; i >= 0; i--){
390 var out = this.outputs[i];
391 var output_type = out.output_type;
392 if (output_type == "display_data" && other){
393 this.outputs.splice(i,1);
394 }else if (output_type == "stream"){
395 if (stdout && out.stream == "stdout"){
396 this.outputs.splice(i,1);
397 }else if (stderr && out.stream == "stderr"){
398 this.outputs.splice(i,1);
399 }
400 }
401 }
370 402 };
371 403
372 404
373 405 CodeCell.prototype.clear_input = function () {
374 406 this.code_mirror.setValue('');
375 407 };
376 408
377 409
378 410 CodeCell.prototype.collapse = function () {
379 411 if (!this.collapsed) {
380 412 this.element.find('div.output').hide();
381 413 this.collapsed = true;
382 414 };
383 415 };
384 416
385 417
386 418 CodeCell.prototype.expand = function () {
387 419 if (this.collapsed) {
388 420 this.element.find('div.output').show();
389 421 this.collapsed = false;
390 422 };
391 423 };
392 424
393 425
394 426 CodeCell.prototype.toggle_output = function () {
395 427 if (this.collapsed) {
396 428 this.expand();
397 429 } else {
398 430 this.collapse();
399 431 };
400 432 };
401 433
402 434 CodeCell.prototype.set_input_prompt = function (number) {
403 435 var n = number || ' ';
404 436 this.input_prompt_number = n
405 437 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
406 438 };
407 439
408 440
409 441 CodeCell.prototype.get_code = function () {
410 442 return this.code_mirror.getValue();
411 443 };
412 444
413 445
414 446 CodeCell.prototype.set_code = function (code) {
415 447 return this.code_mirror.setValue(code);
416 448 };
417 449
418 450
419 451 CodeCell.prototype.at_top = function () {
420 452 var cursor = this.code_mirror.getCursor();
421 453 if (cursor.line === 0) {
422 454 return true;
423 455 } else {
424 456 return false;
425 457 }
426 458 };
427 459
428 460
429 461 CodeCell.prototype.at_bottom = function () {
430 462 var cursor = this.code_mirror.getCursor();
431 463 if (cursor.line === (this.code_mirror.lineCount()-1)) {
432 464 return true;
433 465 } else {
434 466 return false;
435 467 }
436 468 };
437 469
438 470
439 471 CodeCell.prototype.fromJSON = function (data) {
440 472 console.log('Import from JSON:', data);
441 473 if (data.cell_type === 'code') {
442 474 if (data.input !== undefined) {
443 475 this.set_code(data.input);
444 476 }
445 477 if (data.prompt_number !== undefined) {
446 478 this.set_input_prompt(data.prompt_number);
447 479 } else {
448 480 this.set_input_prompt();
449 481 };
450 482 var len = data.outputs.length;
451 483 for (var i=0; i<len; i++) {
452 484 this.append_output(data.outputs[i]);
453 485 };
454 486 if (data.collapsed !== undefined) {
455 487 if (data.collapsed) {
456 488 this.collapse();
457 489 };
458 490 };
459 491 };
460 492 };
461 493
462 494
463 495 CodeCell.prototype.toJSON = function () {
464 496 var data = {};
465 497 data.input = this.get_code();
466 498 data.cell_type = 'code';
467 499 if (this.input_prompt_number !== ' ') {
468 500 data.prompt_number = this.input_prompt_number
469 501 };
470 502 var outputs = [];
471 503 var len = this.outputs.length;
472 504 for (var i=0; i<len; i++) {
473 505 outputs[i] = this.outputs[i];
474 506 };
475 507 data.outputs = outputs;
476 508 data.language = 'python';
477 509 data.collapsed = this.collapsed;
478 510 // console.log('Export to JSON:',data);
479 511 return data;
480 512 };
481 513
482 514
483 515 IPython.CodeCell = CodeCell;
484 516
485 517 return IPython;
486 518 }(IPython));
@@ -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 cells[i].clear_output();
630 cells[i].clear_output(true,true,true);
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(content.stdout, content.stderr, content.other);
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 cell.clear_output();
828 cell.clear_output(true, true, true);
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,495 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, stdout=True, stderr=True, other=True):
84 content = dict(stdout=stdout, stderr=stderr, other=other)
85 self.session.send(
86 self.pub_socket, u'clear_output', content,
87 parent=self.parent_header
88 )
83 89
84 90 class ZMQInteractiveShell(InteractiveShell):
85 91 """A subclass of InteractiveShell for ZMQ."""
86 92
87 93 displayhook_class = Type(ZMQShellDisplayHook)
88 94 display_pub_class = Type(ZMQDisplayPublisher)
89 95
90 96 # Override the traitlet in the parent class, because there's no point using
91 97 # readline for the kernel. Can be removed when the readline code is moved
92 98 # to the terminal frontend.
93 99 colors_force = CBool(True)
94 100 readline_use = CBool(False)
95 101 # autoindent has no meaning in a zmqshell, and attempting to enable it
96 102 # will print a warning in the absence of readline.
97 103 autoindent = CBool(False)
98 104
99 105 exiter = Instance(ZMQExitAutocall)
100 106 def _exiter_default(self):
101 107 return ZMQExitAutocall(self)
102 108
103 109 keepkernel_on_exit = None
104 110
105 111 def init_environment(self):
106 112 """Configure the user's environment.
107 113
108 114 """
109 115 env = os.environ
110 116 # These two ensure 'ls' produces nice coloring on BSD-derived systems
111 117 env['TERM'] = 'xterm-color'
112 118 env['CLICOLOR'] = '1'
113 119 # Since normal pagers don't work at all (over pexpect we don't have
114 120 # single-key control of the subprocess), try to disable paging in
115 121 # subprocesses as much as possible.
116 122 env['PAGER'] = 'cat'
117 123 env['GIT_PAGER'] = 'cat'
118 124
119 125 def auto_rewrite_input(self, cmd):
120 126 """Called to show the auto-rewritten input for autocall and friends.
121 127
122 128 FIXME: this payload is currently not correctly processed by the
123 129 frontend.
124 130 """
125 131 new = self.displayhook.prompt1.auto_rewrite() + cmd
126 132 payload = dict(
127 133 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
128 134 transformed_input=new,
129 135 )
130 136 self.payload_manager.write_payload(payload)
131 137
132 138 def ask_exit(self):
133 139 """Engage the exit actions."""
134 140 payload = dict(
135 141 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
136 142 exit=True,
137 143 keepkernel=self.keepkernel_on_exit,
138 144 )
139 145 self.payload_manager.write_payload(payload)
140 146
141 147 def _showtraceback(self, etype, evalue, stb):
142 148
143 149 exc_content = {
144 150 u'traceback' : stb,
145 151 u'ename' : unicode(etype.__name__),
146 152 u'evalue' : unicode(evalue)
147 153 }
148 154
149 155 dh = self.displayhook
150 156 # Send exception info over pub socket for other clients than the caller
151 157 # to pick up
152 158 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
153 159
154 160 # FIXME - Hack: store exception info in shell object. Right now, the
155 161 # caller is reading this info after the fact, we need to fix this logic
156 162 # to remove this hack. Even uglier, we need to store the error status
157 163 # here, because in the main loop, the logic that sets it is being
158 164 # skipped because runlines swallows the exceptions.
159 165 exc_content[u'status'] = u'error'
160 166 self._reply_content = exc_content
161 167 # /FIXME
162 168
163 169 return exc_content
164 170
165 171 #------------------------------------------------------------------------
166 172 # Magic overrides
167 173 #------------------------------------------------------------------------
168 174 # Once the base class stops inheriting from magic, this code needs to be
169 175 # moved into a separate machinery as well. For now, at least isolate here
170 176 # the magics which this class needs to implement differently from the base
171 177 # class, or that are unique to it.
172 178
173 179 def magic_doctest_mode(self,parameter_s=''):
174 180 """Toggle doctest mode on and off.
175 181
176 182 This mode is intended to make IPython behave as much as possible like a
177 183 plain Python shell, from the perspective of how its prompts, exceptions
178 184 and output look. This makes it easy to copy and paste parts of a
179 185 session into doctests. It does so by:
180 186
181 187 - Changing the prompts to the classic ``>>>`` ones.
182 188 - Changing the exception reporting mode to 'Plain'.
183 189 - Disabling pretty-printing of output.
184 190
185 191 Note that IPython also supports the pasting of code snippets that have
186 192 leading '>>>' and '...' prompts in them. This means that you can paste
187 193 doctests from files or docstrings (even if they have leading
188 194 whitespace), and the code will execute correctly. You can then use
189 195 '%history -t' to see the translated history; this will give you the
190 196 input after removal of all the leading prompts and whitespace, which
191 197 can be pasted back into an editor.
192 198
193 199 With these features, you can switch into this mode easily whenever you
194 200 need to do testing and changes to doctests, without having to leave
195 201 your existing IPython session.
196 202 """
197 203
198 204 from IPython.utils.ipstruct import Struct
199 205
200 206 # Shorthands
201 207 shell = self.shell
202 208 disp_formatter = self.shell.display_formatter
203 209 ptformatter = disp_formatter.formatters['text/plain']
204 210 # dstore is a data store kept in the instance metadata bag to track any
205 211 # changes we make, so we can undo them later.
206 212 dstore = shell.meta.setdefault('doctest_mode', Struct())
207 213 save_dstore = dstore.setdefault
208 214
209 215 # save a few values we'll need to recover later
210 216 mode = save_dstore('mode', False)
211 217 save_dstore('rc_pprint', ptformatter.pprint)
212 218 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
213 219 save_dstore('xmode', shell.InteractiveTB.mode)
214 220
215 221 if mode == False:
216 222 # turn on
217 223 ptformatter.pprint = False
218 224 disp_formatter.plain_text_only = True
219 225 shell.magic_xmode('Plain')
220 226 else:
221 227 # turn off
222 228 ptformatter.pprint = dstore.rc_pprint
223 229 disp_formatter.plain_text_only = dstore.rc_plain_text_only
224 230 shell.magic_xmode(dstore.xmode)
225 231
226 232 # Store new mode and inform on console
227 233 dstore.mode = bool(1-int(mode))
228 234 mode_label = ['OFF','ON'][dstore.mode]
229 235 print('Doctest mode is:', mode_label)
230 236
231 237 # Send the payload back so that clients can modify their prompt display
232 238 payload = dict(
233 239 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
234 240 mode=dstore.mode)
235 241 self.payload_manager.write_payload(payload)
236 242
237 243 def magic_edit(self,parameter_s='',last_call=['','']):
238 244 """Bring up an editor and execute the resulting code.
239 245
240 246 Usage:
241 247 %edit [options] [args]
242 248
243 249 %edit runs an external text editor. You will need to set the command for
244 250 this editor via the ``TerminalInteractiveShell.editor`` option in your
245 251 configuration file before it will work.
246 252
247 253 This command allows you to conveniently edit multi-line code right in
248 254 your IPython session.
249 255
250 256 If called without arguments, %edit opens up an empty editor with a
251 257 temporary file and will execute the contents of this file when you
252 258 close it (don't forget to save it!).
253 259
254 260
255 261 Options:
256 262
257 263 -n <number>: open the editor at a specified line number. By default,
258 264 the IPython editor hook uses the unix syntax 'editor +N filename', but
259 265 you can configure this by providing your own modified hook if your
260 266 favorite editor supports line-number specifications with a different
261 267 syntax.
262 268
263 269 -p: this will call the editor with the same data as the previous time
264 270 it was used, regardless of how long ago (in your current session) it
265 271 was.
266 272
267 273 -r: use 'raw' input. This option only applies to input taken from the
268 274 user's history. By default, the 'processed' history is used, so that
269 275 magics are loaded in their transformed version to valid Python. If
270 276 this option is given, the raw input as typed as the command line is
271 277 used instead. When you exit the editor, it will be executed by
272 278 IPython's own processor.
273 279
274 280 -x: do not execute the edited code immediately upon exit. This is
275 281 mainly useful if you are editing programs which need to be called with
276 282 command line arguments, which you can then do using %run.
277 283
278 284
279 285 Arguments:
280 286
281 287 If arguments are given, the following possibilites exist:
282 288
283 289 - The arguments are numbers or pairs of colon-separated numbers (like
284 290 1 4:8 9). These are interpreted as lines of previous input to be
285 291 loaded into the editor. The syntax is the same of the %macro command.
286 292
287 293 - If the argument doesn't start with a number, it is evaluated as a
288 294 variable and its contents loaded into the editor. You can thus edit
289 295 any string which contains python code (including the result of
290 296 previous edits).
291 297
292 298 - If the argument is the name of an object (other than a string),
293 299 IPython will try to locate the file where it was defined and open the
294 300 editor at the point where it is defined. You can use `%edit function`
295 301 to load an editor exactly at the point where 'function' is defined,
296 302 edit it and have the file be executed automatically.
297 303
298 304 If the object is a macro (see %macro for details), this opens up your
299 305 specified editor with a temporary file containing the macro's data.
300 306 Upon exit, the macro is reloaded with the contents of the file.
301 307
302 308 Note: opening at an exact line is only supported under Unix, and some
303 309 editors (like kedit and gedit up to Gnome 2.8) do not understand the
304 310 '+NUMBER' parameter necessary for this feature. Good editors like
305 311 (X)Emacs, vi, jed, pico and joe all do.
306 312
307 313 - If the argument is not found as a variable, IPython will look for a
308 314 file with that name (adding .py if necessary) and load it into the
309 315 editor. It will execute its contents with execfile() when you exit,
310 316 loading any code in the file into your interactive namespace.
311 317
312 318 After executing your code, %edit will return as output the code you
313 319 typed in the editor (except when it was an existing file). This way
314 320 you can reload the code in further invocations of %edit as a variable,
315 321 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
316 322 the output.
317 323
318 324 Note that %edit is also available through the alias %ed.
319 325
320 326 This is an example of creating a simple function inside the editor and
321 327 then modifying it. First, start up the editor:
322 328
323 329 In [1]: ed
324 330 Editing... done. Executing edited code...
325 331 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
326 332
327 333 We can then call the function foo():
328 334
329 335 In [2]: foo()
330 336 foo() was defined in an editing session
331 337
332 338 Now we edit foo. IPython automatically loads the editor with the
333 339 (temporary) file where foo() was previously defined:
334 340
335 341 In [3]: ed foo
336 342 Editing... done. Executing edited code...
337 343
338 344 And if we call foo() again we get the modified version:
339 345
340 346 In [4]: foo()
341 347 foo() has now been changed!
342 348
343 349 Here is an example of how to edit a code snippet successive
344 350 times. First we call the editor:
345 351
346 352 In [5]: ed
347 353 Editing... done. Executing edited code...
348 354 hello
349 355 Out[5]: "print 'hello'n"
350 356
351 357 Now we call it again with the previous output (stored in _):
352 358
353 359 In [6]: ed _
354 360 Editing... done. Executing edited code...
355 361 hello world
356 362 Out[6]: "print 'hello world'n"
357 363
358 364 Now we call it with the output #8 (stored in _8, also as Out[8]):
359 365
360 366 In [7]: ed _8
361 367 Editing... done. Executing edited code...
362 368 hello again
363 369 Out[7]: "print 'hello again'n"
364 370 """
365 371
366 372 opts,args = self.parse_options(parameter_s,'prn:')
367 373
368 374 try:
369 375 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
370 376 except MacroToEdit as e:
371 377 # TODO: Implement macro editing over 2 processes.
372 378 print("Macro editing not yet implemented in 2-process model.")
373 379 return
374 380
375 381 # Make sure we send to the client an absolute path, in case the working
376 382 # directory of client and kernel don't match
377 383 filename = os.path.abspath(filename)
378 384
379 385 payload = {
380 386 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
381 387 'filename' : filename,
382 388 'line_number' : lineno
383 389 }
384 390 self.payload_manager.write_payload(payload)
385 391
386 392 def magic_gui(self, *args, **kwargs):
387 393 raise NotImplementedError(
388 394 'Kernel GUI support is not implemented yet, except for --pylab.')
389 395
390 396 def magic_pylab(self, *args, **kwargs):
391 397 raise NotImplementedError(
392 398 'pylab support must be enabled in command line options.')
393 399
394 400 # A few magics that are adapted to the specifics of using pexpect and a
395 401 # remote terminal
396 402
397 403 def magic_clear(self, arg_s):
398 404 """Clear the terminal."""
399 405 if os.name == 'posix':
400 406 self.shell.system("clear")
401 407 else:
402 408 self.shell.system("cls")
403 409
404 410 if os.name == 'nt':
405 411 # This is the usual name in windows
406 412 magic_cls = magic_clear
407 413
408 414 # Terminal pagers won't work over pexpect, but we do have our own pager
409 415
410 416 def magic_less(self, arg_s):
411 417 """Show a file through the pager.
412 418
413 419 Files ending in .py are syntax-highlighted."""
414 420 cont = open(arg_s).read()
415 421 if arg_s.endswith('.py'):
416 422 cont = self.shell.pycolorize(cont)
417 423 page.page(cont)
418 424
419 425 magic_more = magic_less
420 426
421 427 # Man calls a pager, so we also need to redefine it
422 428 if os.name == 'posix':
423 429 def magic_man(self, arg_s):
424 430 """Find the man page for the given command and display in pager."""
425 431 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
426 432 split=False))
427 433
428 434 # FIXME: this is specific to the GUI, so we should let the gui app load
429 435 # magics at startup that are only for the gui. Once the gui app has proper
430 436 # profile and configuration management, we can have it initialize a kernel
431 437 # with a special config file that provides these.
432 438 def magic_guiref(self, arg_s):
433 439 """Show a basic reference about the GUI console."""
434 440 from IPython.core.usage import gui_reference
435 441 page.page(gui_reference, auto_html=True)
436 442
437 443 def magic_connect_info(self, arg_s):
438 444 """Print information for connecting other clients to this kernel
439 445
440 446 It will print the contents of this session's connection file, as well as
441 447 shortcuts for local clients.
442 448
443 449 In the simplest case, when called from the most recently launched kernel,
444 450 secondary clients can be connected, simply with:
445 451
446 452 $> ipython <app> --existing
447 453
448 454 """
449 455 try:
450 456 connection_file = get_connection_file()
451 457 info = get_connection_info(unpack=False)
452 458 except Exception as e:
453 459 error("Could not get connection info: %r" % e)
454 460 return
455 461
456 462 print (info + '\n')
457 463 print ("Paste the above JSON into a file, and connect with:\n"
458 464 " $> ipython <app> --existing <file>\n"
459 465 "or, if you are local, you can connect with just:\n"
460 466 " $> ipython <app> --existing %s\n"
461 467 "or even just:\n"
462 468 " $> ipython <app> --existing\n"
463 469 "if this is the most recent IPython session you have started."
464 470 % os.path.basename(connection_file)
465 471 )
466 472
467 473 def magic_qtconsole(self, arg_s):
468 474 """Open a qtconsole connected to this kernel.
469 475
470 476 Useful for connecting a qtconsole to running notebooks, for better
471 477 debugging.
472 478 """
473 479 try:
474 480 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
475 481 except Exception as e:
476 482 error("Could not start qtconsole: %r" % e)
477 483 return
478 484
479 485
480 486 def set_next_input(self, text):
481 487 """Send the specified text to the frontend to be presented at the next
482 488 input cell."""
483 489 payload = dict(
484 490 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
485 491 text=text
486 492 )
487 493 self.payload_manager.write_payload(payload)
488 494
489 495 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now