diff --git a/IPython/html/static/notebook/js/widget.js b/IPython/html/static/notebook/js/widget.js
index e7c667d..94e2b72 100644
--- a/IPython/html/static/notebook/js/widget.js
+++ b/IPython/html/static/notebook/js/widget.js
@@ -31,6 +31,7 @@ var IPython = (function (IPython) {
};
WidgetManager.prototype.init_kernel = function (kernel) {
+ // connect the kernel, and register message handlers
this.kernel = kernel;
var msg_types = ['widget_create', 'widget_destroy', 'widget_update'];
for (var i = 0; i < msg_types.length; i++) {
@@ -44,6 +45,20 @@ var IPython = (function (IPython) {
this.widget_types[widget_type] = constructor;
};
+ WidgetManager.prototype.register_widget = function (widget) {
+ // Register a widget in the mapping
+ this.widgets[widget.widget_id] = widget;
+ widget.kernel = this.kernel;
+ return widget.widget_id;
+ };
+
+ WidgetManager.prototype.unregister_widget = function (widget_id) {
+ // Remove a widget from the mapping
+ delete this.widgets[widget_id];
+ };
+
+ // widget message handlers
+
WidgetManager.prototype.widget_create = function (msg) {
var content = msg.content;
var constructor = this.widget_types[content.widget_type];
@@ -52,7 +67,10 @@ var IPython = (function (IPython) {
console.log("Available widget types are: ", this.widget_types);
return;
}
- var widget = new constructor(this.kernel, content);
+ var widget = new constructor(content.widget_id);
+ this.register_widget(widget);
+ widget.handle_create(content.data);
+
this.widgets[content.widget_id] = widget;
};
@@ -79,39 +97,48 @@ var IPython = (function (IPython) {
// Widget base class
//-----------------------------------------------------------------------
- var Widget = function (kernel, content) {
- this.kernel = kernel;
- if (!content) return;
- this.widget_id = content.widget_id;
- this.handle_create(content.data);
- };
-
- Widget.prototype.handle_create = function (data) {
+ var Widget = function (widget_id) {
+ this.widget_id = widget_id;
+ this.widget_type = 'widget';
};
- Widget.prototype.handle_update = function (data) {
- };
-
- Widget.prototype.handle_destroy = function (data) {
+ // methods for sending messages
+ Widget.prototype.create = function (data) {
+ var content = {
+ widget_id : this.widget_id,
+ widget_type : this.widget_type,
+ data : data || {},
+ };
+ this.kernel.send_shell_message("widget_create", content);
};
Widget.prototype.update = function (data) {
var content = {
widget_id : this.widget_id,
- data : data,
+ data : data || {},
};
this.kernel.send_shell_message("widget_update", content);
};
-
Widget.prototype.destroy = function (data) {
var content = {
widget_id : this.widget_id,
- data : data,
+ data : data || {},
};
this.kernel.send_shell_message("widget_destroy", content);
};
+ // methods for handling incoming messages
+
+ Widget.prototype.handle_create = function (data) {
+ };
+
+ Widget.prototype.handle_update = function (data) {
+ };
+
+ Widget.prototype.handle_destroy = function (data) {
+ };
+
IPython.WidgetManager = WidgetManager;
IPython.Widget = Widget;
diff --git a/IPython/kernel/widgets/manager.py b/IPython/kernel/widgets/manager.py
index 0a2ca46..c0fc995 100644
--- a/IPython/kernel/widgets/manager.py
+++ b/IPython/kernel/widgets/manager.py
@@ -11,11 +11,11 @@
# Imports
#-----------------------------------------------------------------------------
-from weakref import ref
-
from IPython.config import LoggingConfigurable
from IPython.core.prompts import LazyEvaluate
from IPython.core.getipython import get_ipython
+
+from IPython.utils.importstring import import_item
from IPython.utils.traitlets import Instance, Unicode, Dict, Any
#-----------------------------------------------------------------------------
@@ -30,6 +30,7 @@ def lazy_keys(dikt):
"""
return LazyEvaluate(lambda d: list(d.keys()))
+
class WidgetManager(LoggingConfigurable):
"""Manager for Widgets in the Kernel"""
@@ -46,25 +47,32 @@ class WidgetManager(LoggingConfigurable):
return self.shell.parent.session
widgets = Dict()
+ widget_types = Dict()
# Public APIs
+ def register_widget_type(self, widget_type, constructor):
+ """Register a constructor for a given widget_type
+
+ constructor can be a Widget class or an importstring for a Widget class.
+ """
+ if isinstance(constructor, basestring):
+ constructor = import_item(constructor)
+
+ self.widget_types[widget_type] = constructor
+
def register_widget(self, widget):
"""Register a new widget"""
- self.widgets[widget.widget_id] = ref(widget)
+ widget_id = widget.widget_id
widget.shell = self.shell
widget.iopub_socket = self.iopub_socket
- widget.create()
- return widget.widget_id
+ self.widgets[widget_id] = widget
+ return widget_id
def unregister_widget(self, widget_id):
"""Unregister a widget, and destroy its counterpart"""
# unlike get_widget, this should raise a KeyError
- widget_ref = self.widgets.pop(widget_id)
- widget = widget_ref()
- if widget is None:
- # already destroyed, nothing to do
- return
+ widget = self.widgets.pop(widget_id)
widget.destroy()
def get_widget(self, widget_id):
@@ -80,16 +88,27 @@ class WidgetManager(LoggingConfigurable):
self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
return
# call, because we store weakrefs
- widget = self.widgets[widget_id]()
- if widget is None:
- self.log.error("Widget %s has been removed", widget_id)
- del self.widgets[widget_id]
- self.log.debug("Current widgets: %s", lazy_keys(self.widgets))
- return
+ widget = self.widgets[widget_id]
return widget
# Message handlers
+ def widget_create(self, stream, ident, msg):
+ """Handler for widget_update messages"""
+ content = msg['content']
+ widget_id = content['widget_id']
+ widget_type = content['widget_type']
+ constructor = self.widget_types.get(widget_type, None)
+ if constructor is None:
+ self.log.error("No such widget_type registered: %s", widget_type)
+ return
+ widget = constructor(widget_id=widget_id,
+ shell=self.shell,
+ iopub_socket=self.iopub_socket,
+ _create_data=content['data'],
+ )
+ self.register_widget(widget)
+
def widget_update(self, stream, ident, msg):
"""Handler for widget_update messages"""
content = msg['content']
@@ -110,6 +129,6 @@ class WidgetManager(LoggingConfigurable):
return
widget.handle_destroy(content['data'])
del self.widgets[widget_id]
-
+
__all__ = ['WidgetManager']
diff --git a/IPython/kernel/widgets/widget.py b/IPython/kernel/widgets/widget.py
index f258b69..8532419 100644
--- a/IPython/kernel/widgets/widget.py
+++ b/IPython/kernel/widgets/widget.py
@@ -13,6 +13,7 @@
import uuid
+from IPython.core.getipython import get_ipython
from IPython.config import LoggingConfigurable
from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
@@ -47,6 +48,18 @@ class Widget(LoggingConfigurable):
def _widget_id_default(self):
return uuid.uuid4().hex
+ primary = Bool(False, help="Am I the primary or secondary Widget?")
+
+ def __init__(self, **kwargs):
+ super(Widget, self).__init__(**kwargs)
+ get_ipython().widget_manager.register_widget(self)
+ if self.primary:
+ # I am primary, create my peer
+ self.create()
+ else:
+ # I am secondary, handle creation
+ self.handle_create(self._create_data)
+
def _publish_msg(self, msg_type, data=None, **keys):
"""Helper for sending a widget message on IOPub"""
data = {} if data is None else data
@@ -61,16 +74,20 @@ class Widget(LoggingConfigurable):
# publishing messages
- def create(self):
+ def create(self, data=None):
"""Create the frontend-side version of this widget"""
- self._publish_msg('widget_create', self._create_data, widget_type = self.widget_type)
+ if data is None:
+ data = self._create_data
+ self._publish_msg('widget_create', data, widget_type=self.widget_type)
- def destroy(self):
+ def destroy(self, data=None):
"""Destroy the frontend-side version of this widget"""
if self._destroyed:
# only destroy once
return
- self._publish_msg('widget_destroy', self._destroy_data)
+ if data is None:
+ data = self._destroy_data
+ self._publish_msg('widget_destroy', data)
self._destroyed = True
def update(self, data=None):
@@ -79,6 +96,10 @@ class Widget(LoggingConfigurable):
# handling of incoming messages
+ def handle_create(self, data):
+ """Handle a widget_create message"""
+ self.log.debug("handle_create %s", data)
+
def handle_destroy(self, data):
"""Handle a widget_destroy message"""
self.log.debug("handle_destroy %s", data)
diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py
index 97d121f..3f53dfe 100755
--- a/IPython/kernel/zmq/ipkernel.py
+++ b/IPython/kernel/zmq/ipkernel.py
@@ -168,7 +168,7 @@ class Kernel(Configurable):
for msg_type in msg_types:
self.shell_handlers[msg_type] = getattr(self, msg_type)
- widget_msg_types = [ 'widget_update', 'widget_destroy' ]
+ widget_msg_types = [ 'widget_create', 'widget_update', 'widget_destroy' ]
widget_manager = self.shell.widget_manager
for msg_type in widget_msg_types:
self.shell_handlers[msg_type] = getattr(widget_manager, msg_type)