From e7ee9c12b34007cde1545783fcd8ca32fde7d434 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Wed, 8 Oct 2014 20:08:34 -0700 Subject: [PATCH 01/10] Enable widget instanciation from front-end. --- IPython/html/static/widgets/js/manager.js | 37 ++++++++++++++++++++++- IPython/html/widgets/widget.py | 37 ++++++++++++++++------- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 944effd5a..6a0200fb4 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -187,12 +187,47 @@ define([ WidgetManager.prototype._handle_comm_open = function (comm, msg) { // Handle when a comm is opened. + return this.create_model({model_name: msg.content.data.target_name, comm: comm}); + }; + + WidgetManager.prototype.create_model = function (model_name, target_name) { + // Create and return a new widget model. + // + // Parameters + // ---------- + // model_name: string + // Target name of the widget model to create. + // target_name: string + // Target name of the widget in the back-end. + return this._create_model({model_name: model_name, target_name: target_name}); + }; + + WidgetManager.prototype._create_model = function (options) { + // Create and return a new widget model. + // + // Parameters + // ---------- + // options: dictionary + // Dictionary of options with the following contents: + // model_name: string + // Target name of the widget model to create. + // target_name: (optional) string + // Target name of the widget in the back-end. + // comm: (optional) Comm + + // Create a comm if it wasn't provided. + var comm = options.comm; + if (!comm) { + comm = this.comm_manager.new_comm('ipython.widget', {'target_name': options.target_name}); + } + + // Create and return a new model that is connected to the comm. var that = this; var instantiate_model = function(ModelType) { var model_id = comm.comm_id; var widget_model = new ModelType(that, model_id, comm); - widget_model.on('comm:close', function () { + widget_model.on('comm:close', function () {sss delete that._models[model_id]; }); that._models[model_id] = widget_model; diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index f1571eab4..65c2a3307 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -18,6 +18,7 @@ import collections from IPython.core.getipython import get_ipython from IPython.kernel.comm import Comm from IPython.config import LoggingConfigurable +from IPython.utils.importstring import import_item from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, \ CaselessStrEnum, Tuple, CUnicode, Int, Set from IPython.utils.py3compat import string_types @@ -95,6 +96,15 @@ class Widget(LoggingConfigurable): if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback): Widget._widget_construction_callback(widget) + @staticmethod + def handle_comm_opened(comm, msg): + """Static method, called when a widget is constructed.""" + target_name = msg['content']['data']['target_name'] + widget_class = import_item(target_name) + widget = widget_class(open_comm=False) + widget.set_comm(comm) + + #------------------------------------------------------------------------- # Traits #------------------------------------------------------------------------- @@ -125,13 +135,14 @@ class Widget(LoggingConfigurable): #------------------------------------------------------------------------- # (Con/de)structor #------------------------------------------------------------------------- - def __init__(self, **kwargs): + def __init__(self, open_comm=True, **kwargs): """Public constructor""" self._model_id = kwargs.pop('model_id', None) super(Widget, self).__init__(**kwargs) Widget._call_widget_constructed(self) - self.open() + if open_comm: + self.open() def __del__(self): """Object disposal""" @@ -149,15 +160,19 @@ class Widget(LoggingConfigurable): 'model_module': self._model_module}) if self._model_id is not None: args['comm_id'] = self._model_id - self.comm = Comm(**args) - self._model_id = self.model_id + self.set_comm(Comm(**args)) - self.comm.on_msg(self._handle_msg) - Widget.widgets[self.model_id] = self - # first update self.send_state() + def set_comm(self, comm): + """Set's the comm of the widget.""" + self.comm = comm + self._model_id = self.model_id + + self.comm.on_msg(self._handle_msg) + Widget.widgets[self.model_id] = self + @property def model_id(self): """Gets the model id of this widget. @@ -330,7 +345,7 @@ class Widget(LoggingConfigurable): def _handle_custom_msg(self, content): """Called when a custom msg is received.""" self._msg_callbacks(self, content) - + def _notify_trait(self, name, old_value, new_value): """Called when a property has been changed.""" # Trigger default traitlet callback machinery. This allows any user @@ -341,10 +356,10 @@ class Widget(LoggingConfigurable): # Send the state after the user registered callbacks for trait changes # have all fired (allows for user to validate values). if self.comm is not None and name in self.keys: - # Make sure this isn't information that the front-end just sent us. + # Make sure this isn't information that the front-end just sent us. if self._should_send_property(name, new_value): - # Send new state to front-end - self.send_state(key=name) + # Send new state to front-end + self.send_state(key=name) def _handle_displayed(self, **kwargs): """Called when a view has been displayed for this widget instance""" From 2b5ebb9e8f2a04b8e44b0c3d5caeb02801fcf112 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Wed, 8 Oct 2014 20:59:56 -0700 Subject: [PATCH 02/10] Make Python push initial state. Also added initial state push callback. --- IPython/html/static/widgets/js/manager.js | 17 +++++++++++++---- IPython/html/static/widgets/js/widget.js | 10 +++++++++- IPython/html/widgets/widget.py | 15 ++++++++------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 6a0200fb4..2c2de8be8 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -190,7 +190,7 @@ define([ return this.create_model({model_name: msg.content.data.target_name, comm: comm}); }; - WidgetManager.prototype.create_model = function (model_name, target_name) { + WidgetManager.prototype.create_model = function (model_name, target_name, init_state_callback) { // Create and return a new widget model. // // Parameters @@ -199,7 +199,13 @@ define([ // Target name of the widget model to create. // target_name: string // Target name of the widget in the back-end. - return this._create_model({model_name: model_name, target_name: target_name}); + // init_state_callback: (optional) callback + // Called when the first state push from the back-end is + // recieved. + return this._create_model({ + model_name: model_name, + target_name: target_name, + init_state_callback: init_state_callback}); }; WidgetManager.prototype._create_model = function (options) { @@ -214,6 +220,9 @@ define([ // target_name: (optional) string // Target name of the widget in the back-end. // comm: (optional) Comm + // init_state_callback: (optional) callback + // Called when the first state push from the back-end is + // recieved. // Create a comm if it wasn't provided. var comm = options.comm; @@ -226,8 +235,8 @@ define([ var instantiate_model = function(ModelType) { var model_id = comm.comm_id; - var widget_model = new ModelType(that, model_id, comm); - widget_model.on('comm:close', function () {sss + var widget_model = new ModelType(that, model_id, comm, options.init_state_callback); + widget_model.on('comm:close', function () { delete that._models[model_id]; }); that._models[model_id] = widget_model; diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index d60b38489..fb3a4049e 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -9,7 +9,7 @@ define(["widgets/js/manager", ], function(widgetmanager, _, Backbone, $, IPython){ var WidgetModel = Backbone.Model.extend({ - constructor: function (widget_manager, model_id, comm) { + constructor: function (widget_manager, model_id, comm, init_state_callback) { // Constructor // // Creates a WidgetModel instance. @@ -20,7 +20,11 @@ define(["widgets/js/manager", // model_id : string // An ID unique to this model. // comm : Comm instance (optional) + // init_state_callback : callback (optional) + // Called once when the first state message is recieved from + // the back-end. this.widget_manager = widget_manager; + this.init_state_callback = init_state_callback; this._buffered_state_diff = {}; this.pending_msgs = 0; this.msg_buffer = null; @@ -70,6 +74,10 @@ define(["widgets/js/manager", switch (method) { case 'update': this.set_state(msg.content.data.state); + if (this.init_state_callback) { + this.init_state_callback.apply(this, [this]); + this.init_state_callback = null; + } break; case 'custom': this.trigger('msg:custom', msg.content.data.content); diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index 65c2a3307..d840d5bac 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -142,7 +142,7 @@ class Widget(LoggingConfigurable): Widget._call_widget_constructed(self) if open_comm: - self.open() + self.open() def __del__(self): """Object disposal""" @@ -161,17 +161,18 @@ class Widget(LoggingConfigurable): if self._model_id is not None: args['comm_id'] = self._model_id self.set_comm(Comm(**args)) - - # first update - self.send_state() def set_comm(self, comm): """Set's the comm of the widget.""" self.comm = comm - self._model_id = self.model_id + self._model_id = self.model_id + + self.comm.on_msg(self._handle_msg) + Widget.widgets[self.model_id] = self - self.comm.on_msg(self._handle_msg) - Widget.widgets[self.model_id] = self + # first update + self.send_state() + @property def model_id(self): From 5a655b24da18ed3803c5592befe669ffec7cab57 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Thu, 9 Oct 2014 08:11:48 -0700 Subject: [PATCH 03/10] Fixed typo. --- IPython/html/static/widgets/js/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 2c2de8be8..08058baf8 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -187,7 +187,7 @@ define([ WidgetManager.prototype._handle_comm_open = function (comm, msg) { // Handle when a comm is opened. - return this.create_model({model_name: msg.content.data.target_name, comm: comm}); + return this._create_model({model_name: msg.content.data.target_name, comm: comm}); }; WidgetManager.prototype.create_model = function (model_name, target_name, init_state_callback) { From fbca0bfa3f78987bbc54d42f7db25a557899d542 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Thu, 9 Oct 2014 12:39:10 -0700 Subject: [PATCH 04/10] Rebase fixes. --- IPython/html/static/widgets/js/manager.js | 2 +- IPython/html/widgets/widget.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 08058baf8..41a9cfadc 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -187,7 +187,7 @@ define([ WidgetManager.prototype._handle_comm_open = function (comm, msg) { // Handle when a comm is opened. - return this._create_model({model_name: msg.content.data.target_name, comm: comm}); + return this._create_model({model_name: msg.content.data.model_name, comm: comm}); }; WidgetManager.prototype.create_model = function (model_name, target_name, init_state_callback) { diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index d840d5bac..e4c189dea 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -142,7 +142,7 @@ class Widget(LoggingConfigurable): Widget._call_widget_constructed(self) if open_comm: - self.open() + self.open() def __del__(self): """Object disposal""" @@ -165,11 +165,11 @@ class Widget(LoggingConfigurable): def set_comm(self, comm): """Set's the comm of the widget.""" self.comm = comm - self._model_id = self.model_id - - self.comm.on_msg(self._handle_msg) - Widget.widgets[self.model_id] = self - + self._model_id = self.model_id + + self.comm.on_msg(self._handle_msg) + Widget.widgets[self.model_id] = self + # first update self.send_state() @@ -359,8 +359,8 @@ class Widget(LoggingConfigurable): if self.comm is not None and name in self.keys: # Make sure this isn't information that the front-end just sent us. if self._should_send_property(name, new_value): - # Send new state to front-end - self.send_state(key=name) + # Send new state to front-end + self.send_state(key=name) def _handle_displayed(self, **kwargs): """Called when a view has been displayed for this widget instance""" From a68df8a8b1891821cdf2daf4d0f67c6ca50a0a48 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Tue, 21 Oct 2014 13:32:15 -0700 Subject: [PATCH 05/10] Address @minrk 's review comments. --- IPython/html/static/widgets/js/widget.js | 2 +- IPython/html/widgets/widget.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index fb3a4049e..0a4caa7e7 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -76,7 +76,7 @@ define(["widgets/js/manager", this.set_state(msg.content.data.state); if (this.init_state_callback) { this.init_state_callback.apply(this, [this]); - this.init_state_callback = null; + delete this.init_state_callback; } break; case 'custom': diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index e4c189dea..f16b94df5 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -102,7 +102,7 @@ class Widget(LoggingConfigurable): target_name = msg['content']['data']['target_name'] widget_class = import_item(target_name) widget = widget_class(open_comm=False) - widget.set_comm(comm) + widget.comm = comm #------------------------------------------------------------------------- @@ -160,11 +160,11 @@ class Widget(LoggingConfigurable): 'model_module': self._model_module}) if self._model_id is not None: args['comm_id'] = self._model_id - self.set_comm(Comm(**args)) + self.comm = Comm(**args) - def set_comm(self, comm): - """Set's the comm of the widget.""" - self.comm = comm + def _comm_changed(self, name, new): + """Called when the comm is changed.""" + self.comm = new self._model_id = self.model_id self.comm.on_msg(self._handle_msg) @@ -173,7 +173,6 @@ class Widget(LoggingConfigurable): # first update self.send_state() - @property def model_id(self): """Gets the model id of this widget. From 3cb5ac6fbb26bb743259a269e0be7a248ace00fe Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Wed, 22 Oct 2014 12:09:51 -0700 Subject: [PATCH 06/10] Clarified API for the create_model function, Also added a test! --- IPython/html/static/widgets/js/manager.js | 34 +++++++++------------ IPython/html/tests/widgets/manager.js | 36 +++++++++++++++++++++++ IPython/html/tests/widgets/widget.js | 12 -------- 3 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 IPython/html/tests/widgets/manager.js diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 41a9cfadc..51b07e269 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -187,29 +187,22 @@ define([ WidgetManager.prototype._handle_comm_open = function (comm, msg) { // Handle when a comm is opened. - return this._create_model({model_name: msg.content.data.model_name, comm: comm}); + return this.create_model({model_name: msg.content.data.model_name, comm: comm}); }; - WidgetManager.prototype.create_model = function (model_name, target_name, init_state_callback) { + WidgetManager.prototype.create_model = function (options) { // Create and return a new widget model. // - // Parameters - // ---------- - // model_name: string - // Target name of the widget model to create. - // target_name: string - // Target name of the widget in the back-end. - // init_state_callback: (optional) callback - // Called when the first state push from the back-end is - // recieved. - return this._create_model({ - model_name: model_name, - target_name: target_name, - init_state_callback: init_state_callback}); - }; - - WidgetManager.prototype._create_model = function (options) { - // Create and return a new widget model. + // Minimally, one must provide the model_name and target_name + // parameters to create a model from Javascript. + // + // Example + // -------- + // JS: + // window.slider = IPython.notebook.kernel.widget_manager.create_model({ + // model_name: 'WidgetModel', + // target_name: 'IPython.html.widgets.widget_int.IntSlider', + // init_state_callback: function(model) { console.log('Create success!', model); }}); // // Parameters // ---------- @@ -222,7 +215,8 @@ define([ // comm: (optional) Comm // init_state_callback: (optional) callback // Called when the first state push from the back-end is - // recieved. + // recieved. Allows you to modify the model after it's + // complete state is filled and synced. // Create a comm if it wasn't provided. var comm = options.comm; diff --git a/IPython/html/tests/widgets/manager.js b/IPython/html/tests/widgets/manager.js new file mode 100644 index 000000000..4281946c2 --- /dev/null +++ b/IPython/html/tests/widgets/manager.js @@ -0,0 +1,36 @@ +// Test the widget manager. +casper.notebook_test(function () { + var index; + var slider = {}; + + this.then(function () { + + // Check if the WidgetManager class is defined. + this.test.assert(this.evaluate(function() { + return IPython.WidgetManager !== undefined; + }), 'WidgetManager class is defined'); + + // Check if the widget manager has been instantiated. + this.test.assert(this.evaluate(function() { + return IPython.notebook.kernel.widget_manager !== undefined; + }), 'Notebook widget manager instantiated'); + + // Try creating a widget from Javascript. + slider.id = this.evaluate(function() { + var slider = IPython.notebook.kernel.widget_manager.create_model({ + model_name: 'WidgetModel', + target_name: 'IPython.html.widgets.widget_int.IntSlider', + init_state_callback: function(model) { console.log('Create success!', model); }}); + return slider.id; + }); + }); + + index = this.append_cell( + 'from IPython.html.widgets import Widget\n' + + 'widget = Widget.widgets.values()[0]\n' + + 'print(widget.model_id)'); + this.execute_cell_then(index, function(index) { + var output = this.get_output_cell(index).text.trim(); + this.test.assertEquals(output, slider.id, "Widget created from the front-end."); + }); +}); diff --git a/IPython/html/tests/widgets/widget.js b/IPython/html/tests/widgets/widget.js index d51a833a0..0782daed8 100644 --- a/IPython/html/tests/widgets/widget.js +++ b/IPython/html/tests/widgets/widget.js @@ -38,14 +38,6 @@ var recursive_compare = function(a, b) { // Test the widget framework. casper.notebook_test(function () { var index; - - this.then(function () { - - // Check if the WidgetManager class is defined. - this.test.assert(this.evaluate(function() { - return IPython.WidgetManager !== undefined; - }), 'WidgetManager class is defined'); - }); index = this.append_cell( 'from IPython.html import widgets\n' + @@ -54,10 +46,6 @@ casper.notebook_test(function () { this.execute_cell_then(index); this.then(function () { - // Check if the widget manager has been instantiated. - this.test.assert(this.evaluate(function() { - return IPython.notebook.kernel.widget_manager !== undefined; - }), 'Notebook widget manager instantiated'); // Functions that can be used to test the packing and unpacking APIs var that = this; From cb2c76999c77c9b35970c1f07e7e3e076f5641db Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Mon, 27 Oct 2014 10:56:37 -0700 Subject: [PATCH 07/10] Address some more review comments... --- IPython/html/tests/widgets/manager.js | 2 +- IPython/html/widgets/widget.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/IPython/html/tests/widgets/manager.js b/IPython/html/tests/widgets/manager.js index 4281946c2..ea98dfff5 100644 --- a/IPython/html/tests/widgets/manager.js +++ b/IPython/html/tests/widgets/manager.js @@ -27,7 +27,7 @@ casper.notebook_test(function () { index = this.append_cell( 'from IPython.html.widgets import Widget\n' + - 'widget = Widget.widgets.values()[0]\n' + + 'widget = list(Widget.widgets.values())[0]\n' + 'print(widget.model_id)'); this.execute_cell_then(index, function(index) { var output = this.get_output_cell(index).text.trim(); diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index f16b94df5..1551b18b6 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -101,8 +101,7 @@ class Widget(LoggingConfigurable): """Static method, called when a widget is constructed.""" target_name = msg['content']['data']['target_name'] widget_class = import_item(target_name) - widget = widget_class(open_comm=False) - widget.comm = comm + widget = widget_class(comm=comm) #------------------------------------------------------------------------- @@ -141,8 +140,7 @@ class Widget(LoggingConfigurable): super(Widget, self).__init__(**kwargs) Widget._call_widget_constructed(self) - if open_comm: - self.open() + self.open() def __del__(self): """Object disposal""" From db10ae8f2d7fe87b588d9854455de5fd0fb8264e Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Mon, 27 Oct 2014 17:57:02 -0700 Subject: [PATCH 08/10] s/target_name/widget_class --- IPython/html/static/widgets/js/manager.js | 8 ++++---- IPython/html/widgets/widget.py | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 51b07e269..093db65a6 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -193,7 +193,7 @@ define([ WidgetManager.prototype.create_model = function (options) { // Create and return a new widget model. // - // Minimally, one must provide the model_name and target_name + // Minimally, one must provide the model_name and widget_class // parameters to create a model from Javascript. // // Example @@ -201,7 +201,7 @@ define([ // JS: // window.slider = IPython.notebook.kernel.widget_manager.create_model({ // model_name: 'WidgetModel', - // target_name: 'IPython.html.widgets.widget_int.IntSlider', + // widget_class: 'IPython.html.widgets.widget_int.IntSlider', // init_state_callback: function(model) { console.log('Create success!', model); }}); // // Parameters @@ -210,7 +210,7 @@ define([ // Dictionary of options with the following contents: // model_name: string // Target name of the widget model to create. - // target_name: (optional) string + // widget_class: (optional) string // Target name of the widget in the back-end. // comm: (optional) Comm // init_state_callback: (optional) callback @@ -221,7 +221,7 @@ define([ // Create a comm if it wasn't provided. var comm = options.comm; if (!comm) { - comm = this.comm_manager.new_comm('ipython.widget', {'target_name': options.target_name}); + comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class}); } // Create and return a new model that is connected to the comm. diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index 1551b18b6..bd88294d3 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -99,8 +99,7 @@ class Widget(LoggingConfigurable): @staticmethod def handle_comm_opened(comm, msg): """Static method, called when a widget is constructed.""" - target_name = msg['content']['data']['target_name'] - widget_class = import_item(target_name) + widget_class = import_item(msg['content']['data']['widget_class']) widget = widget_class(comm=comm) From 04d0874a80e9720a9db78098641042c9c68cbc85 Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Tue, 28 Oct 2014 14:37:32 -0700 Subject: [PATCH 09/10] JS test fix --- IPython/html/tests/widgets/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPython/html/tests/widgets/manager.js b/IPython/html/tests/widgets/manager.js index ea98dfff5..94ddfc0e5 100644 --- a/IPython/html/tests/widgets/manager.js +++ b/IPython/html/tests/widgets/manager.js @@ -19,7 +19,7 @@ casper.notebook_test(function () { slider.id = this.evaluate(function() { var slider = IPython.notebook.kernel.widget_manager.create_model({ model_name: 'WidgetModel', - target_name: 'IPython.html.widgets.widget_int.IntSlider', + widget_class: 'IPython.html.widgets.widget_int.IntSlider', init_state_callback: function(model) { console.log('Create success!', model); }}); return slider.id; }); From ba77877c18d629e1a5ae2fa1120e46eb39e85dfa Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Tue, 28 Oct 2014 17:18:37 -0700 Subject: [PATCH 10/10] Rebase fixes --- IPython/html/static/widgets/js/manager.js | 48 ++++++++++++++++------- IPython/html/static/widgets/js/widget.js | 2 +- IPython/html/tests/widgets/manager.js | 26 ++++++++---- IPython/html/widgets/widget.py | 2 +- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 093db65a6..242c67975 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -53,7 +53,7 @@ define([ " message was from. Widget will not be displayed"); } else { var that = this; - this.create_view(model, {cell: cell, callback: function(view) { + this.create_view(model, {cell: cell, success: function(view) { that._handle_display_view(view); if (cell.widget_subarea) { cell.widget_subarea.append(view.$el); @@ -84,7 +84,7 @@ define([ var view_name = model.get('_view_name'); var view_mod = model.get('_view_module'); - var errback = options.errback || function(err) {console.log(err);}; + var error = options.error || function(error) { console.log(error); }; var instantiate_view = function(ViewType) { if (ViewType) { @@ -100,9 +100,11 @@ define([ var view = new ViewType(parameters); view.render(); model.on('destroy', view.remove, view); - options.callback(view); + if (options.success) { + options.success(view); + } } else { - errback({unknown_view: true, view_name: view_name, + error({unknown_view: true, view_name: view_name, view_module: view_mod}); } }; @@ -110,7 +112,7 @@ define([ if (view_mod) { require([view_mod], function(module) { instantiate_view(module[view_name]); - }, errback); + }, error); } else { instantiate_view(WidgetManager._view_types[view_name]); } @@ -157,7 +159,7 @@ define([ handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area); } - // Create callback dict using what is known + // Create callback dictionary using what is known var that = this; callbacks = { iopub : { @@ -187,7 +189,10 @@ define([ WidgetManager.prototype._handle_comm_open = function (comm, msg) { // Handle when a comm is opened. - return this.create_model({model_name: msg.content.data.model_name, comm: comm}); + this.create_model({ + model_name: msg.content.data.model_name, + model_module: msg.content.data.model_module, + comm: comm}); }; WidgetManager.prototype.create_model = function (options) { @@ -199,7 +204,7 @@ define([ // Example // -------- // JS: - // window.slider = IPython.notebook.kernel.widget_manager.create_model({ + // IPython.notebook.kernel.widget_manager.create_model({ // model_name: 'WidgetModel', // widget_class: 'IPython.html.widgets.widget_int.IntSlider', // init_state_callback: function(model) { console.log('Create success!', model); }}); @@ -210,23 +215,31 @@ define([ // Dictionary of options with the following contents: // model_name: string // Target name of the widget model to create. + // model_module: (optional) string + // Module name of the widget model to create. // widget_class: (optional) string // Target name of the widget in the back-end. // comm: (optional) Comm + // success: (optional) callback + // Callback for when the model was created successfully. + // error: (optional) callback + // Callback for when the model wasn't created. // init_state_callback: (optional) callback // Called when the first state push from the back-end is // recieved. Allows you to modify the model after it's // complete state is filled and synced. + // Make default callbacks if not specified. + var error = options.error || function(error) { console.log(error); }; + // Create a comm if it wasn't provided. var comm = options.comm; if (!comm) { comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class}); } - // Create and return a new model that is connected to the comm. + // Create a new model that is connected to the comm. var that = this; - var instantiate_model = function(ModelType) { var model_id = comm.comm_id; var widget_model = new ModelType(that, model_id, comm, options.init_state_callback); @@ -234,22 +247,27 @@ define([ delete that._models[model_id]; }); that._models[model_id] = widget_model; + if (options.success) { + options.success(widget_model); + } }; - var widget_type_name = msg.content.data.model_name; - var widget_module = msg.content.data.model_module; - + // Get the model type using require or through the registry. + var widget_type_name = options.model_name; + var widget_module = options.model_module; if (widget_module) { + // Load the module containing the widget model require([widget_module], function(mod) { if (mod[widget_type_name]) { instantiate_model(mod[widget_type_name]); } else { - console.log("Error creating widget model: " + widget_type_name + error("Error creating widget model: " + widget_type_name + " not found in " + widget_module); } - }, function(err) { console.log(err); }); + }, error); } else { + // No module specified, load from the global models registry instantiate_model(WidgetManager._model_types[widget_type_name]); } diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index 0a4caa7e7..34822bd75 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -327,7 +327,7 @@ define(["widgets/js/manager", // to the subview without having to add it here. var that = this; var old_callback = options.callback || function(view) {}; - options = $.extend({ parent: this, callback: function(child_view) { + options = $.extend({ parent: this, success: function(child_view) { // Associate the view id with the model id. if (that.child_model_views[child_model.id] === undefined) { that.child_model_views[child_model.id] = []; diff --git a/IPython/html/tests/widgets/manager.js b/IPython/html/tests/widgets/manager.js index 94ddfc0e5..fed79fd85 100644 --- a/IPython/html/tests/widgets/manager.js +++ b/IPython/html/tests/widgets/manager.js @@ -1,7 +1,6 @@ // Test the widget manager. casper.notebook_test(function () { var index; - var slider = {}; this.then(function () { @@ -16,12 +15,22 @@ casper.notebook_test(function () { }), 'Notebook widget manager instantiated'); // Try creating a widget from Javascript. - slider.id = this.evaluate(function() { - var slider = IPython.notebook.kernel.widget_manager.create_model({ - model_name: 'WidgetModel', - widget_class: 'IPython.html.widgets.widget_int.IntSlider', - init_state_callback: function(model) { console.log('Create success!', model); }}); - return slider.id; + this.evaluate(function() { + IPython.notebook.kernel.widget_manager.create_model({ + model_name: 'WidgetModel', + widget_class: 'IPython.html.widgets.widget_int.IntSlider', + init_state_callback: function(model) { + console.log('Create success!', model); + window.slider_id = model.id; + } + }); + }); + }); + + // Wait for the state to be recieved. + this.waitFor(function check() { + return this.evaluate(function() { + return window.slider_id !== undefined; }); }); @@ -31,6 +40,7 @@ casper.notebook_test(function () { 'print(widget.model_id)'); this.execute_cell_then(index, function(index) { var output = this.get_output_cell(index).text.trim(); - this.test.assertEquals(output, slider.id, "Widget created from the front-end."); + var slider_id = this.evaluate(function() { return window.slider_id; }); + this.test.assertEquals(output, slider_id, "Widget created from the front-end."); }); }); diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index bd88294d3..0f0c61b86 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -133,7 +133,7 @@ class Widget(LoggingConfigurable): #------------------------------------------------------------------------- # (Con/de)structor #------------------------------------------------------------------------- - def __init__(self, open_comm=True, **kwargs): + def __init__(self, **kwargs): """Public constructor""" self._model_id = kwargs.pop('model_id', None) super(Widget, self).__init__(**kwargs)