##// END OF EJS Templates
add Kernel-side widgets
MinRK -
Show More
@@ -0,0 +1,2 b''
1 from .manager import *
2 from .widget import *
@@ -0,0 +1,115 b''
1 """Base class to manage widgets"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 from weakref import ref
15
16 from IPython.config import LoggingConfigurable
17 from IPython.core.prompts import LazyEvaluate
18 from IPython.core.getipython import get_ipython
19 from IPython.utils.traitlets import Instance, Unicode, Dict, Any
20
21 #-----------------------------------------------------------------------------
22 # Code
23 #-----------------------------------------------------------------------------
24
25 def lazy_keys(dikt):
26 """Return lazy-evaluated string representation of a dictionary's keys
27
28 Key list is only constructed if it will actually be used.
29 Used for debug-logging.
30 """
31 return LazyEvaluate(lambda d: list(d.keys()))
32
33 class WidgetManager(LoggingConfigurable):
34 """Manager for Widgets in the Kernel"""
35
36 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
37 def _shell_default(self):
38 return get_ipython()
39 iopub_socket = Any()
40 def _iopub_socket_default(self):
41 return self.shell.parent.iopub_socket
42 session = Instance('IPython.kernel.zmq.session.Session')
43 def _session_default(self):
44 if self.shell is None:
45 return
46 return self.shell.parent.session
47
48 widgets = Dict()
49
50 # Public APIs
51
52 def register_widget(self, widget):
53 """Register a new widget"""
54 self.widgets[widget.widget_id] = ref(widget)
55 widget.shell = self.shell
56 widget.iopub_socket = self.iopub_socket
57 widget.create()
58 return widget.widget_id
59
60 def unregister_widget(self, widget_id):
61 """Unregister a widget, and destroy its counterpart"""
62 # unlike get_widget, this should raise a KeyError
63 widget_ref = self.widgets.pop(widget_id)
64 widget = widget_ref()
65 if widget is None:
66 # already destroyed, nothing to do
67 return
68 widget.destroy()
69
70 def get_widget(self, widget_id):
71 """Get a widget with a particular id
72
73 Returns the widget if found, otherwise None.
74
75 This will not raise an error,
76 it will log messages if the widget cannot be found.
77 """
78 if widget_id not in self.widgets:
79 self.log.error("No such widget: %s", widget_id)
80 self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
81 return
82 # call, because we store weakrefs
83 widget = self.widgets[widget_id]()
84 if widget is None:
85 self.log.error("Widget %s has been removed", widget_id)
86 del self.widgets[widget_id]
87 self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
88 return
89 return widget
90
91 # Message handlers
92
93 def widget_update(self, stream, ident, msg):
94 """Handler for widget_update messages"""
95 content = msg['content']
96 widget_id = content['widget_id']
97 widget = self.get_widget(widget_id)
98 if widget is None:
99 # no such widget
100 return
101 widget.handle_update(content['data'])
102
103 def widget_destroy(self, stream, ident, msg):
104 """Handler for widget_destroy messages"""
105 content = msg['content']
106 widget_id = content['widget_id']
107 widget = self.get_widget(widget_id)
108 if widget is None:
109 # no such widget
110 return
111 widget.handle_destroy(content['data'])
112 del self.widgets[widget_id]
113
114
115 __all__ = ['WidgetManager']
@@ -0,0 +1,92 b''
1 """Base class for a Widget"""
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 import uuid
15
16 from IPython.config import LoggingConfigurable
17 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
18
19 #-----------------------------------------------------------------------------
20 # Code
21 #-----------------------------------------------------------------------------
22
23 class Widget(LoggingConfigurable):
24
25 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
26 def _shell_default(self):
27 return get_ipython()
28 iopub_socket = Any()
29 def _iopub_socket_default(self):
30 return self.shell.parent.iopub_socket
31 session = Instance('IPython.kernel.zmq.session.Session')
32 def _session_default(self):
33 if self.shell is None:
34 return
35 return self.shell.parent.session
36
37 topic = Bytes()
38 def _topic_default(self):
39 return ('widget-%s' % self.widget_id).encode('ascii')
40
41 _destroy_data = Dict(help="data dict, if any, to be included in widget_destroy")
42 _create_data = Dict(help="data dict, if any, to be included in widget_create")
43
44 _destroyed = Bool(False)
45 widget_type = Unicode('widget')
46 widget_id = Unicode()
47 def _widget_id_default(self):
48 return uuid.uuid4().hex
49
50 def _publish_msg(self, msg_type, data=None, **keys):
51 """Helper for sending a widget message on IOPub"""
52 data = {} if data is None else data
53 self.session.send(self.iopub_socket, msg_type,
54 dict(data=data, widget_id=self.widget_id, **keys),
55 ident=self.topic,
56 )
57
58 def __del__(self):
59 """trigger destroy on gc"""
60 self.destroy()
61
62 # publishing messages
63
64 def create(self):
65 """Create the frontend-side version of this widget"""
66 self._publish_msg('widget_create', self._create_data, widget_type = self.widget_type)
67
68 def destroy(self):
69 """Destroy the frontend-side version of this widget"""
70 if self._destroyed:
71 # only destroy once
72 return
73 self._publish_msg('widget_destroy', self._destroy_data)
74 self._destroyed = True
75
76 def update(self, data=None):
77 """Update the frontend-side version of this widget"""
78 self._publish_msg('widget_update', data)
79
80 # handling of incoming messages
81
82 def handle_destroy(self, data):
83 """Handle a widget_destroy message"""
84 self.log.debug("handle_destroy %s", data)
85
86 def handle_update(self, data):
87 """Handle a widget_update message"""
88 self.log.debug("handle_update %s", data)
89 self.update_data = data
90
91
92 __all__ = ['Widget']
@@ -507,6 +507,7 b' class InteractiveShell(SingletonConfigurable):'
507 507 self.init_pdb()
508 508 self.init_extension_manager()
509 509 self.init_payload()
510 self.init_widgets()
510 511 self.hooks.late_startup_hook()
511 512 atexit.register(self.atexit_operations)
512 513
@@ -2319,6 +2320,14 b' class InteractiveShell(SingletonConfigurable):'
2319 2320 self.configurables.append(self.payload_manager)
2320 2321
2321 2322 #-------------------------------------------------------------------------
2323 # Things related to widgets
2324 #-------------------------------------------------------------------------
2325
2326 def init_widgets(self):
2327 # not implemented in the base class
2328 pass
2329
2330 #-------------------------------------------------------------------------
2322 2331 # Things related to the prefilter
2323 2332 #-------------------------------------------------------------------------
2324 2333
@@ -168,11 +168,17 b' class Kernel(Configurable):'
168 168 for msg_type in msg_types:
169 169 self.shell_handlers[msg_type] = getattr(self, msg_type)
170 170
171 widget_msg_types = [ 'widget_update', 'widget_destroy' ]
172 widget_manager = self.shell.widget_manager
173 for msg_type in widget_msg_types:
174 self.shell_handlers[msg_type] = getattr(widget_manager, msg_type)
175
171 176 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
172 177 self.control_handlers = {}
173 178 for msg_type in control_msg_types:
174 179 self.control_handlers[msg_type] = getattr(self, msg_type)
175 180
181
176 182 def dispatch_control(self, msg):
177 183 """dispatch control requests"""
178 184 idents,msg = self.session.feed_identities(msg, copy=False)
@@ -49,6 +49,7 b' from IPython.utils.warn import error'
49 49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 51 from IPython.kernel.zmq.session import extract_header
52 from IPython.kernel.widgets import WidgetManager
52 53 from session import Session
53 54
54 55 #-----------------------------------------------------------------------------
@@ -594,6 +595,9 b' class ZMQInteractiveShell(InteractiveShell):'
594 595 self.register_magics(KernelMagics)
595 596 self.magics_manager.register_alias('ed', 'edit')
596 597
598 def init_widgets(self):
599 self.widget_manager = WidgetManager(shell=self, parent=self)
600 self.configurables.append(self.widget_manager)
597 601
598 602
599 603 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now