Intermediate changes to javascript side of backbone widgets

This commit is contained in:
Jason Grout 2013-12-31 09:26:07 -07:00 committed by Jonathan Frederic
parent 518cb4c647
commit ad1e23bfc2
11 changed files with 560 additions and 375 deletions

View File

@ -46,9 +46,9 @@
WidgetManager.prototype.attach_comm_manager = function (comm_manager) {
this.comm_manager = comm_manager;
// Register already register widget model types with the comm manager.
// Register already-registered widget model types with the comm manager.
for (var widget_model_name in this.widget_model_types) {
this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_comm_open, this));
}
};
@ -57,7 +57,7 @@
// Register the widget with the comm manager. Make sure to pass this object's context
// in so `this` works in the call back.
if (this.comm_manager !== null) {
this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_comm_open, this));
}
this.widget_model_types[widget_model_name] = widget_model_type;
};
@ -66,12 +66,97 @@
WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
this.widget_view_types[widget_view_name] = widget_view_type;
};
WidgetManager.prototype.handle_msg = function(msg, model) {
var method = msg.content.data.method;
switch (method) {
case 'display':
var cell = this.get_msg_cell(msg.parent_header.msg_id);
if (cell === null) {
console.log("Could not determine where the display" +
" message was from. Widget will not be displayed");
} else {
var view = this.create_view(model,
msg.content.data.view_name, cell);
if (view !== undefined
&& cell.widget_subarea !== undefined
&& cell.widget_subarea !== null) {
cell.widget_area.show();
cell.widget_subarea.append(view.$el);
}
}
break;
case 'set_snapshot':
var cell = this.get_msg_cell(msg.parent_header.msg_id);
cell.metadata.snapshot = msg.content.data.snapshot;
break;
}
}
WidgetManager.prototype.create_view = function(model, view_name, cell) {
view_name = view_name || model.get('default_view_name');
var ViewType = this.widget_view_types[view_name];
if (ViewType !== undefined && ViewType !== null) {
var view = new ViewType({model: model, widget_manager: this, cell: cell});
view.render();
//this.views.push(view);
/*
// jng: Handle when the view element is remove from the page.
// observe the view destruction event and do this. We may need
// to override the view's remove method to trigger this event.
var that = this;
view.$el.on("remove", function () {
var index = that.views.indexOf(view);
if (index > -1) {
that.views.splice(index, 1);
}
view.remove(); // Clean-up view
// Close the comm if there are no views left.
if (that.views.length() === 0) {
//jng: trigger comm close event
}
if (that.comm !== undefined) {
that.comm.close();
delete that.comm.model; // Delete ref so GC will collect widget model.
delete that.comm;
}
delete that.widget_id; // Delete id from model so widget manager cleans up.
});
*/
return view;
}
},
WidgetManager.prototype.get_msg_cell = function (msg_id) {
var cell = null;
// First, check to see if the msg was triggered by cell execution.
if (IPython.notebook !== undefined && IPython.notebook !== null) {
return IPython.notebook.get_msg_cell(msg_id);
cell = IPython.notebook.get_msg_cell(msg_id);
}
if (cell !== null) {
return cell
}
// Second, check to see if a get_cell callback was defined
// for the message. get_cell callbacks are registered for
// widget messages, so this block is actually checking to see if the
// message was triggered by a widget.
var kernel = this.get_kernel();
if (kernel !== undefined && kernel !== null) {
var callbacks = kernel.get_callbacks_for_msg(msg_id);
if (callbacks !== undefined &&
callbacks.iopub !== undefined &&
callbacks.iopub.get_cell !== undefined) {
return callbacks.iopub.get_cell();
}
}
// Not triggered by a cell or widget (no get_cell callback
// exists).
return null;
};
@ -109,7 +194,7 @@
};
WidgetManager.prototype._handle_com_open = function (comm, msg) {
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
var widget_type_name = msg.content.target_name;
var widget_model = new this.widget_model_types[widget_type_name](this, comm.comm_id, comm);
this._model_instances[comm.comm_id] = widget_model;
@ -126,4 +211,4 @@
return IPython.widget_manager;
});
}());
}());

View File

