Merge pull request #6454 from jasongrout/links

Javascript link
Jonathan Frederic 12 years ago
commit 3ea8929b82

@ -106,9 +106,9 @@ define([
console.error('Comm promise not found for comm id ' + content.comm_id);
return;
}
var that = this;
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
this.unregister_comm(comm);
that.unregister_comm(comm);
try {
comm.handle_close(msg);
} catch (e) {

@ -3,6 +3,7 @@
define([
"widgets/js/manager",
"widgets/js/widget_link",
"widgets/js/widget_bool",
"widgets/js/widget_button",
"widgets/js/widget_box",
@ -13,10 +14,15 @@ define([
"widgets/js/widget_selection",
"widgets/js/widget_selectioncontainer",
"widgets/js/widget_string",
], function(widgetmanager) {
], function(widgetmanager, linkModels) {
for (var target_name in linkModels) {
if (linkModels.hasOwnProperty(target_name)) {
widgetmanager.WidgetManager.register_widget_model(target_name, linkModels[target_name]);
}
}
// Register all of the loaded views with the widget manager.
for (var i = 1; i < arguments.length; i++) {
for (var i = 2; i < arguments.length; i++) {
for (var target_name in arguments[i]) {
if (arguments[i].hasOwnProperty(target_name)) {
widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);

@ -0,0 +1,86 @@
// Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"widgets/js/widget",
"jquery",
], function(widget, $){
var LinkModel = widget.WidgetModel.extend({
initialize: function() {
this.on("change:widgets", function(model, value, options) {
this.update_bindings(model.previous("widgets") || [], value);
this.update_value(this.get("widgets")[0]);
}, this);
this.once("destroy", function(model, collection, options) {
this.update_bindings(this.get("widgets"), []);
}, this);
},
update_bindings: function(oldlist, newlist) {
var that = this;
_.each(oldlist, function(elt) {elt[0].off("change:" + elt[1], null, that);});
_.each(newlist, function(elt) {elt[0].on("change:" + elt[1],
function(model, value, options) {
that.update_value(elt);
}, that);
// TODO: register for any destruction handlers
// to take an item out of the list
});
},
update_value: function(elt) {
if (this.updating) {return;}
var model = elt[0];
var attr = elt[1];
var new_value = model.get(attr);
this.updating = true;
_.each(_.without(this.get("widgets"), elt),
function(element, index, list) {
if (element[0]) {
element[0].set(element[1], new_value);
element[0].save_changes();
}
}, this);
this.updating = false;
},
});
var DirectionalLinkModel = widget.WidgetModel.extend({
initialize: function() {
this.on("change", this.update_bindings, this);
this.once("destroy", function() {
if (this.source) {
this.source[0].off("change:" + this.source[1], null, this);
}
}, this);
},
update_bindings: function() {
if (this.source) {
this.source[0].off("change:" + this.source[1], null, this);
}
this.source = this.get("source");
if (this.source) {
this.source[0].on("change:" + this.source[1], function() { this.update_value(this.source); }, this);
this.update_value(this.source);
}
},
update_value: function(elt) {
if (this.updating) {return;}
var model = elt[0];
var attr = elt[1];
var new_value = model.get(attr);
this.updating = true;
_.each(this.get("targets"),
function(element, index, list) {
if (element[0]) {
element[0].set(element[1], new_value);
element[0].save_changes();
}
}, this);
this.updating = false;
},
});
return {
"LinkModel": LinkModel,
"DirectionalLinkModel": DirectionalLinkModel,
}
});

@ -11,6 +11,7 @@ from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
from .widget_selectioncontainer import Tab, Accordion
from .widget_string import HTML, Latex, Text, Textarea
from .interaction import interact, interactive, fixed, interact_manual
from .widget_link import Link, link, DirectionalLink, dlink
# Deprecated classes
from .widget_bool import CheckboxWidget, ToggleButtonWidget

@ -0,0 +1,61 @@
"""Link and DirectionalLink classes.
Propagate changes between widgets on the javascript side
"""
#-----------------------------------------------------------------------------
# Copyright (c) 2014, the IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
from .widget import Widget
from IPython.utils.traitlets import Unicode, Tuple, Any
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
class Link(Widget):
"""Link Widget"""
_model_name = Unicode('LinkModel', sync=True)
widgets = Tuple(sync=True, allow_none=False)
def __init__(self, widgets=(), **kwargs):
kwargs['widgets'] = widgets
super(Link, self).__init__(**kwargs)
# for compatibility with traitlet links
def unlink(self):
self.close()
def link(*args):
return Link(widgets=args)
class DirectionalLink(Widget):
"""Directional Link Widget"""
_model_name = Unicode('DirectionalLinkModel', sync=True)
targets = Any(sync=True)
source = Tuple(sync=True)
# Does not quite behave like other widgets but reproduces
# the behavior of IPython.utils.traitlets.directional_link
def __init__(self, source, targets=(), **kwargs):
kwargs['source'] = source
kwargs['targets'] = targets
super(DirectionalLink, self).__init__(**kwargs)
# for compatibility with traitlet links
def unlink(self):
self.close()
def dlink(source, *targets):
return DirectionalLink(source, targets)

@ -197,6 +197,154 @@
"int_range.on_trait_change(on_value_change, 'value')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Linking Widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often, you may want to simply link widget attributes together. Synchronization of attributes can be done in a simpler way than by using bare traitlets events. \n",
"\n",
"The first method is to use the `link` and `directional_link` functions from the `traitlets` module. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Linking traitlets attributes from the server side"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.utils import traitlets"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'The values of slider1, slider2 and slider3 are synchronized')\n",
"sliders1, slider2, slider3 = widgets.IntSlider(description='Slider 1'),\\\n",
" widgets.IntSlider(description='Slider 2'),\\\n",
" widgets.IntSlider(description='Slider 3')\n",
"l = traitlets.link((sliders1, 'value'), (slider2, 'value'), (slider3, 'value'))\n",
"display(caption, sliders1, slider2, slider3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'Changes in source values are reflected in target1 and target2')\n",
"source, target1, target2 = widgets.IntSlider(description='Source'),\\\n",
" widgets.IntSlider(description='Target 1'),\\\n",
" widgets.IntSlider(description='Target 2')\n",
"traitlets.dlink((source, 'value'), (target1, 'value'), (target2, 'value'))\n",
"display(caption, source, target1, target2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Function `traitlets.link` returns a `Link` object. The link can be broken by calling the `unlink` method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# l.unlink()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Linking widgets attributes from the client side"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When synchronizing traitlets attributes, you may experience a lag because of the latency dues to the rountrip to the server side. You can also directly link widgets attributes, either in a unidirectional or a bidirectional fashion using the link widgets. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'The values of range1, range2 and range3 are synchronized')\n",
"range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n",
" widgets.IntSlider(description='Range 2'),\\\n",
" widgets.IntSlider(description='Range 3')\n",
"l = widgets.link((range1, 'value'), (range2, 'value'), (range3, 'value'))\n",
"display(caption, range1, range2, range3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'Changes in source_range values are reflected in target_range1 and target_range2')\n",
"source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n",
" widgets.IntSlider(description='Target range 1'),\\\n",
" widgets.IntSlider(description='Target range 2')\n",
"widgets.dlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n",
"display(caption, source_range, target_range1, target_range2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Function `widgets.link` returns a `Link` widget. The link can be broken by calling the `unlink` method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# l.unlink()"
]
},
{
"cell_type": "markdown",
"metadata": {},
@ -213,15 +361,22 @@
]
],
"kernelspec": {
"display_name": "Python 2",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "python",
"name": "ipython",
"version": 2
},
"display_name": "Python 2",
"language": "python",
"name": "python2"
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.8"
},
"signature": "sha256:05a3e92089b37f68e3134587ffef6ef73830e5f8b3c515ba24640d7c803820c3"
"signature": "sha256:b6eadc174d0d9c1907518d9f37760eb3dca3aec0ef1f3746e6f0537a36e99919"
},
"nbformat": 4,
"nbformat_minor": 0

Loading…
Cancel
Save