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