##// END OF EJS Templates
Added display hook logic to handle widgets
Jonathan Frederic -
Show More
@@ -1,690 +1,694 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import struct
23 import struct
24
24
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
25 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
26 unicode_type)
26 unicode_type)
27 from IPython.html.widgets import Widget
27
28
28 from .displaypub import publish_display_data
29 from .displaypub import publish_display_data
29
30
30 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
31 # utility functions
32 # utility functions
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
33
34
34 def _safe_exists(path):
35 def _safe_exists(path):
35 """Check path, but don't let exceptions raise"""
36 """Check path, but don't let exceptions raise"""
36 try:
37 try:
37 return os.path.exists(path)
38 return os.path.exists(path)
38 except Exception:
39 except Exception:
39 return False
40 return False
40
41
41 def _merge(d1, d2):
42 def _merge(d1, d2):
42 """Like update, but merges sub-dicts instead of clobbering at the top level.
43 """Like update, but merges sub-dicts instead of clobbering at the top level.
43
44
44 Updates d1 in-place
45 Updates d1 in-place
45 """
46 """
46
47
47 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 if not isinstance(d2, dict) or not isinstance(d1, dict):
48 return d2
49 return d2
49 for key, value in d2.items():
50 for key, value in d2.items():
50 d1[key] = _merge(d1.get(key), value)
51 d1[key] = _merge(d1.get(key), value)
51 return d1
52 return d1
52
53
53 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
54 """internal implementation of all display_foo methods
55 """internal implementation of all display_foo methods
55
56
56 Parameters
57 Parameters
57 ----------
58 ----------
58 mimetype : str
59 mimetype : str
59 The mimetype to be published (e.g. 'image/png')
60 The mimetype to be published (e.g. 'image/png')
60 objs : tuple of objects
61 objs : tuple of objects
61 The Python objects to display, or if raw=True raw text data to
62 The Python objects to display, or if raw=True raw text data to
62 display.
63 display.
63 raw : bool
64 raw : bool
64 Are the data objects raw data or Python objects that need to be
65 Are the data objects raw data or Python objects that need to be
65 formatted before display? [default: False]
66 formatted before display? [default: False]
66 metadata : dict (optional)
67 metadata : dict (optional)
67 Metadata to be associated with the specific mimetype output.
68 Metadata to be associated with the specific mimetype output.
68 """
69 """
69 if metadata:
70 if metadata:
70 metadata = {mimetype: metadata}
71 metadata = {mimetype: metadata}
71 if raw:
72 if raw:
72 # turn list of pngdata into list of { 'image/png': pngdata }
73 # turn list of pngdata into list of { 'image/png': pngdata }
73 objs = [ {mimetype: obj} for obj in objs ]
74 objs = [ {mimetype: obj} for obj in objs ]
74 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
75
76
76 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
77 # Main functions
78 # Main functions
78 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
79
80
80 def display(*objs, **kwargs):
81 def display(*objs, **kwargs):
81 """Display a Python object in all frontends.
82 """Display a Python object in all frontends.
82
83
83 By default all representations will be computed and sent to the frontends.
84 By default all representations will be computed and sent to the frontends.
84 Frontends can decide which representation is used and how.
85 Frontends can decide which representation is used and how.
85
86
86 Parameters
87 Parameters
87 ----------
88 ----------
88 objs : tuple of objects
89 objs : tuple of objects
89 The Python objects to display.
90 The Python objects to display.
90 raw : bool, optional
91 raw : bool, optional
91 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
92 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
92 or Python objects that need to be formatted before display? [default: False]
93 or Python objects that need to be formatted before display? [default: False]
93 include : list or tuple, optional
94 include : list or tuple, optional
94 A list of format type strings (MIME types) to include in the
95 A list of format type strings (MIME types) to include in the
95 format data dict. If this is set *only* the format types included
96 format data dict. If this is set *only* the format types included
96 in this list will be computed.
97 in this list will be computed.
97 exclude : list or tuple, optional
98 exclude : list or tuple, optional
98 A list of format type strings (MIME types) to exclude in the format
99 A list of format type strings (MIME types) to exclude in the format
99 data dict. If this is set all format types will be computed,
100 data dict. If this is set all format types will be computed,
100 except for those included in this argument.
101 except for those included in this argument.
101 metadata : dict, optional
102 metadata : dict, optional
102 A dictionary of metadata to associate with the output.
103 A dictionary of metadata to associate with the output.
103 mime-type keys in this dictionary will be associated with the individual
104 mime-type keys in this dictionary will be associated with the individual
104 representation formats, if they exist.
105 representation formats, if they exist.
105 """
106 """
106 raw = kwargs.get('raw', False)
107 raw = kwargs.get('raw', False)
107 include = kwargs.get('include')
108 include = kwargs.get('include')
108 exclude = kwargs.get('exclude')
109 exclude = kwargs.get('exclude')
109 metadata = kwargs.get('metadata')
110 metadata = kwargs.get('metadata')
110
111
111 from IPython.core.interactiveshell import InteractiveShell
112 from IPython.core.interactiveshell import InteractiveShell
112
113
113 if raw:
114 if isinstance(obj, Widget):
114 for obj in objs:
115 obj._repr_widget_(**kwargs)
115 publish_display_data('display', obj, metadata)
116 else:
116 else:
117 format = InteractiveShell.instance().display_formatter.format
117 if raw:
118 for obj in objs:
118 for obj in objs:
119 format_dict, md_dict = format(obj, include=include, exclude=exclude)
119 publish_display_data('display', obj, metadata)
120 if metadata:
120 else:
121 # kwarg-specified metadata gets precedence
121 format = InteractiveShell.instance().display_formatter.format
122 _merge(md_dict, metadata)
122 for obj in objs:
123 publish_display_data('display', format_dict, md_dict)
123 format_dict, md_dict = format(obj, include=include, exclude=exclude)
124 if metadata:
125 # kwarg-specified metadata gets precedence
126 _merge(md_dict, metadata)
127 publish_display_data('display', format_dict, md_dict)
124
128
125
129
126 def display_pretty(*objs, **kwargs):
130 def display_pretty(*objs, **kwargs):
127 """Display the pretty (default) representation of an object.
131 """Display the pretty (default) representation of an object.
128
132
129 Parameters
133 Parameters
130 ----------
134 ----------
131 objs : tuple of objects
135 objs : tuple of objects
132 The Python objects to display, or if raw=True raw text data to
136 The Python objects to display, or if raw=True raw text data to
133 display.
137 display.
134 raw : bool
138 raw : bool
135 Are the data objects raw data or Python objects that need to be
139 Are the data objects raw data or Python objects that need to be
136 formatted before display? [default: False]
140 formatted before display? [default: False]
137 metadata : dict (optional)
141 metadata : dict (optional)
138 Metadata to be associated with the specific mimetype output.
142 Metadata to be associated with the specific mimetype output.
139 """
143 """
140 _display_mimetype('text/plain', objs, **kwargs)
144 _display_mimetype('text/plain', objs, **kwargs)
141
145
142
146
143 def display_html(*objs, **kwargs):
147 def display_html(*objs, **kwargs):
144 """Display the HTML representation of an object.
148 """Display the HTML representation of an object.
145
149
146 Parameters
150 Parameters
147 ----------
151 ----------
148 objs : tuple of objects
152 objs : tuple of objects
149 The Python objects to display, or if raw=True raw HTML data to
153 The Python objects to display, or if raw=True raw HTML data to
150 display.
154 display.
151 raw : bool
155 raw : bool
152 Are the data objects raw data or Python objects that need to be
156 Are the data objects raw data or Python objects that need to be
153 formatted before display? [default: False]
157 formatted before display? [default: False]
154 metadata : dict (optional)
158 metadata : dict (optional)
155 Metadata to be associated with the specific mimetype output.
159 Metadata to be associated with the specific mimetype output.
156 """
160 """
157 _display_mimetype('text/html', objs, **kwargs)
161 _display_mimetype('text/html', objs, **kwargs)
158
162
159
163
160 def display_svg(*objs, **kwargs):
164 def display_svg(*objs, **kwargs):
161 """Display the SVG representation of an object.
165 """Display the SVG representation of an object.
162
166
163 Parameters
167 Parameters
164 ----------
168 ----------
165 objs : tuple of objects
169 objs : tuple of objects
166 The Python objects to display, or if raw=True raw svg data to
170 The Python objects to display, or if raw=True raw svg data to
167 display.
171 display.
168 raw : bool
172 raw : bool
169 Are the data objects raw data or Python objects that need to be
173 Are the data objects raw data or Python objects that need to be
170 formatted before display? [default: False]
174 formatted before display? [default: False]
171 metadata : dict (optional)
175 metadata : dict (optional)
172 Metadata to be associated with the specific mimetype output.
176 Metadata to be associated with the specific mimetype output.
173 """
177 """
174 _display_mimetype('image/svg+xml', objs, **kwargs)
178 _display_mimetype('image/svg+xml', objs, **kwargs)
175
179
176
180
177 def display_png(*objs, **kwargs):
181 def display_png(*objs, **kwargs):
178 """Display the PNG representation of an object.
182 """Display the PNG representation of an object.
179
183
180 Parameters
184 Parameters
181 ----------
185 ----------
182 objs : tuple of objects
186 objs : tuple of objects
183 The Python objects to display, or if raw=True raw png data to
187 The Python objects to display, or if raw=True raw png data to
184 display.
188 display.
185 raw : bool
189 raw : bool
186 Are the data objects raw data or Python objects that need to be
190 Are the data objects raw data or Python objects that need to be
187 formatted before display? [default: False]
191 formatted before display? [default: False]
188 metadata : dict (optional)
192 metadata : dict (optional)
189 Metadata to be associated with the specific mimetype output.
193 Metadata to be associated with the specific mimetype output.
190 """
194 """
191 _display_mimetype('image/png', objs, **kwargs)
195 _display_mimetype('image/png', objs, **kwargs)
192
196
193
197
194 def display_jpeg(*objs, **kwargs):
198 def display_jpeg(*objs, **kwargs):
195 """Display the JPEG representation of an object.
199 """Display the JPEG representation of an object.
196
200
197 Parameters
201 Parameters
198 ----------
202 ----------
199 objs : tuple of objects
203 objs : tuple of objects
200 The Python objects to display, or if raw=True raw JPEG data to
204 The Python objects to display, or if raw=True raw JPEG data to
201 display.
205 display.
202 raw : bool
206 raw : bool
203 Are the data objects raw data or Python objects that need to be
207 Are the data objects raw data or Python objects that need to be
204 formatted before display? [default: False]
208 formatted before display? [default: False]
205 metadata : dict (optional)
209 metadata : dict (optional)
206 Metadata to be associated with the specific mimetype output.
210 Metadata to be associated with the specific mimetype output.
207 """
211 """
208 _display_mimetype('image/jpeg', objs, **kwargs)
212 _display_mimetype('image/jpeg', objs, **kwargs)
209
213
210
214
211 def display_latex(*objs, **kwargs):
215 def display_latex(*objs, **kwargs):
212 """Display the LaTeX representation of an object.
216 """Display the LaTeX representation of an object.
213
217
214 Parameters
218 Parameters
215 ----------
219 ----------
216 objs : tuple of objects
220 objs : tuple of objects
217 The Python objects to display, or if raw=True raw latex data to
221 The Python objects to display, or if raw=True raw latex data to
218 display.
222 display.
219 raw : bool
223 raw : bool
220 Are the data objects raw data or Python objects that need to be
224 Are the data objects raw data or Python objects that need to be
221 formatted before display? [default: False]
225 formatted before display? [default: False]
222 metadata : dict (optional)
226 metadata : dict (optional)
223 Metadata to be associated with the specific mimetype output.
227 Metadata to be associated with the specific mimetype output.
224 """
228 """
225 _display_mimetype('text/latex', objs, **kwargs)
229 _display_mimetype('text/latex', objs, **kwargs)
226
230
227
231
228 def display_json(*objs, **kwargs):
232 def display_json(*objs, **kwargs):
229 """Display the JSON representation of an object.
233 """Display the JSON representation of an object.
230
234
231 Note that not many frontends support displaying JSON.
235 Note that not many frontends support displaying JSON.
232
236
233 Parameters
237 Parameters
234 ----------
238 ----------
235 objs : tuple of objects
239 objs : tuple of objects
236 The Python objects to display, or if raw=True raw json data to
240 The Python objects to display, or if raw=True raw json data to
237 display.
241 display.
238 raw : bool
242 raw : bool
239 Are the data objects raw data or Python objects that need to be
243 Are the data objects raw data or Python objects that need to be
240 formatted before display? [default: False]
244 formatted before display? [default: False]
241 metadata : dict (optional)
245 metadata : dict (optional)
242 Metadata to be associated with the specific mimetype output.
246 Metadata to be associated with the specific mimetype output.
243 """
247 """
244 _display_mimetype('application/json', objs, **kwargs)
248 _display_mimetype('application/json', objs, **kwargs)
245
249
246
250
247 def display_javascript(*objs, **kwargs):
251 def display_javascript(*objs, **kwargs):
248 """Display the Javascript representation of an object.
252 """Display the Javascript representation of an object.
249
253
250 Parameters
254 Parameters
251 ----------
255 ----------
252 objs : tuple of objects
256 objs : tuple of objects
253 The Python objects to display, or if raw=True raw javascript data to
257 The Python objects to display, or if raw=True raw javascript data to
254 display.
258 display.
255 raw : bool
259 raw : bool
256 Are the data objects raw data or Python objects that need to be
260 Are the data objects raw data or Python objects that need to be
257 formatted before display? [default: False]
261 formatted before display? [default: False]
258 metadata : dict (optional)
262 metadata : dict (optional)
259 Metadata to be associated with the specific mimetype output.
263 Metadata to be associated with the specific mimetype output.
260 """
264 """
261 _display_mimetype('application/javascript', objs, **kwargs)
265 _display_mimetype('application/javascript', objs, **kwargs)
262
266
263 #-----------------------------------------------------------------------------
267 #-----------------------------------------------------------------------------
264 # Smart classes
268 # Smart classes
265 #-----------------------------------------------------------------------------
269 #-----------------------------------------------------------------------------
266
270
267
271
268 class DisplayObject(object):
272 class DisplayObject(object):
269 """An object that wraps data to be displayed."""
273 """An object that wraps data to be displayed."""
270
274
271 _read_flags = 'r'
275 _read_flags = 'r'
272
276
273 def __init__(self, data=None, url=None, filename=None):
277 def __init__(self, data=None, url=None, filename=None):
274 """Create a display object given raw data.
278 """Create a display object given raw data.
275
279
276 When this object is returned by an expression or passed to the
280 When this object is returned by an expression or passed to the
277 display function, it will result in the data being displayed
281 display function, it will result in the data being displayed
278 in the frontend. The MIME type of the data should match the
282 in the frontend. The MIME type of the data should match the
279 subclasses used, so the Png subclass should be used for 'image/png'
283 subclasses used, so the Png subclass should be used for 'image/png'
280 data. If the data is a URL, the data will first be downloaded
284 data. If the data is a URL, the data will first be downloaded
281 and then displayed. If
285 and then displayed. If
282
286
283 Parameters
287 Parameters
284 ----------
288 ----------
285 data : unicode, str or bytes
289 data : unicode, str or bytes
286 The raw data or a URL or file to load the data from
290 The raw data or a URL or file to load the data from
287 url : unicode
291 url : unicode
288 A URL to download the data from.
292 A URL to download the data from.
289 filename : unicode
293 filename : unicode
290 Path to a local file to load the data from.
294 Path to a local file to load the data from.
291 """
295 """
292 if data is not None and isinstance(data, string_types):
296 if data is not None and isinstance(data, string_types):
293 if data.startswith('http') and url is None:
297 if data.startswith('http') and url is None:
294 url = data
298 url = data
295 filename = None
299 filename = None
296 data = None
300 data = None
297 elif _safe_exists(data) and filename is None:
301 elif _safe_exists(data) and filename is None:
298 url = None
302 url = None
299 filename = data
303 filename = data
300 data = None
304 data = None
301
305
302 self.data = data
306 self.data = data
303 self.url = url
307 self.url = url
304 self.filename = None if filename is None else unicode_type(filename)
308 self.filename = None if filename is None else unicode_type(filename)
305
309
306 self.reload()
310 self.reload()
307 self._check_data()
311 self._check_data()
308
312
309 def _check_data(self):
313 def _check_data(self):
310 """Override in subclasses if there's something to check."""
314 """Override in subclasses if there's something to check."""
311 pass
315 pass
312
316
313 def reload(self):
317 def reload(self):
314 """Reload the raw data from file or URL."""
318 """Reload the raw data from file or URL."""
315 if self.filename is not None:
319 if self.filename is not None:
316 with open(self.filename, self._read_flags) as f:
320 with open(self.filename, self._read_flags) as f:
317 self.data = f.read()
321 self.data = f.read()
318 elif self.url is not None:
322 elif self.url is not None:
319 try:
323 try:
320 try:
324 try:
321 from urllib.request import urlopen # Py3
325 from urllib.request import urlopen # Py3
322 except ImportError:
326 except ImportError:
323 from urllib2 import urlopen
327 from urllib2 import urlopen
324 response = urlopen(self.url)
328 response = urlopen(self.url)
325 self.data = response.read()
329 self.data = response.read()
326 # extract encoding from header, if there is one:
330 # extract encoding from header, if there is one:
327 encoding = None
331 encoding = None
328 for sub in response.headers['content-type'].split(';'):
332 for sub in response.headers['content-type'].split(';'):
329 sub = sub.strip()
333 sub = sub.strip()
330 if sub.startswith('charset'):
334 if sub.startswith('charset'):
331 encoding = sub.split('=')[-1].strip()
335 encoding = sub.split('=')[-1].strip()
332 break
336 break
333 # decode data, if an encoding was specified
337 # decode data, if an encoding was specified
334 if encoding:
338 if encoding:
335 self.data = self.data.decode(encoding, 'replace')
339 self.data = self.data.decode(encoding, 'replace')
336 except:
340 except:
337 self.data = None
341 self.data = None
338
342
339 class TextDisplayObject(DisplayObject):
343 class TextDisplayObject(DisplayObject):
340 """Validate that display data is text"""
344 """Validate that display data is text"""
341 def _check_data(self):
345 def _check_data(self):
342 if self.data is not None and not isinstance(self.data, string_types):
346 if self.data is not None and not isinstance(self.data, string_types):
343 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
347 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
344
348
345 class Pretty(TextDisplayObject):
349 class Pretty(TextDisplayObject):
346
350
347 def _repr_pretty_(self):
351 def _repr_pretty_(self):
348 return self.data
352 return self.data
349
353
350
354
351 class HTML(TextDisplayObject):
355 class HTML(TextDisplayObject):
352
356
353 def _repr_html_(self):
357 def _repr_html_(self):
354 return self.data
358 return self.data
355
359
356 def __html__(self):
360 def __html__(self):
357 """
361 """
358 This method exists to inform other HTML-using modules (e.g. Markupsafe,
362 This method exists to inform other HTML-using modules (e.g. Markupsafe,
359 htmltag, etc) that this object is HTML and does not need things like
363 htmltag, etc) that this object is HTML and does not need things like
360 special characters (<>&) escaped.
364 special characters (<>&) escaped.
361 """
365 """
362 return self._repr_html_()
366 return self._repr_html_()
363
367
364
368
365 class Math(TextDisplayObject):
369 class Math(TextDisplayObject):
366
370
367 def _repr_latex_(self):
371 def _repr_latex_(self):
368 s = self.data.strip('$')
372 s = self.data.strip('$')
369 return "$$%s$$" % s
373 return "$$%s$$" % s
370
374
371
375
372 class Latex(TextDisplayObject):
376 class Latex(TextDisplayObject):
373
377
374 def _repr_latex_(self):
378 def _repr_latex_(self):
375 return self.data
379 return self.data
376
380
377
381
378 class SVG(DisplayObject):
382 class SVG(DisplayObject):
379
383
380 # wrap data in a property, which extracts the <svg> tag, discarding
384 # wrap data in a property, which extracts the <svg> tag, discarding
381 # document headers
385 # document headers
382 _data = None
386 _data = None
383
387
384 @property
388 @property
385 def data(self):
389 def data(self):
386 return self._data
390 return self._data
387
391
388 @data.setter
392 @data.setter
389 def data(self, svg):
393 def data(self, svg):
390 if svg is None:
394 if svg is None:
391 self._data = None
395 self._data = None
392 return
396 return
393 # parse into dom object
397 # parse into dom object
394 from xml.dom import minidom
398 from xml.dom import minidom
395 svg = cast_bytes_py2(svg)
399 svg = cast_bytes_py2(svg)
396 x = minidom.parseString(svg)
400 x = minidom.parseString(svg)
397 # get svg tag (should be 1)
401 # get svg tag (should be 1)
398 found_svg = x.getElementsByTagName('svg')
402 found_svg = x.getElementsByTagName('svg')
399 if found_svg:
403 if found_svg:
400 svg = found_svg[0].toxml()
404 svg = found_svg[0].toxml()
401 else:
405 else:
402 # fallback on the input, trust the user
406 # fallback on the input, trust the user
403 # but this is probably an error.
407 # but this is probably an error.
404 pass
408 pass
405 svg = cast_unicode(svg)
409 svg = cast_unicode(svg)
406 self._data = svg
410 self._data = svg
407
411
408 def _repr_svg_(self):
412 def _repr_svg_(self):
409 return self.data
413 return self.data
410
414
411
415
412 class JSON(TextDisplayObject):
416 class JSON(TextDisplayObject):
413
417
414 def _repr_json_(self):
418 def _repr_json_(self):
415 return self.data
419 return self.data
416
420
417 css_t = """$("head").append($("<link/>").attr({
421 css_t = """$("head").append($("<link/>").attr({
418 rel: "stylesheet",
422 rel: "stylesheet",
419 type: "text/css",
423 type: "text/css",
420 href: "%s"
424 href: "%s"
421 }));
425 }));
422 """
426 """
423
427
424 lib_t1 = """$.getScript("%s", function () {
428 lib_t1 = """$.getScript("%s", function () {
425 """
429 """
426 lib_t2 = """});
430 lib_t2 = """});
427 """
431 """
428
432
429 class Javascript(TextDisplayObject):
433 class Javascript(TextDisplayObject):
430
434
431 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
435 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
432 """Create a Javascript display object given raw data.
436 """Create a Javascript display object given raw data.
433
437
434 When this object is returned by an expression or passed to the
438 When this object is returned by an expression or passed to the
435 display function, it will result in the data being displayed
439 display function, it will result in the data being displayed
436 in the frontend. If the data is a URL, the data will first be
440 in the frontend. If the data is a URL, the data will first be
437 downloaded and then displayed.
441 downloaded and then displayed.
438
442
439 In the Notebook, the containing element will be available as `element`,
443 In the Notebook, the containing element will be available as `element`,
440 and jQuery will be available. The output area starts hidden, so if
444 and jQuery will be available. The output area starts hidden, so if
441 the js appends content to `element` that should be visible, then
445 the js appends content to `element` that should be visible, then
442 it must call `container.show()` to unhide the area.
446 it must call `container.show()` to unhide the area.
443
447
444 Parameters
448 Parameters
445 ----------
449 ----------
446 data : unicode, str or bytes
450 data : unicode, str or bytes
447 The Javascript source code or a URL to download it from.
451 The Javascript source code or a URL to download it from.
448 url : unicode
452 url : unicode
449 A URL to download the data from.
453 A URL to download the data from.
450 filename : unicode
454 filename : unicode
451 Path to a local file to load the data from.
455 Path to a local file to load the data from.
452 lib : list or str
456 lib : list or str
453 A sequence of Javascript library URLs to load asynchronously before
457 A sequence of Javascript library URLs to load asynchronously before
454 running the source code. The full URLs of the libraries should
458 running the source code. The full URLs of the libraries should
455 be given. A single Javascript library URL can also be given as a
459 be given. A single Javascript library URL can also be given as a
456 string.
460 string.
457 css: : list or str
461 css: : list or str
458 A sequence of css files to load before running the source code.
462 A sequence of css files to load before running the source code.
459 The full URLs of the css files should be given. A single css URL
463 The full URLs of the css files should be given. A single css URL
460 can also be given as a string.
464 can also be given as a string.
461 """
465 """
462 if isinstance(lib, string_types):
466 if isinstance(lib, string_types):
463 lib = [lib]
467 lib = [lib]
464 elif lib is None:
468 elif lib is None:
465 lib = []
469 lib = []
466 if isinstance(css, string_types):
470 if isinstance(css, string_types):
467 css = [css]
471 css = [css]
468 elif css is None:
472 elif css is None:
469 css = []
473 css = []
470 if not isinstance(lib, (list,tuple)):
474 if not isinstance(lib, (list,tuple)):
471 raise TypeError('expected sequence, got: %r' % lib)
475 raise TypeError('expected sequence, got: %r' % lib)
472 if not isinstance(css, (list,tuple)):
476 if not isinstance(css, (list,tuple)):
473 raise TypeError('expected sequence, got: %r' % css)
477 raise TypeError('expected sequence, got: %r' % css)
474 self.lib = lib
478 self.lib = lib
475 self.css = css
479 self.css = css
476 super(Javascript, self).__init__(data=data, url=url, filename=filename)
480 super(Javascript, self).__init__(data=data, url=url, filename=filename)
477
481
478 def _repr_javascript_(self):
482 def _repr_javascript_(self):
479 r = ''
483 r = ''
480 for c in self.css:
484 for c in self.css:
481 r += css_t % c
485 r += css_t % c
482 for l in self.lib:
486 for l in self.lib:
483 r += lib_t1 % l
487 r += lib_t1 % l
484 r += self.data
488 r += self.data
485 r += lib_t2*len(self.lib)
489 r += lib_t2*len(self.lib)
486 return r
490 return r
487
491
488 # constants for identifying png/jpeg data
492 # constants for identifying png/jpeg data
489 _PNG = b'\x89PNG\r\n\x1a\n'
493 _PNG = b'\x89PNG\r\n\x1a\n'
490 _JPEG = b'\xff\xd8'
494 _JPEG = b'\xff\xd8'
491
495
492 def _pngxy(data):
496 def _pngxy(data):
493 """read the (width, height) from a PNG header"""
497 """read the (width, height) from a PNG header"""
494 ihdr = data.index(b'IHDR')
498 ihdr = data.index(b'IHDR')
495 # next 8 bytes are width/height
499 # next 8 bytes are width/height
496 w4h4 = data[ihdr+4:ihdr+12]
500 w4h4 = data[ihdr+4:ihdr+12]
497 return struct.unpack('>ii', w4h4)
501 return struct.unpack('>ii', w4h4)
498
502
499 def _jpegxy(data):
503 def _jpegxy(data):
500 """read the (width, height) from a JPEG header"""
504 """read the (width, height) from a JPEG header"""
501 # adapted from http://www.64lines.com/jpeg-width-height
505 # adapted from http://www.64lines.com/jpeg-width-height
502
506
503 idx = 4
507 idx = 4
504 while True:
508 while True:
505 block_size = struct.unpack('>H', data[idx:idx+2])[0]
509 block_size = struct.unpack('>H', data[idx:idx+2])[0]
506 idx = idx + block_size
510 idx = idx + block_size
507 if data[idx:idx+2] == b'\xFF\xC0':
511 if data[idx:idx+2] == b'\xFF\xC0':
508 # found Start of Frame
512 # found Start of Frame
509 iSOF = idx
513 iSOF = idx
510 break
514 break
511 else:
515 else:
512 # read another block
516 # read another block
513 idx += 2
517 idx += 2
514
518
515 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
519 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
516 return w, h
520 return w, h
517
521
518 class Image(DisplayObject):
522 class Image(DisplayObject):
519
523
520 _read_flags = 'rb'
524 _read_flags = 'rb'
521 _FMT_JPEG = u'jpeg'
525 _FMT_JPEG = u'jpeg'
522 _FMT_PNG = u'png'
526 _FMT_PNG = u'png'
523 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
527 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
524
528
525 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
529 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
526 """Create a PNG/JPEG image object given raw data.
530 """Create a PNG/JPEG image object given raw data.
527
531
528 When this object is returned by an input cell or passed to the
532 When this object is returned by an input cell or passed to the
529 display function, it will result in the image being displayed
533 display function, it will result in the image being displayed
530 in the frontend.
534 in the frontend.
531
535
532 Parameters
536 Parameters
533 ----------
537 ----------
534 data : unicode, str or bytes
538 data : unicode, str or bytes
535 The raw image data or a URL or filename to load the data from.
539 The raw image data or a URL or filename to load the data from.
536 This always results in embedded image data.
540 This always results in embedded image data.
537 url : unicode
541 url : unicode
538 A URL to download the data from. If you specify `url=`,
542 A URL to download the data from. If you specify `url=`,
539 the image data will not be embedded unless you also specify `embed=True`.
543 the image data will not be embedded unless you also specify `embed=True`.
540 filename : unicode
544 filename : unicode
541 Path to a local file to load the data from.
545 Path to a local file to load the data from.
542 Images from a file are always embedded.
546 Images from a file are always embedded.
543 format : unicode
547 format : unicode
544 The format of the image data (png/jpeg/jpg). If a filename or URL is given
548 The format of the image data (png/jpeg/jpg). If a filename or URL is given
545 for format will be inferred from the filename extension.
549 for format will be inferred from the filename extension.
546 embed : bool
550 embed : bool
547 Should the image data be embedded using a data URI (True) or be
551 Should the image data be embedded using a data URI (True) or be
548 loaded using an <img> tag. Set this to True if you want the image
552 loaded using an <img> tag. Set this to True if you want the image
549 to be viewable later with no internet connection in the notebook.
553 to be viewable later with no internet connection in the notebook.
550
554
551 Default is `True`, unless the keyword argument `url` is set, then
555 Default is `True`, unless the keyword argument `url` is set, then
552 default value is `False`.
556 default value is `False`.
553
557
554 Note that QtConsole is not able to display images if `embed` is set to `False`
558 Note that QtConsole is not able to display images if `embed` is set to `False`
555 width : int
559 width : int
556 Width to which to constrain the image in html
560 Width to which to constrain the image in html
557 height : int
561 height : int
558 Height to which to constrain the image in html
562 Height to which to constrain the image in html
559 retina : bool
563 retina : bool
560 Automatically set the width and height to half of the measured
564 Automatically set the width and height to half of the measured
561 width and height.
565 width and height.
562 This only works for embedded images because it reads the width/height
566 This only works for embedded images because it reads the width/height
563 from image data.
567 from image data.
564 For non-embedded images, you can just set the desired display width
568 For non-embedded images, you can just set the desired display width
565 and height directly.
569 and height directly.
566
570
567 Examples
571 Examples
568 --------
572 --------
569 # embedded image data, works in qtconsole and notebook
573 # embedded image data, works in qtconsole and notebook
570 # when passed positionally, the first arg can be any of raw image data,
574 # when passed positionally, the first arg can be any of raw image data,
571 # a URL, or a filename from which to load image data.
575 # a URL, or a filename from which to load image data.
572 # The result is always embedding image data for inline images.
576 # The result is always embedding image data for inline images.
573 Image('http://www.google.fr/images/srpr/logo3w.png')
577 Image('http://www.google.fr/images/srpr/logo3w.png')
574 Image('/path/to/image.jpg')
578 Image('/path/to/image.jpg')
575 Image(b'RAW_PNG_DATA...')
579 Image(b'RAW_PNG_DATA...')
576
580
577 # Specifying Image(url=...) does not embed the image data,
581 # Specifying Image(url=...) does not embed the image data,
578 # it only generates `<img>` tag with a link to the source.
582 # it only generates `<img>` tag with a link to the source.
579 # This will not work in the qtconsole or offline.
583 # This will not work in the qtconsole or offline.
580 Image(url='http://www.google.fr/images/srpr/logo3w.png')
584 Image(url='http://www.google.fr/images/srpr/logo3w.png')
581
585
582 """
586 """
583 if filename is not None:
587 if filename is not None:
584 ext = self._find_ext(filename)
588 ext = self._find_ext(filename)
585 elif url is not None:
589 elif url is not None:
586 ext = self._find_ext(url)
590 ext = self._find_ext(url)
587 elif data is None:
591 elif data is None:
588 raise ValueError("No image data found. Expecting filename, url, or data.")
592 raise ValueError("No image data found. Expecting filename, url, or data.")
589 elif isinstance(data, string_types) and (
593 elif isinstance(data, string_types) and (
590 data.startswith('http') or _safe_exists(data)
594 data.startswith('http') or _safe_exists(data)
591 ):
595 ):
592 ext = self._find_ext(data)
596 ext = self._find_ext(data)
593 else:
597 else:
594 ext = None
598 ext = None
595
599
596 if ext is not None:
600 if ext is not None:
597 format = ext.lower()
601 format = ext.lower()
598 if ext == u'jpg' or ext == u'jpeg':
602 if ext == u'jpg' or ext == u'jpeg':
599 format = self._FMT_JPEG
603 format = self._FMT_JPEG
600 if ext == u'png':
604 if ext == u'png':
601 format = self._FMT_PNG
605 format = self._FMT_PNG
602 elif isinstance(data, bytes) and format == 'png':
606 elif isinstance(data, bytes) and format == 'png':
603 # infer image type from image data header,
607 # infer image type from image data header,
604 # only if format might not have been specified.
608 # only if format might not have been specified.
605 if data[:2] == _JPEG:
609 if data[:2] == _JPEG:
606 format = 'jpeg'
610 format = 'jpeg'
607
611
608 self.format = unicode_type(format).lower()
612 self.format = unicode_type(format).lower()
609 self.embed = embed if embed is not None else (url is None)
613 self.embed = embed if embed is not None else (url is None)
610
614
611 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
615 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
612 raise ValueError("Cannot embed the '%s' image format" % (self.format))
616 raise ValueError("Cannot embed the '%s' image format" % (self.format))
613 self.width = width
617 self.width = width
614 self.height = height
618 self.height = height
615 self.retina = retina
619 self.retina = retina
616 super(Image, self).__init__(data=data, url=url, filename=filename)
620 super(Image, self).__init__(data=data, url=url, filename=filename)
617
621
618 if retina:
622 if retina:
619 self._retina_shape()
623 self._retina_shape()
620
624
621 def _retina_shape(self):
625 def _retina_shape(self):
622 """load pixel-doubled width and height from image data"""
626 """load pixel-doubled width and height from image data"""
623 if not self.embed:
627 if not self.embed:
624 return
628 return
625 if self.format == 'png':
629 if self.format == 'png':
626 w, h = _pngxy(self.data)
630 w, h = _pngxy(self.data)
627 elif self.format == 'jpeg':
631 elif self.format == 'jpeg':
628 w, h = _jpegxy(self.data)
632 w, h = _jpegxy(self.data)
629 else:
633 else:
630 # retina only supports png
634 # retina only supports png
631 return
635 return
632 self.width = w // 2
636 self.width = w // 2
633 self.height = h // 2
637 self.height = h // 2
634
638
635 def reload(self):
639 def reload(self):
636 """Reload the raw data from file or URL."""
640 """Reload the raw data from file or URL."""
637 if self.embed:
641 if self.embed:
638 super(Image,self).reload()
642 super(Image,self).reload()
639 if self.retina:
643 if self.retina:
640 self._retina_shape()
644 self._retina_shape()
641
645
642 def _repr_html_(self):
646 def _repr_html_(self):
643 if not self.embed:
647 if not self.embed:
644 width = height = ''
648 width = height = ''
645 if self.width:
649 if self.width:
646 width = ' width="%d"' % self.width
650 width = ' width="%d"' % self.width
647 if self.height:
651 if self.height:
648 height = ' height="%d"' % self.height
652 height = ' height="%d"' % self.height
649 return u'<img src="%s"%s%s/>' % (self.url, width, height)
653 return u'<img src="%s"%s%s/>' % (self.url, width, height)
650
654
651 def _data_and_metadata(self):
655 def _data_and_metadata(self):
652 """shortcut for returning metadata with shape information, if defined"""
656 """shortcut for returning metadata with shape information, if defined"""
653 md = {}
657 md = {}
654 if self.width:
658 if self.width:
655 md['width'] = self.width
659 md['width'] = self.width
656 if self.height:
660 if self.height:
657 md['height'] = self.height
661 md['height'] = self.height
658 if md:
662 if md:
659 return self.data, md
663 return self.data, md
660 else:
664 else:
661 return self.data
665 return self.data
662
666
663 def _repr_png_(self):
667 def _repr_png_(self):
664 if self.embed and self.format == u'png':
668 if self.embed and self.format == u'png':
665 return self._data_and_metadata()
669 return self._data_and_metadata()
666
670
667 def _repr_jpeg_(self):
671 def _repr_jpeg_(self):
668 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
672 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
669 return self._data_and_metadata()
673 return self._data_and_metadata()
670
674
671 def _find_ext(self, s):
675 def _find_ext(self, s):
672 return unicode_type(s.split('.')[-1].lower())
676 return unicode_type(s.split('.')[-1].lower())
673
677
674
678
675 def clear_output(wait=False):
679 def clear_output(wait=False):
676 """Clear the output of the current cell receiving output.
680 """Clear the output of the current cell receiving output.
677
681
678 Parameters
682 Parameters
679 ----------
683 ----------
680 wait : bool [default: false]
684 wait : bool [default: false]
681 Wait to clear the output until new output is available to replace it."""
685 Wait to clear the output until new output is available to replace it."""
682 from IPython.core.interactiveshell import InteractiveShell
686 from IPython.core.interactiveshell import InteractiveShell
683 if InteractiveShell.initialized():
687 if InteractiveShell.initialized():
684 InteractiveShell.instance().display_pub.clear_output(wait)
688 InteractiveShell.instance().display_pub.clear_output(wait)
685 else:
689 else:
686 from IPython.utils import io
690 from IPython.utils import io
687 print('\033[2K\r', file=io.stdout, end='')
691 print('\033[2K\r', file=io.stdout, end='')
688 io.stdout.flush()
692 io.stdout.flush()
689 print('\033[2K\r', file=io.stderr, end='')
693 print('\033[2K\r', file=io.stderr, end='')
690 io.stderr.flush()
694 io.stderr.flush()
General Comments 0
You need to be logged in to leave comments. Login now