Show More
@@ -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 | self.init_pdb() |
|
507 | self.init_pdb() | |
508 | self.init_extension_manager() |
|
508 | self.init_extension_manager() | |
509 | self.init_payload() |
|
509 | self.init_payload() | |
|
510 | self.init_widgets() | |||
510 | self.hooks.late_startup_hook() |
|
511 | self.hooks.late_startup_hook() | |
511 | atexit.register(self.atexit_operations) |
|
512 | atexit.register(self.atexit_operations) | |
512 |
|
513 | |||
@@ -2317,7 +2318,15 b' class InteractiveShell(SingletonConfigurable):' | |||||
2317 | def init_payload(self): |
|
2318 | def init_payload(self): | |
2318 | self.payload_manager = PayloadManager(parent=self) |
|
2319 | self.payload_manager = PayloadManager(parent=self) | |
2319 | self.configurables.append(self.payload_manager) |
|
2320 | self.configurables.append(self.payload_manager) | |
2320 |
|
2321 | |||
|
2322 | #------------------------------------------------------------------------- | |||
|
2323 | # Things related to widgets | |||
|
2324 | #------------------------------------------------------------------------- | |||
|
2325 | ||||
|
2326 | def init_widgets(self): | |||
|
2327 | # not implemented in the base class | |||
|
2328 | pass | |||
|
2329 | ||||
2321 | #------------------------------------------------------------------------- |
|
2330 | #------------------------------------------------------------------------- | |
2322 | # Things related to the prefilter |
|
2331 | # Things related to the prefilter | |
2323 | #------------------------------------------------------------------------- |
|
2332 | #------------------------------------------------------------------------- |
@@ -168,11 +168,17 b' class Kernel(Configurable):' | |||||
168 | for msg_type in msg_types: |
|
168 | for msg_type in msg_types: | |
169 | self.shell_handlers[msg_type] = getattr(self, msg_type) |
|
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 | control_msg_types = msg_types + [ 'clear_request', 'abort_request' ] |
|
176 | control_msg_types = msg_types + [ 'clear_request', 'abort_request' ] | |
172 | self.control_handlers = {} |
|
177 | self.control_handlers = {} | |
173 | for msg_type in control_msg_types: |
|
178 | for msg_type in control_msg_types: | |
174 | self.control_handlers[msg_type] = getattr(self, msg_type) |
|
179 | self.control_handlers[msg_type] = getattr(self, msg_type) | |
175 |
|
180 | |||
|
181 | ||||
176 | def dispatch_control(self, msg): |
|
182 | def dispatch_control(self, msg): | |
177 | """dispatch control requests""" |
|
183 | """dispatch control requests""" | |
178 | idents,msg = self.session.feed_identities(msg, copy=False) |
|
184 | idents,msg = self.session.feed_identities(msg, copy=False) |
@@ -49,6 +49,7 b' from IPython.utils.warn import error' | |||||
49 | from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook |
|
49 | from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook | |
50 | from IPython.kernel.zmq.datapub import ZMQDataPublisher |
|
50 | from IPython.kernel.zmq.datapub import ZMQDataPublisher | |
51 | from IPython.kernel.zmq.session import extract_header |
|
51 | from IPython.kernel.zmq.session import extract_header | |
|
52 | from IPython.kernel.widgets import WidgetManager | |||
52 | from session import Session |
|
53 | from session import Session | |
53 |
|
54 | |||
54 | #----------------------------------------------------------------------------- |
|
55 | #----------------------------------------------------------------------------- | |
@@ -593,7 +594,10 b' class ZMQInteractiveShell(InteractiveShell):' | |||||
593 | super(ZMQInteractiveShell, self).init_magics() |
|
594 | super(ZMQInteractiveShell, self).init_magics() | |
594 | self.register_magics(KernelMagics) |
|
595 | self.register_magics(KernelMagics) | |
595 | self.magics_manager.register_alias('ed', 'edit') |
|
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 | InteractiveShellABC.register(ZMQInteractiveShell) |
|
603 | InteractiveShellABC.register(ZMQInteractiveShell) |
General Comments 0
You need to be logged in to leave comments.
Login now