##// END OF EJS Templates
Merge pull request #6454 from jasongrout/links...
Jonathan Frederic -
r19401:ce5036c2 merge
parent child Browse files
Show More
@@ -0,0 +1,86 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 "widgets/js/widget",
6 "jquery",
7 ], function(widget, $){
8 var LinkModel = widget.WidgetModel.extend({
9 initialize: function() {
10 this.on("change:widgets", function(model, value, options) {
11 this.update_bindings(model.previous("widgets") || [], value);
12 this.update_value(this.get("widgets")[0]);
13 }, this);
14 this.once("destroy", function(model, collection, options) {
15 this.update_bindings(this.get("widgets"), []);
16 }, this);
17 },
18 update_bindings: function(oldlist, newlist) {
19 var that = this;
20 _.each(oldlist, function(elt) {elt[0].off("change:" + elt[1], null, that);});
21 _.each(newlist, function(elt) {elt[0].on("change:" + elt[1],
22 function(model, value, options) {
23 that.update_value(elt);
24 }, that);
25 // TODO: register for any destruction handlers
26 // to take an item out of the list
27 });
28 },
29 update_value: function(elt) {
30 if (this.updating) {return;}
31 var model = elt[0];
32 var attr = elt[1];
33 var new_value = model.get(attr);
34 this.updating = true;
35 _.each(_.without(this.get("widgets"), elt),
36 function(element, index, list) {
37 if (element[0]) {
38 element[0].set(element[1], new_value);
39 element[0].save_changes();
40 }
41 }, this);
42 this.updating = false;
43 },
44 });
45
46 var DirectionalLinkModel = widget.WidgetModel.extend({
47 initialize: function() {
48 this.on("change", this.update_bindings, this);
49 this.once("destroy", function() {
50 if (this.source) {
51 this.source[0].off("change:" + this.source[1], null, this);
52 }
53 }, this);
54 },
55 update_bindings: function() {
56 if (this.source) {
57 this.source[0].off("change:" + this.source[1], null, this);
58 }
59 this.source = this.get("source");
60 if (this.source) {
61 this.source[0].on("change:" + this.source[1], function() { this.update_value(this.source); }, this);
62 this.update_value(this.source);
63 }
64 },
65 update_value: function(elt) {
66 if (this.updating) {return;}
67 var model = elt[0];
68 var attr = elt[1];
69 var new_value = model.get(attr);
70 this.updating = true;
71 _.each(this.get("targets"),
72 function(element, index, list) {
73 if (element[0]) {
74 element[0].set(element[1], new_value);
75 element[0].save_changes();
76 }
77 }, this);
78 this.updating = false;
79 },
80 });
81
82 return {
83 "LinkModel": LinkModel,
84 "DirectionalLinkModel": DirectionalLinkModel,
85 }
86 });
@@ -0,0 +1,61 b''
1 """Link and DirectionalLink classes.
2
3 Propagate changes between widgets on the javascript side
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2014, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16 from .widget import Widget
17 from IPython.utils.traitlets import Unicode, Tuple, Any
18
19 #-----------------------------------------------------------------------------
20 # Classes
21 #-----------------------------------------------------------------------------
22
23
24 class Link(Widget):
25 """Link Widget"""
26 _model_name = Unicode('LinkModel', sync=True)
27 widgets = Tuple(sync=True, allow_none=False)
28
29 def __init__(self, widgets=(), **kwargs):
30 kwargs['widgets'] = widgets
31 super(Link, self).__init__(**kwargs)
32
33 # for compatibility with traitlet links
34 def unlink(self):
35 self.close()
36
37
38 def link(*args):
39 return Link(widgets=args)
40
41
42 class DirectionalLink(Widget):
43 """Directional Link Widget"""
44 _model_name = Unicode('DirectionalLinkModel', sync=True)
45 targets = Any(sync=True)
46 source = Tuple(sync=True)
47
48 # Does not quite behave like other widgets but reproduces
49 # the behavior of IPython.utils.traitlets.directional_link
50 def __init__(self, source, targets=(), **kwargs):
51 kwargs['source'] = source
52 kwargs['targets'] = targets
53 super(DirectionalLink, self).__init__(**kwargs)
54
55 # for compatibility with traitlet links
56 def unlink(self):
57 self.close()
58
59
60 def dlink(source, *targets):
61 return DirectionalLink(source, targets)
@@ -106,9 +106,9 b' define(['
106 console.error('Comm promise not found for comm id ' + content.comm_id);
106 console.error('Comm promise not found for comm id ' + content.comm_id);
107 return;
107 return;
108 }
108 }
109
109 var that = this;
110 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
110 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
111 this.unregister_comm(comm);
111 that.unregister_comm(comm);
112 try {
112 try {
113 comm.handle_close(msg);
113 comm.handle_close(msg);
114 } catch (e) {
114 } catch (e) {
@@ -3,6 +3,7 b''
3
3
4 define([
4 define([
5 "widgets/js/manager",
5 "widgets/js/manager",
6 "widgets/js/widget_link",
6 "widgets/js/widget_bool",
7 "widgets/js/widget_bool",
7 "widgets/js/widget_button",
8 "widgets/js/widget_button",
8 "widgets/js/widget_box",
9 "widgets/js/widget_box",
@@ -13,10 +14,15 b' define(['
13 "widgets/js/widget_selection",
14 "widgets/js/widget_selection",
14 "widgets/js/widget_selectioncontainer",
15 "widgets/js/widget_selectioncontainer",
15 "widgets/js/widget_string",
16 "widgets/js/widget_string",
16 ], function(widgetmanager) {
17 ], function(widgetmanager, linkModels) {
18 for (var target_name in linkModels) {
19 if (linkModels.hasOwnProperty(target_name)) {
20 widgetmanager.WidgetManager.register_widget_model(target_name, linkModels[target_name]);
21 }
22 }
17
23
18 // Register all of the loaded views with the widget manager.
24 // Register all of the loaded views with the widget manager.
19 for (var i = 1; i < arguments.length; i++) {
25 for (var i = 2; i < arguments.length; i++) {
20 for (var target_name in arguments[i]) {
26 for (var target_name in arguments[i]) {
21 if (arguments[i].hasOwnProperty(target_name)) {
27 if (arguments[i].hasOwnProperty(target_name)) {
22 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
28 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
@@ -11,6 +11,7 b' from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select'
11 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_selectioncontainer import Tab, Accordion
12 from .widget_string import HTML, Latex, Text, Textarea
12 from .widget_string import HTML, Latex, Text, Textarea
13 from .interaction import interact, interactive, fixed, interact_manual
13 from .interaction import interact, interactive, fixed, interact_manual
14 from .widget_link import Link, link, DirectionalLink, dlink
14
15
15 # Deprecated classes
16 # Deprecated classes
16 from .widget_bool import CheckboxWidget, ToggleButtonWidget
17 from .widget_bool import CheckboxWidget, ToggleButtonWidget
@@ -201,6 +201,154 b''
201 "cell_type": "markdown",
201 "cell_type": "markdown",
202 "metadata": {},
202 "metadata": {},
203 "source": [
203 "source": [
204 "# Linking Widgets"
205 ]
206 },
207 {
208 "cell_type": "markdown",
209 "metadata": {},
210 "source": [
211 "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",
212 "\n",
213 "The first method is to use the `link` and `directional_link` functions from the `traitlets` module. "
214 ]
215 },
216 {
217 "cell_type": "markdown",
218 "metadata": {},
219 "source": [
220 "## Linking traitlets attributes from the server side"
221 ]
222 },
223 {
224 "cell_type": "code",
225 "execution_count": null,
226 "metadata": {
227 "collapsed": false
228 },
229 "outputs": [],
230 "source": [
231 "from IPython.utils import traitlets"
232 ]
233 },
234 {
235 "cell_type": "code",
236 "execution_count": null,
237 "metadata": {
238 "collapsed": false
239 },
240 "outputs": [],
241 "source": [
242 "caption = widgets.Latex(value = 'The values of slider1, slider2 and slider3 are synchronized')\n",
243 "sliders1, slider2, slider3 = widgets.IntSlider(description='Slider 1'),\\\n",
244 " widgets.IntSlider(description='Slider 2'),\\\n",
245 " widgets.IntSlider(description='Slider 3')\n",
246 "l = traitlets.link((sliders1, 'value'), (slider2, 'value'), (slider3, 'value'))\n",
247 "display(caption, sliders1, slider2, slider3)"
248 ]
249 },
250 {
251 "cell_type": "code",
252 "execution_count": null,
253 "metadata": {
254 "collapsed": false
255 },
256 "outputs": [],
257 "source": [
258 "caption = widgets.Latex(value = 'Changes in source values are reflected in target1 and target2')\n",
259 "source, target1, target2 = widgets.IntSlider(description='Source'),\\\n",
260 " widgets.IntSlider(description='Target 1'),\\\n",
261 " widgets.IntSlider(description='Target 2')\n",
262 "traitlets.dlink((source, 'value'), (target1, 'value'), (target2, 'value'))\n",
263 "display(caption, source, target1, target2)"
264 ]
265 },
266 {
267 "cell_type": "markdown",
268 "metadata": {},
269 "source": [
270 "Function `traitlets.link` returns a `Link` object. The link can be broken by calling the `unlink` method."
271 ]
272 },
273 {
274 "cell_type": "code",
275 "execution_count": null,
276 "metadata": {
277 "collapsed": false
278 },
279 "outputs": [],
280 "source": [
281 "# l.unlink()"
282 ]
283 },
284 {
285 "cell_type": "markdown",
286 "metadata": {},
287 "source": [
288 "## Linking widgets attributes from the client side"
289 ]
290 },
291 {
292 "cell_type": "markdown",
293 "metadata": {},
294 "source": [
295 "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. "
296 ]
297 },
298 {
299 "cell_type": "code",
300 "execution_count": null,
301 "metadata": {
302 "collapsed": false
303 },
304 "outputs": [],
305 "source": [
306 "caption = widgets.Latex(value = 'The values of range1, range2 and range3 are synchronized')\n",
307 "range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n",
308 " widgets.IntSlider(description='Range 2'),\\\n",
309 " widgets.IntSlider(description='Range 3')\n",
310 "l = widgets.link((range1, 'value'), (range2, 'value'), (range3, 'value'))\n",
311 "display(caption, range1, range2, range3)"
312 ]
313 },
314 {
315 "cell_type": "code",
316 "execution_count": null,
317 "metadata": {
318 "collapsed": false
319 },
320 "outputs": [],
321 "source": [
322 "caption = widgets.Latex(value = 'Changes in source_range values are reflected in target_range1 and target_range2')\n",
323 "source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n",
324 " widgets.IntSlider(description='Target range 1'),\\\n",
325 " widgets.IntSlider(description='Target range 2')\n",
326 "widgets.dlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n",
327 "display(caption, source_range, target_range1, target_range2)"
328 ]
329 },
330 {
331 "cell_type": "markdown",
332 "metadata": {},
333 "source": [
334 "Function `widgets.link` returns a `Link` widget. The link can be broken by calling the `unlink` method."
335 ]
336 },
337 {
338 "cell_type": "code",
339 "execution_count": null,
340 "metadata": {
341 "collapsed": false
342 },
343 "outputs": [],
344 "source": [
345 "# l.unlink()"
346 ]
347 },
348 {
349 "cell_type": "markdown",
350 "metadata": {},
351 "source": [
204 "[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
352 "[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
205 ]
353 ]
206 }
354 }
@@ -213,15 +361,22 b''
213 ]
361 ]
214 ],
362 ],
215 "kernelspec": {
363 "kernelspec": {
364 "display_name": "Python 2",
365 "name": "python2"
366 },
367 "language_info": {
216 "codemirror_mode": {
368 "codemirror_mode": {
217 "name": "python",
369 "name": "ipython",
218 "version": 2
370 "version": 2
219 },
371 },
220 "display_name": "Python 2",
372 "file_extension": ".py",
221 "language": "python",
373 "mimetype": "text/x-python",
222 "name": "python2"
374 "name": "python",
375 "nbconvert_exporter": "python",
376 "pygments_lexer": "ipython2",
377 "version": "2.7.8"
223 },
378 },
224 "signature": "sha256:05a3e92089b37f68e3134587ffef6ef73830e5f8b3c515ba24640d7c803820c3"
379 "signature": "sha256:b6eadc174d0d9c1907518d9f37760eb3dca3aec0ef1f3746e6f0537a36e99919"
225 },
380 },
226 "nbformat": 4,
381 "nbformat": 4,
227 "nbformat_minor": 0
382 "nbformat_minor": 0
General Comments 0
You need to be logged in to leave comments. Login now