@ -28,17 +28,15 @@ function(widget_manager, underscore, backbone){
this.pending_msgs = 0;
this.msg_throttle = 3;
this.msg_buffer = null;
this.views = [];
this.id = widget_id;
this._custom_msg_callbacks = [];
if (comm !== undefined) {
// Remember comm associated with the model.
this.comm = comm;
comm.model = this;
// Hook comm messages up to model.
var that = this;
comm.on_close($.proxy(this._handle_comm_closed, this));
comm.on_msg($.proxy(this._handle_comm_msg, this));
}
@ -47,68 +45,18 @@ function(widget_manager, underscore, backbone){
},
send: function (content, cell) {
if (this._has_comm()) {
// Used the last modified view as the sender of the message. This
// will insure that any python code triggered by the sent message
// can create and display widgets and output.
if (cell === undefined) {
if (this.last_modified_view !== undefined &&
this.last_modified_view.cell !== undefined) {
cell = this.last_modified_view.cell;
}
}
var callbacks = this._make_callbacks(cell);
send: function (content, callbacks) {
console.log('send',content, callbacks);
if (this.comm !== undefined) {
var data = {method: 'custom', custom_content: content};
this.comm.send(data, callbacks);
}
},
on_view_created: function (callback) {
this._view_created_callback = callback;
},
on_close: function (callback) {
this._close_callback = callback;
},
on_msg: function (callback, remove) {
if (remove) {
var found_index = -1;
for (var index in this._custom_msg_callbacks) {
if (callback === this._custom_msg_callbacks[index]) {
found_index = index;
break;
}
}
if (found_index >= 0) {
this._custom_msg_callbacks.splice(found_index, 1);
}
} else {
this._custom_msg_callbacks.push(callback);
}
},
_handle_custom_msg: function (content) {
for (var index in this._custom_msg_callbacks) {
try {
this._custom_msg_callbacks[index](content);
} catch (e) {
console.log("Exception in widget model msg callback", e, content);
}
}
},
// Handle when a widget is closed.
_handle_comm_closed: function (msg) {
this._execute_views_method('remove');
// jng: widget manager should observe the comm_close event and delete views when triggered
this.trigger('comm:close');
if (this._has_comm()) {
delete this.comm.model; // Delete ref so GC will collect widget model.
delete this.comm;
@ -117,43 +65,19 @@ function(widget_manager, underscore, backbone){
},
// Handle incomming comm msg.
// Handle incoming comm msg.
_handle_comm_msg: function (msg) {
var method = msg.content.data.method;
switch (method) {
case 'display':
// Try to get the cell.
var cell = this._get_msg_cell(msg.parent_header.msg_id);
if (cell === null) {
console.log("Could not determine where the display" +
" message was from. Widget will not be displayed");
} else {
this.create_views(msg.content.data.view_name,
msg.content.data.parent,
cell);
}
break;
case 'update':
this.apply_update(msg.content.data.state);
break;
case 'add_class':
case 'remove_class':
var selector = msg.content.data.selector;
if (selector === undefined) {
selector = '';
}
var class_list = msg.content.data.class_list;
this._execute_views_method(method, selector, class_list);
break;
case 'set_snapshot':
var cell = this._get_msg_cell(msg.parent_header.msg_id);
cell.metadata.snapshot = msg.content.data.snapshot;
break;
case 'custom':
this._handle_custom_msg(msg.content.data.custom_content);
this.trigger('msg:custom', msg.content.data.custom_content);
break;
default:
// pass on to widget manager
this.widget_manager.handle_msg(msg, this);
}
},
@ -164,18 +88,7 @@ function(widget_manager, underscore, backbone){
try {
for (var key in state) {
if (state.hasOwnProperty(key)) {
if (key == "_css") {
// Set the css value of the model as an attribute
// instead of a backbone trait because we are only
// interested in backend css -> frontend css. In
// other words, if the css dict changes in the
// frontend, we don't need to push the changes to
// the backend.
this.css = state[key];
} else {
this.set(key, state[key]);
}
this.set(key, state[key]);
}
}
this.save();
@ -185,17 +98,15 @@ function(widget_manager, underscore, backbone){
},
_handle_status: function (cell, msg) {
_handle_status: function (msg, callbacks) {
//execution_state : ('busy', 'idle', 'starting')
if (this._has_comm()) {
if (msg.content.execution_state=='idle') {
if (this.comm !== undefined) {
if (msg.content.execution_state ==='idle') {
// Send buffer if this message caused another message to be
// throttled.
if (this.msg_buffer !== null &&
this.msg_throttle == this.pending_msgs) {
var callbacks = this._make_callbacks(cell);
this.msg_throttle === this.pending_msgs) {
var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
this.comm.send(data, callbacks);
this.msg_buffer = null;
@ -217,7 +128,7 @@ function(widget_manager, underscore, backbone){
// Only send updated state if the state hasn't been changed
// during an update.
if (this._has_comm()) {
if (this.comm !== undefined) {
if (!this.updating) {
if (this.pending_msgs >= this.msg_throttle) {
// The throttle has been exceeded, buffer the current msg so
@ -247,14 +158,7 @@ function(widget_manager, underscore, backbone){
}
var data = {method: 'backbone', sync_method: method, sync_data: send_json};
var cell = null;
if (this.last_modified_view !== undefined && this.last_modified_view !== null) {
cell = this.last_modified_view.cell;
}
var callbacks = this._make_callbacks(cell);
this.comm.send(data, callbacks);
this.comm.send(data, this.cell_callbacks());
this.pending_msgs++;
}
}
@ -265,133 +169,22 @@ function(widget_manager, underscore, backbone){
return model_json;
},
_handle_view_created: function (view) {
if (this._view_created_callback) {
try {
this._view_created_callback(view);
} catch (e) {
console.log("Exception in widget model view displayed callback", e, view, this);
}
}
},
_execute_views_method: function (/* method_name, [argument0], [argument1], [...] */) {
var method_name = arguments[0];
var args = null;
if (arguments.length > 1) {
args = [].splice.call(arguments,1);
}
for (var view_index in this.views) {
var view = this.views[view_index];
var method = view[method_name];
if (args === null) {
method.apply(view);
} else {
method.apply(view, args);
}
}
},
// Create view that represents the model.
create_views: function (view_name, parent_id, cell) {
var new_views = [];
var view;
// Try creating and adding the view to it's parent.
var displayed = false;
if (parent_id !== undefined) {
var parent_model = this.widget_manager.get_model(parent_id);
if (parent_model !== null) {
var parent_views = parent_model.views;
for (var parent_view_index in parent_views) {
var parent_view = parent_views[parent_view_index];
if (parent_view.cell === cell) {
if (parent_view.display_child !== undefined) {
view = this._create_view(view_name, cell);
if (view !== null) {
new_views.push(view);
parent_view.display_child(view);
displayed = true;
this._handle_view_created(view);
}
}
}
}
}
}
// If no parent view is defined or exists. Add the view's
// element to cell's widget div.
if (!displayed) {
view = this._create_view(view_name, cell);
if (view !== null) {
new_views.push(view);
if (cell.widget_subarea !== undefined && cell.widget_subarea !== null) {
cell.widget_area.show();
cell.widget_subarea.append(view.$el);
this._handle_view_created(view);
}
}
}
// Force the new view(s) to update their selves
for (var view_index in new_views) {
view = new_views[view_index];
view.update();
}
},
// Create a view
_create_view: function (view_name, cell) {
var ViewType = this.widget_manager.widget_view_types[view_name];
if (ViewType !== undefined && ViewType !== null) {
var view = new ViewType({model: this});
view.render();
this.views.push(view);
view.cell = cell;
// Handle when the view element is remove from the page.
var that = this;
view.$el.on("remove", function () {
var index = that.views.indexOf(view);
if (index > -1) {
that.views.splice(index, 1);
}
view.remove(); // Clean-up view
// Close the comm if there are no views left.
if (that.views.length() === 0) {
if (that._close_callback) {
try {
that._close_callback(that);
} catch (e) {
console.log("Exception in widget model close callback", e, that);
}
}
if (that._has_comm()) {
that.comm.close();
delete that.comm.model; // Delete ref so GC will collect widget model.
delete that.comm;
}
delete that.widget_id; // Delete id from model so widget manager cleans up.
}
});
return view;
}
return null;
},
// Build a callback dict.
_make_callbacks: function (cell) {
cell_callbacks: function (cell) {
var callbacks = {};
console.log('cell_callbacks A', cell);
if (cell === undefined) {
// Used the last modified view as the sender of the message. This
// will insure that any python code triggered by the sent message
// can create and display widgets and output.
if (this.last_modified_view !== undefined &&
this.last_modified_view.cell !== undefined) {
cell = this.last_modified_view.cell;
} else {
cell = null;
}
}
console.log('cell_callbacks B', cell);
if (cell !== null) {
// Try to get output handlers
@ -402,7 +195,7 @@ function(widget_manager, underscore, backbone){
handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
}
// Create callback dict usign what is known
// Create callback dict using what is known
var that = this;
callbacks = {
iopub : {
@ -410,7 +203,7 @@ function(widget_manager, underscore, backbone){
clear_output : handle_clear_output,
status : function (msg) {
that._handle_status(cell, msg);
that._handle_status(msg, that.cell_callbacks(cell));
},
// Special function only registered by widget messages.
@ -422,57 +215,86 @@ function(widget_manager, underscore, backbone){
},
};
}
console.log('constructed callbacks for',cell);
return callbacks;
},
// Get the output area corresponding to the msg_id.
// cell is an instance of IPython.Cell
_get_msg_cell: function (msg_id) {
// First, check to see if the msg was triggered by cell execution.
var cell = this.widget_manager.get_msg_cell(msg_id);
if (cell !== null) {
return cell;
}
// Second, check to see if a get_cell callback was defined
// for the message. get_cell callbacks are registered for
// widget messages, so this block is actually checking to see if the
// message was triggered by a widget.
var kernel = this.widget_manager.get_kernel();
if (kernel !== undefined && kernel !== null) {
var callbacks = kernel.get_callbacks_for_msg(msg_id);
if (callbacks !== undefined &&
callbacks.iopub !== undefined &&
callbacks.iopub.get_cell !== undefined) {
return callbacks.iopub.get_cell();
}
}
// Not triggered by a cell or widget (no get_cell callback
// exists).
return null;
},
// Function that checks if a comm has been attached to this widget
// model. Returns True if a valid comm is attached.
_has_comm: function() {
return this.comm !== undefined && this.comm !== null;
},
});
//--------------------------------------------------------------------
// WidgetView class
//--------------------------------------------------------------------
var WidgetView = Backbone.View.extend({
initialize: function () {
var BaseWidgetView = Backbone.View.extend({
initialize: function(options) {
this.model.on('change',this.update,this);
this.widget_manager = options.widget_manager;
this.comm_manager = options.widget_manager.comm_manager;
this.cell = options.cell
this.render();
// jng: maybe the following shouldn't be automatic---maybe the render method should take
// care of knowing what needs to be added as a child?
var children_attr = this.model.get('_children_attr');
for (var i in children_attr) {
var child_attr = children_attr[i];
var child_model = this.comm_manager.comms[this.model.get(child_attr)].model;
var child_view_name = this.child_view_name(child_attr, child_model);
var child_view = this.widget_manager.create_view(child_model, child_view_name, this.cell);
this.add_child_view(child_attr, child_view);
}
var children_lists_attr = this.model.get('_children_lists_attr')
for (var i in children_lists_attr) {
var child_attr = children_lists_attr[i];
var child_list = this.model.get(child_attr);
for (var j in child_list) {
var child_model = this.comm_manager.comms[child_list[j]].model;
var child_view_name = this.child_view_name(child_attr, child_model);
var child_view = this.widget_manager.create_view(child_model, child_view_name, this.cell);
this.add_child_view(child_attr, child_view);
}
}
},
update: function(){
// update thyself to be consistent with this.model
},
child_view: function(attr, viewname) {
var child_model = this.comm_manager.comms[this.model.get(attr)].model;
var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
return child_view;
},
render: function(){
// render thyself
},
child_view_name: function(attr, model) {
// attr is the name of the attribute we are constructing a view for
// model is the model stored in that attribute
// return a valid view_name to construct a view of that type
// or null for the default view for the model
return null;
},
add_child_view: function(attr, view) {
//attr is the name of the attribute containing a reference to this child
//view is the child view that has been constructed
//typically this will just add the child view's view.el attribute to some dom element
},
send: function (content) {
this.model.send(content, this.model.cell_callbacks(this.cell));
},
touch: function () {
this.model.last_modified_view = this;
this.model.save(this.model.changedAttributes(), {patch: true});
},
});
var WidgetView = BaseWidgetView.extend({
initialize: function (options) {
this.visible = true;
this.model.on('sync',this.update,this);
BaseWidgetView.prototype.initialize.apply(this, arguments);
},
add_class: function (selector, class_list) {
@ -489,27 +311,12 @@ function(widget_manager, underscore, backbone){
}
},
send: function (content) {
this.model.send(content, this.cell);
},
touch: function () {
this.model.last_modified_view = this;
this.model.save(this.model.changedAttributes(), {patch: true});
},
update: function () {
if (this.model.get('visible') !== undefined) {
if (this.visible != this.model.get('visible')) {
this.visible = this.model.get('visible');
if (this.visible) {
this.$el.show();
} else {
this.$el.hide();
}
}
// jng: hook into change:visible trigger
var visible = this.model.get('visible');
if (visible !== undefined && this.visible !== visible) {
this.visible = visible;
this.$el.toggle(visible)
}
if (this.model.css !== undefined) {
@ -552,4 +359,4 @@ function(widget_manager, underscore, backbone){
IPython.WidgetView = WidgetView;
return widget_manager;
});
});

View File

@ -52,16 +52,21 @@ define(["notebook/js/widgets/base"], function(widget_manager) {
render: function(){
this.$el
.addClass('widget-container');
var children = this.model.get('children')
for(var i in this.model.get('children')) {
this.update()
},
update: function(){
set_flex_properties(this, this.$el);
return IPython.WidgetView.prototype.update.call(this);
},
display_child: function(view) {
this.$el.append(view.$el);
},
add_child_view: function(attr, view) {
if (attr==='children') {
this.$el.append(view.$el);
}
}
});
widget_manager.register_widget_view('ContainerView', ContainerView);
@ -229,8 +234,10 @@ define(["notebook/js/widgets/base"], function(widget_manager) {
return IPython.WidgetView.prototype.update.call(this);
},
display_child: function(view) {
this.$body.append(view.$el);
add_child_view: function(attr, view) {
if (attr==='children') {
this.$body.append(view.$el);
}
},
_get_selector_element: function(selector) {

View File

@ -27,7 +27,6 @@ define(["notebook/js/widgets/base"], function(widget_manager){
.addClass('accordion');
this.containers = [];
},
update: function() {
// Set tab titles
var titles = this.model.get('_titles');
@ -58,7 +57,7 @@ define(["notebook/js/widgets/base"], function(widget_manager){
return IPython.WidgetView.prototype.update.call(this);
},
display_child: function(view) {
add_child_view: function(attr, view) {
var index = this.containers.length;
var uuid = IPython.utils.uuid();
@ -103,7 +102,13 @@ define(["notebook/js/widgets/base"], function(widget_manager){
var TabView = IPython.WidgetView.extend({
initialize: function() {
this.containers = [];
IPython.WidgetView.prototype.initialize.apply(this, arguments);
},
render: function(){
console.log('rendering tabs', this);
var uuid = 'tabs'+IPython.utils.uuid();
var that = this;
this.$tabs = $('<div />', {id: uuid})
@ -113,8 +118,7 @@ define(["notebook/js/widgets/base"], function(widget_manager){
this.$tab_contents = $('<div />', {id: uuid + 'Content'})
.addClass('tab-content')
.appendTo(this.$el);
this.containers = [];
this.update();
},
update: function() {
@ -135,8 +139,8 @@ define(["notebook/js/widgets/base"], function(widget_manager){
return IPython.WidgetView.prototype.update.call(this);
},
display_child: function(view) {
add_child_view: function(attr, view) {
console.log('adding child view', attr, view);
var index = this.containers.length;
var uuid = IPython.utils.uuid();

View File

@ -86,7 +86,7 @@ var IPython = (function (IPython) {
try {
f(comm, msg);
} catch (e) {
console.log("Exception opening new comm:", e, msg);
console.log("Exception opening new comm:", e, e.stack, msg);
comm.close();
this.unregister_comm(comm);
}
@ -102,7 +102,7 @@ var IPython = (function (IPython) {
try {
comm.handle_close(msg);
} catch (e) {
console.log("Exception closing comm: ", e, msg);
console.log("Exception closing comm: ", e, e.stack, msg);
}
};
@ -115,7 +115,7 @@ var IPython = (function (IPython) {
try {
comm.handle_msg(msg);
} catch (e) {
console.log("Exception handling comm msg: ", e, msg);
console.log("Exception handling comm msg: ", e, e.stack, msg);
}
};
@ -176,7 +176,7 @@ var IPython = (function (IPython) {
try {
callback(msg);
} catch (e) {
console.log("Exception in Comm callback", e, msg);
console.log("Exception in Comm callback", e, e.stack, msg);
}
}
};

View File

@ -34,9 +34,13 @@ from IPython.utils.py3compat import string_types
class BaseWidget(LoggingConfigurable):
# Shared declarations (Class level)
_keys = List(Unicode, help="List of keys comprising the state of the model.")
_children_attr = List(Unicode, help="List of keys of children objects of the model.")
_children_lists_attr = List(Unicode, help="List of keys containing lists of children objects of the model.")
_keys = List(Unicode, default_value = [],
help="List of keys comprising the state of the model.", allow_none=False)
_children_attr = List(Unicode, default_value = [],
help="List of keys of children objects of the model.", allow_none=False)
_children_lists_attr = List(Unicode, default_value = [],
help="List of keys containing lists of children objects of the model.",
allow_none=False)
widget_construction_callback = None
def on_widget_constructed(callback):
@ -59,9 +63,10 @@ class BaseWidget(LoggingConfigurable):
to use to represent the widget.""")
# Private/protected declarations
# todo: change this to a context manager
_property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
_displayed = False
_comm = None
_comm = Instance('IPython.kernel.comm.Comm')
def __init__(self, **kwargs):
"""Public constructor
@ -72,6 +77,7 @@ class BaseWidget(LoggingConfigurable):
# Register after init to allow default values to be specified
# TODO: register three different handlers, one for each list, and abstract out the common parts
#print self.keys, self._children_attr, self._children_lists_attr
self.on_trait_change(self._handle_property_changed, self.keys+self._children_attr+self._children_lists_attr)
Widget._handle_widget_constructed(self)
@ -90,7 +96,7 @@ class BaseWidget(LoggingConfigurable):
# Properties
@property
def keys(self):
keys = ['_children_attr', '_children_lists_attr']
keys = ['_children_attr', '_children_lists_attr', 'default_view_name']
keys.extend(self._keys)
return keys
@ -118,7 +124,6 @@ class BaseWidget(LoggingConfigurable):
if 'custom_content' in data:
self._handle_custom_msg(data['custom_content'])
def _handle_custom_msg(self, content):
"""Called when a custom msg is recieved."""
for handler in self._msg_callbacks:
@ -153,7 +158,7 @@ class BaseWidget(LoggingConfigurable):
def _handle_property_changed(self, name, old, new):
"""Called when a proeprty has been changed."""
"""Called when a property has been changed."""
# Make sure this isn't information that the front-end just sent us.
if self._property_lock[0] != name and self._property_lock[1] != new:
# Send new state to frontend
@ -197,7 +202,7 @@ class BaseWidget(LoggingConfigurable):
self._send({"method": "update",
"state": self.get_state()})
def get_state(self, key=None)
def get_state(self, key=None):
"""Gets the widget state, or a piece of it.
Parameters
@ -308,15 +313,18 @@ class BaseWidget(LoggingConfigurable):
def _open_communication(self):
"""Opens a communication with the front-end."""
# Create a comm.
if not hasattr(self, '_comm') or self._comm is None:
if self._comm is None:
self._comm = Comm(target_name=self.target_name)
self._comm.on_msg(self._handle_msg)
self._comm.on_close(self._close_communication)
# first update
self.send_state()
def _close_communication(self):
"""Closes a communication with the front-end."""
if hasattr(self, '_comm') and self._comm is not None:
if self._comm is not None:
try:
self._comm.close()
finally:
@ -332,9 +340,6 @@ class BaseWidget(LoggingConfigurable):
return False
class Widget(BaseWidget):
_children = List(Instance('IPython.html.widgets.widget.Widget'))
_children_lists_attr = List(Unicode, ['_children'])
visible = Bool(True, help="Whether or not the widget is visible.")
# Private/protected declarations

View File

@ -59,7 +59,7 @@ class ButtonWidget(Widget):
def _handle_button_msg(self, content):
"""Hanlde a msg from the front-end
"""Handle a msg from the front-end
Parameters
----------

View File

@ -14,7 +14,7 @@ Represents a container that can be used to group other widgets.
# Imports
#-----------------------------------------------------------------------------
from .widget import Widget
from IPython.utils.traitlets import Unicode, Bool
from IPython.utils.traitlets import Unicode, Bool, List, Instance
#-----------------------------------------------------------------------------
# Classes
@ -23,6 +23,9 @@ class ContainerWidget(Widget):
target_name = Unicode('ContainerWidgetModel')
default_view_name = Unicode('ContainerView')
children = []#List(Instance('IPython.html.widgets.widget.Widget'))
_children_lists_attr = List(Unicode, ['children'])
# Keys, all private and managed by helper methods. Flexible box model
# classes...
_keys = ['_vbox', '_hbox', '_align_start', '_align_end', '_align_center',

View File

@ -15,7 +15,7 @@ pages.
# Imports
#-----------------------------------------------------------------------------
from .widget import Widget
from IPython.utils.traitlets import Unicode, Dict, Int
from IPython.utils.traitlets import Unicode, Dict, Int, List, Instance
#-----------------------------------------------------------------------------
# Classes
@ -29,9 +29,12 @@ class MulticontainerWidget(Widget):
_titles = Dict(help="Titles of the pages")
selected_index = Int(0)
children = []#List(Instance('IPython.html.widgets.widget.Widget'))
_children_lists_attr = List(Unicode, ['children'])
# Public methods
def set_title(self, index, title):
"""Sets the title of a container pages
"""Sets the title of a container page
Parameters
----------

View File

@ -110,7 +110,17 @@
],
"language": "python",
"metadata": {},
"outputs": [],
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'value', 'step', 'max', 'min', 'disabled', 'orientation', 'description']\n",
"[]\n",
"[]\n"
]
}
],
"prompt_number": 3
},
{
@ -131,6 +141,26 @@
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"mywidget.value"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 13,
"text": [
"55.1"
]
}
],
"prompt_number": 13
},
{
"cell_type": "markdown",
"metadata": {},
@ -152,10 +182,13 @@
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"prompt_number": 11,
"text": [
"['visible',\n",
" '_css',\n",
" '_children_attr',\n",
" '_children_lists_attr',\n",
" 'default_view_name',\n",
" 'value',\n",
" 'step',\n",
" 'max',\n",
@ -166,7 +199,7 @@
]
}
],
"prompt_number": 5
"prompt_number": 11
},
{
"cell_type": "markdown",
@ -184,7 +217,7 @@
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
"prompt_number": 12
},
{
"cell_type": "markdown",
@ -205,13 +238,13 @@
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"prompt_number": 30,
"text": [
"0.0"
"25.0"
]
}
],
"prompt_number": 7
"prompt_number": 30
},
{
"cell_type": "markdown",
@ -229,8 +262,38 @@
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 8
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'value', 'values', 'disabled', 'description']\n",
"[]\n",
"[]\n"
]
}
],
"prompt_number": 14
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"mysecondwidget.value"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 15,
"text": [
"u'Item C'"
]
}
],
"prompt_number": 15
},
{
"cell_type": "heading",
@ -259,13 +322,13 @@
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 9,
"prompt_number": 16,
"text": [
"u'FloatSliderView'"
]
}
],
"prompt_number": 9
"prompt_number": 16
},
{
"cell_type": "markdown",
@ -284,7 +347,7 @@
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 10
"prompt_number": 17
},
{
"cell_type": "markdown",

View File

@ -108,7 +108,7 @@
"def on_value_change(name, value):\n",
" print(value)\n",
"\n",
"intrange.on_trait_change(on_value_change, 'value')"
"#intrange.on_trait_change(on_value_change, 'value')"
],
"language": "python",
"metadata": {},
@ -117,25 +117,13 @@
"output_type": "stream",
"stream": "stdout",
"text": [
"28\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"55\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"94\n"
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'value', 'step', 'max', 'min', 'disabled', 'orientation', 'description']\n",
"[]\n",
"[]\n"
]
}
],
"prompt_number": 3
"prompt_number": 9
},
{
"cell_type": "heading",
@ -196,6 +184,26 @@
"Button clicks are transmitted from the front-end to the back-end using custom messages. By using the `on_click` method, a button that prints a message when it has been clicked is shown below."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"display(intrange)\n",
"print('hi')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"hi\n"
]
}
],
"prompt_number": 5
},
{
"cell_type": "code",
"collapsed": false,
@ -205,12 +213,29 @@
"\n",
"def on_button_clicked(sender):\n",
" print(\"Button clicked.\")\n",
" intrange.value +=1\n",
"\n",
"button.on_click(on_button_clicked)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Button clicked.\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
@ -233,7 +258,7 @@
]
}
],
"prompt_number": 5
"prompt_number": 12
},
{
"cell_type": "markdown",
@ -242,6 +267,36 @@
"Event handlers can also be used to create widgets. In the example below, clicking a button spawns another button with a description equal to how many times the parent button had been clicked at the time."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"{'content': {'data': \"{'parent_header': {}, 'msg_type': u'comm_msg', 'msg_id': u'3DBB06AD83C942DD85DC6477B08F1FBF', 'content': {u'data': {u'method': u'custom', u'custom_content': {u'event': u'click'}}, u'comm_id': u'eea5f11ae7aa473993dd0c81d6016648'}, 'header': {u'username': u'username', u'msg_id': u'3DBB06AD83C942DD85DC6477B08F1FBF', u'msg_type': u'comm_msg', u'session': u'0F6D6BE728DA47A38CFC4BDEACF34FC4'}, 'buffers': [], 'metadata': {}}\\ncustom message {'parent_header': {}, 'msg_type': u'comm_msg', 'msg_id': u'3DBB06AD83C942DD85DC6477B08F1FBF', 'content': {u'data': {u'method': u'custom', u'custom_content': {u'event': u'click'}}, u'comm_id': u'eea5f11ae7aa473993dd0c81d6016648'}, 'header': {u'username': u'username', u'msg_id': u'3DBB06AD83C942DD85DC6477B08F1FBF', u'msg_type': u'comm_msg', u'session': u'0F6D6BE728DA47A38CFC4BDEACF34FC4'}, 'buffers': [], 'metadata': {}}\\nhandling click\\n{u'event': u'click'}\\nButton clicked.\\n2\\n\",\n",
" 'name': 'stdout'},\n",
" 'header': {'msg_id': 'd9dc144a-d86c-42c1-8bab-f8a6bc525723',\n",
" 'msg_type': 'stream',\n",
" 'session': '9b9408d8-7420-4e0c-976d-cdda9f8d2564',\n",
" 'username': 'kernel'},\n",
" 'metadata': {},\n",
" 'msg_id': 'd9dc144a-d86c-42c1-8bab-f8a6bc525723',\n",
" 'msg_type': 'stream',\n",
" 'parent_header': {'msg_id': '3DBB06AD83C942DD85DC6477B08F1FBF',\n",
" 'msg_type': 'comm_msg',\n",
" 'session': '0F6D6BE728DA47A38CFC4BDEACF34FC4',\n",
" 'username': 'username'}}"
]
}
],
"prompt_number": 11
},
{
"cell_type": "code",
"collapsed": false,
@ -261,8 +316,161 @@
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
},
{
"output_type": "stream",
"stream": "stdout",
"text": [
"['visible', '_css', '_children_attr', '_children_lists_attr', 'default_view_name', 'description', 'disabled']\n",
"[]\n",
"[]\n"
]
}
],
"prompt_number": 7
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}