##// END OF EJS Templates
Mostly final version of display data....
Brian Granger -
Show More
@@ -1,5 +1,5 b''
1 1 # -*- coding: utf-8 -*-
2 """An interface for publishing data related to the display of objects.
2 """An interface for publishing rich data to frontends.
3 3
4 4 Authors:
5 5
@@ -35,6 +35,69 b' class DisplayPublisher(Configurable):'
35 35 raise TypeError('metadata must be a dict, got: %r' % data)
36 36
37 37 def publish(self, source, data, metadata=None):
38 """Publish data and metadata to all frontends."""
39 pass
38 """Publish data and metadata to all frontends.
40 39
40 See the ``display_data`` message in the messaging documentation for
41 more details about this message type.
42
43 Parameters
44 ----------
45 source : str
46 A string that give the function or method that created the data,
47 such as 'IPython.core.page'.
48 data : dict
49 A dictionary having keys that are valid MIME types (like
50 'text/plain' or 'image/svg+xml') and values that are the data for
51 that MIME type. The data itself must be a JSON'able data
52 structure. Minimally all data should have the 'text/plain' data,
53 which can be displayed by all frontends. If more than the plain
54 text is given, it is up to the frontend to decide which
55 representation to use.
56 metadata : dict
57 A dictionary for metadata related to the data. This can contain
58 arbitrary key, value pairs that frontends can use to interpret
59 the data.
60 """
61 from IPython.utils import io
62 # The default is to simply write the plain text data using io.Term.
63 if data.has_key('text/plain'):
64 print >>io.Term.cout, data['text/plain']
65
66
67 def publish_display_data(source, text, svg=None, png=None,
68 html=None, metadata=None):
69 """Publish a display data to the frontends.
70
71 This function is a high level helper for the publishing of display data.
72 It handle a number of common MIME types in a clean API. For other MIME
73 types, use ``get_ipython().display_pub.publish`` directly.
74
75 Parameters
76 ----------
77 text : str/unicode
78 The string representation of the plot.
79
80 svn : str/unicode
81 The raw svg data of the plot.
82
83 png : ???
84 The raw png data of the plot.
85
86 metadata : dict, optional [default empty]
87 Allows for specification of additional information about the plot data.
88 """
89 from IPython.core.interactiveshell import InteractiveShell
90
91 data_dict = {}
92 data_dict['text/plain'] = text
93 if svg is not None:
94 data_dict['image/svg+xml'] = svg
95 if png is not None:
96 data_dict['image/png'] = png
97 if html is not None:
98 data_dict['text/html'] = html
99 InteractiveShell.instance().display_pub.publish(
100 source,
101 data_dict,
102 metadata
103 )
@@ -41,6 +41,7 b' from IPython.core.builtin_trap import BuiltinTrap'
41 41 from IPython.core.compilerop import CachingCompiler
42 42 from IPython.core.display_trap import DisplayTrap
43 43 from IPython.core.displayhook import DisplayHook
44 from IPython.core.displaypub import DisplayPublisher
44 45 from IPython.core.error import TryNext, UsageError
45 46 from IPython.core.extensions import ExtensionManager
46 47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
@@ -150,6 +151,8 b' class InteractiveShell(Configurable, Magic):'
150 151 debug = CBool(False, config=True)
151 152 deep_reload = CBool(False, config=True)
152 153 displayhook_class = Type(DisplayHook)
154 display_pub_class = Type(DisplayPublisher)
155
153 156 exit_now = CBool(False)
154 157 # Monotonically increasing execution counter
155 158 execution_count = Int(1)
@@ -284,6 +287,7 b' class InteractiveShell(Configurable, Magic):'
284 287 self.init_io()
285 288 self.init_traceback_handlers(custom_exceptions)
286 289 self.init_prompts()
290 self.init_display_pub()
287 291 self.init_displayhook()
288 292 self.init_reload_doctest()
289 293 self.init_magics()
@@ -481,6 +485,9 b' class InteractiveShell(Configurable, Magic):'
481 485 # will initialize that object and all prompt related information.
482 486 pass
483 487
488 def init_display_pub(self):
489 self.display_pub = self.display_pub_class(config=self.config)
490
484 491 def init_displayhook(self):
485 492 # Initialize displayhook, set in/out prompts and printing system
486 493 self.displayhook = self.displayhook_class(
@@ -183,6 +183,21 b' class IPythonWidget(FrontendWidget):'
183 183 self._append_html(self._make_out_prompt(prompt_number))
184 184 self._append_plain_text(content['data']+self.output_sep2)
185 185
186 def _handle_display_data(self, msg):
187 """ The base handler for the ``display_data`` message.
188 """
189 # For now, we don't display data from other frontends, but we
190 # eventually will as this allows all frontends to monitor the display
191 # data. But we need to figure out how to handle this in the GUI.
192 if not self._hidden and self._is_from_this_session(msg):
193 source = msg['content']['source']
194 data = msg['content']['data']
195 metadata = msg['content']['metadata']
196 # In the regular IPythonWidget, we simply print the plain text
197 # representation.
198 if data.has_key('text/plain'):
199 self._append_plain_text(data['text/plain'])
200
186 201 def _started_channels(self):
187 202 """ Reimplemented to make a history request.
188 203 """
@@ -444,7 +459,7 b' class IPythonWidget(FrontendWidget):'
444 459 else:
445 460 self._page(item['text'], html=False)
446 461
447 #------ Trait change handlers ---------------------------------------------
462 #------ Trait change handlers --------------------------------------------
448 463
449 464 def _style_sheet_changed(self):
450 465 """ Set the style sheets of the underlying widgets.
@@ -464,4 +479,4 b' class IPythonWidget(FrontendWidget):'
464 479 self._highlighter.set_style(self.syntax_style)
465 480 else:
466 481 self._highlighter.set_style_sheet(self.style_sheet)
467
482
@@ -56,7 +56,31 b' class RichIPythonWidget(IPythonWidget):'
56 56 menu.addAction('Save SVG As...',
57 57 lambda: save_svg(svg, self._control))
58 58 return menu
59
59
60 #---------------------------------------------------------------------------
61 # 'BaseFrontendMixin' abstract interface
62 #---------------------------------------------------------------------------
63
64 def _handle_display_data(self, msg):
65 """ A handler for ``display_data`` message that handles html and svg.
66 """
67 if not self._hidden and self._is_from_this_session(msg):
68 source = msg['content']['source']
69 data = msg['content']['data']
70 metadata = msg['content']['metadata']
71 # Try to use the svg or html representations.
72 # FIXME: Is this the right ordering of things to try?
73 if data.has_key('image/svg+xml'):
74 svg = data['image/svg+xml']
75 # TODO: try/except this call.
76 self._append_svg(svg)
77 elif data.has_key('text/html'):
78 html = data['text/html']
79 self._append_html(html)
80 else:
81 # Default back to the plain text representation.
82 return super(RichIPythonWidget, self)._handle_display_data(msg)
83
60 84 #---------------------------------------------------------------------------
61 85 # 'FrontendWidget' protected interface
62 86 #---------------------------------------------------------------------------
@@ -65,20 +89,11 b' class RichIPythonWidget(IPythonWidget):'
65 89 """ Reimplemented to handle matplotlib plot payloads.
66 90 """
67 91 if item['source'] == self._payload_source_plot:
92 # TODO: remove this as all plot data is coming back through the
93 # display_data message type.
68 94 if item['format'] == 'svg':
69 95 svg = item['data']
70 try:
71 image = svg_to_image(svg)
72 except ValueError:
73 self._append_plain_text('Received invalid plot data.')
74 else:
75 format = self._add_image(image)
76 self._name_to_svg[str(format.name())] = svg
77 format.setProperty(self._svg_text_format_property, svg)
78 cursor = self._get_end_cursor()
79 cursor.insertBlock()
80 cursor.insertImage(format)
81 cursor.insertBlock()
96 self._append_svg(svg)
82 97 return True
83 98 else:
84 99 # Add other plot formats here!
@@ -90,6 +105,22 b' class RichIPythonWidget(IPythonWidget):'
90 105 # 'RichIPythonWidget' protected interface
91 106 #---------------------------------------------------------------------------
92 107
108 def _append_svg(self, svg):
109 """ Append raw svg data to the widget.
110 """
111 try:
112 image = svg_to_image(svg)
113 except ValueError:
114 self._append_plain_text('Received invalid plot data.')
115 else:
116 format = self._add_image(image)
117 self._name_to_svg[str(format.name())] = svg
118 format.setProperty(self._svg_text_format_property, svg)
119 cursor = self._get_end_cursor()
120 cursor.insertBlock()
121 cursor.insertImage(format)
122 cursor.insertBlock()
123
93 124 def _add_image(self, image):
94 125 """ Adds the specified QImage to the document and returns a
95 126 QTextImageFormat that references it.
@@ -192,4 +223,4 b' class RichIPythonWidget(IPythonWidget):'
192 223
193 224 else:
194 225 return '<b>Unrecognized image format</b>'
195
226
@@ -101,6 +101,9 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):'
101 101 # Emitted when a message of type 'pyerr' is received.
102 102 pyerr_received = QtCore.pyqtSignal(object)
103 103
104 # Emitted when a message of type 'display_data' is received
105 display_data_received = QtCore.pyqtSignal(object)
106
104 107 # Emitted when a crash report message is received from the kernel's
105 108 # last-resort sys.excepthook.
106 109 crash_received = QtCore.pyqtSignal(object)
@@ -117,7 +120,6 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):'
117 120 """
118 121 # Emit the generic signal.
119 122 self.message_received.emit(msg)
120
121 123 # Emit signals for specialized message types.
122 124 msg_type = msg['msg_type']
123 125 signal = getattr(self, msg_type + '_received', None)
@@ -91,6 +91,8 b' class Kernel(Configurable):'
91 91 self.shell = ZMQInteractiveShell.instance()
92 92 self.shell.displayhook.session = self.session
93 93 self.shell.displayhook.pub_socket = self.pub_socket
94 self.shell.display_pub.session = self.session
95 self.shell.display_pub.pub_socket = self.pub_socket
94 96
95 97 # TMP - hack while developing
96 98 self.shell._reply_content = None
@@ -194,6 +196,7 b' class Kernel(Configurable):'
194 196
195 197 # Set the parent message of the display hook and out streams.
196 198 shell.displayhook.set_parent(parent)
199 shell.display_pub.set_parent(parent)
197 200 sys.stdout.set_parent(parent)
198 201 sys.stderr.set_parent(parent)
199 202
@@ -14,7 +14,7 b' from matplotlib.backends.backend_svg import new_figure_manager'
14 14 from matplotlib._pylab_helpers import Gcf
15 15
16 16 # Local imports.
17 from backend_payload import add_plot_payload
17 from IPython.core.displaypub import publish_display_data
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Functions
@@ -85,7 +85,11 b' def send_svg_canvas(canvas):'
85 85 canvas.figure.set_facecolor('white')
86 86 canvas.figure.set_edgecolor('white')
87 87 try:
88 add_plot_payload('svg', svg_from_canvas(canvas))
88 publish_display_data(
89 'IPython.zmq.pylab.backend_inline.send_svg_canvas',
90 '<Matplotlib Plot>',
91 svg=svg_from_canvas(canvas)
92 )
89 93 finally:
90 94 canvas.figure.set_facecolor(fc)
91 95 canvas.figure.set_edgecolor(ec)
@@ -26,6 +26,7 b' from IPython.core.interactiveshell import ('
26 26 )
27 27 from IPython.core import page
28 28 from IPython.core.displayhook import DisplayHook
29 from IPython.core.displaypub import DisplayPublisher
29 30 from IPython.core.macro import Macro
30 31 from IPython.core.payloadpage import install_payload_page
31 32 from IPython.utils import io
@@ -75,10 +76,34 b' class ZMQDisplayHook(DisplayHook):'
75 76 self.msg = None
76 77
77 78
79 class ZMQDisplayPublisher(DisplayPublisher):
80 """A ``DisplayPublisher`` that published data using a ZeroMQ PUB socket."""
81
82 session = Instance(Session)
83 pub_socket = Instance('zmq.Socket')
84 parent_header = Dict({})
85
86 def set_parent(self, parent):
87 """Set the parent for outbound messages."""
88 self.parent_header = extract_header(parent)
89
90 def publish(self, source, data, metadata=None):
91 if metadata is None:
92 metadata = {}
93 self._validate_data(source, data, metadata)
94 msg = self.session.msg(u'display_data', {}, parent=self.parent_header)
95 msg['content']['source'] = source
96 msg['content']['data'] = data
97 msg['content']['metadata'] = metadata
98 self.pub_socket.send_json(msg)
99
100
78 101 class ZMQInteractiveShell(InteractiveShell):
79 102 """A subclass of InteractiveShell for ZMQ."""
80 103
81 104 displayhook_class = Type(ZMQDisplayHook)
105 display_pub_class = Type(ZMQDisplayPublisher)
106
82 107 keepkernel_on_exit = None
83 108
84 109 def init_environment(self):
@@ -700,30 +700,32 b" socket with the names 'stdin' and 'stdin_reply'. This will allow other clients"
700 700 to monitor/display kernel interactions and possibly replay them to their user
701 701 or otherwise expose them.
702 702
703 Representation Data
704 -------------------
703 Display Data
704 ------------
705 705
706 This type of message is used to bring back representations (text, html, svg,
707 etc.) of Python objects to the frontend. Each message can have multiple
708 representations of the object; it is up to the frontend to decide which to use
709 and how. A single message should contain the different representations of a
710 single Python object. Each representation should be a JSON'able data structure,
711 and should be a valid MIME type.
706 This type of message is used to bring back data that should be diplayed (text,
707 html, svg, etc.) in the frontends. This data is published to all frontends.
708 Each message can have multiple representations of the data; it is up to the
709 frontend to decide which to use and how. A single message should contain all
710 possible representations of the same information. Each representation should
711 be a JSON'able data structure, and should be a valid MIME type.
712 712
713 713 Some questions remain about this design:
714 714
715 * Do we use this message type for pyout/displayhook?
716 * What is the best way to organize the content dict of the message?
715 * Do we use this message type for pyout/displayhook? Probably not, because
716 the displayhook also has to handle the Out prompt display. On the other hand
717 we could put that information into the metadata secion.
717 718
718 Message type: ``repr_data``::
719 Message type: ``display_data``::
719 720
720 # Option 1: if we only allow a single source.
721 721 content = {
722 722 'source' : str # Who create the data
723 723 'data' : dict # {'mimetype1' : data1, 'mimetype2' : data2}
724 724 'metadata' : dict # Any metadata that describes the data
725 725 }
726 726
727 Other options for ``display_data`` content::
728
727 729 # Option 2: allowing for a different source for each representation,
728 730 but not keyed by anything.
729 731 content = {
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now