##// 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)
@@ -1,217 +1,217 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 ], function(IPython, $, utils) {
9 9 "use strict";
10 10
11 11 //-----------------------------------------------------------------------
12 12 // CommManager class
13 13 //-----------------------------------------------------------------------
14 14
15 15 var CommManager = function (kernel) {
16 16 this.comms = {};
17 17 this.targets = {};
18 18 if (kernel !== undefined) {
19 19 this.init_kernel(kernel);
20 20 }
21 21 };
22 22
23 23 CommManager.prototype.init_kernel = function (kernel) {
24 24 /**
25 25 * connect the kernel, and register message handlers
26 26 */
27 27 this.kernel = kernel;
28 28 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
29 29 for (var i = 0; i < msg_types.length; i++) {
30 30 var msg_type = msg_types[i];
31 31 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
32 32 }
33 33 };
34 34
35 35 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
36 36 /**
37 37 * Create a new Comm, register it, and open its Kernel-side counterpart
38 38 * Mimics the auto-registration in `Comm.__init__` in the IPython Comm
39 39 */
40 40 var comm = new Comm(target_name);
41 41 this.register_comm(comm);
42 42 comm.open(data, callbacks, metadata);
43 43 return comm;
44 44 };
45 45
46 46 CommManager.prototype.register_target = function (target_name, f) {
47 47 /**
48 48 * Register a target function for a given target name
49 49 */
50 50 this.targets[target_name] = f;
51 51 };
52 52
53 53 CommManager.prototype.unregister_target = function (target_name, f) {
54 54 /**
55 55 * Unregister a target function for a given target name
56 56 */
57 57 delete this.targets[target_name];
58 58 };
59 59
60 60 CommManager.prototype.register_comm = function (comm) {
61 61 /**
62 62 * Register a comm in the mapping
63 63 */
64 64 this.comms[comm.comm_id] = Promise.resolve(comm);
65 65 comm.kernel = this.kernel;
66 66 return comm.comm_id;
67 67 };
68 68
69 69 CommManager.prototype.unregister_comm = function (comm) {
70 70 /**
71 71 * Remove a comm from the mapping
72 72 */
73 73 delete this.comms[comm.comm_id];
74 74 };
75 75
76 76 // comm message handlers
77 77
78 78 CommManager.prototype.comm_open = function (msg) {
79 79 var content = msg.content;
80 80 var that = this;
81 81 var comm_id = content.comm_id;
82 82
83 83 this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
84 84 this.targets).then(function(target) {
85 85 var comm = new Comm(content.target_name, comm_id);
86 86 comm.kernel = that.kernel;
87 87 try {
88 88 var response = target(comm, msg);
89 89 } catch (e) {
90 90 comm.close();
91 91 that.unregister_comm(comm);
92 92 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
93 93 console.error(wrapped_error);
94 94 return Promise.reject(wrapped_error);
95 95 }
96 96 // Regardless of the target return value, we need to
97 97 // then return the comm
98 98 return Promise.resolve(response).then(function() {return comm;});
99 99 }, utils.reject('Could not open comm', true));
100 100 return this.comms[comm_id];
101 101 };
102 102
103 103 CommManager.prototype.comm_close = function(msg) {
104 104 var content = msg.content;
105 105 if (this.comms[content.comm_id] === undefined) {
106 106 console.error('Comm promise not found for comm id ' + content.comm_id);
107 107 return;
108 108 }
109
109 var that = this;
110 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 112 try {
113 113 comm.handle_close(msg);
114 114 } catch (e) {
115 115 console.log("Exception closing comm: ", e, e.stack, msg);
116 116 }
117 117 // don't return a comm, so that further .then() functions
118 118 // get an undefined comm input
119 119 });
120 120 };
121 121
122 122 CommManager.prototype.comm_msg = function(msg) {
123 123 var content = msg.content;
124 124 if (this.comms[content.comm_id] === undefined) {
125 125 console.error('Comm promise not found for comm id ' + content.comm_id);
126 126 return;
127 127 }
128 128
129 129 this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
130 130 try {
131 131 comm.handle_msg(msg);
132 132 } catch (e) {
133 133 console.log("Exception handling comm msg: ", e, e.stack, msg);
134 134 }
135 135 return comm;
136 136 });
137 137 };
138 138
139 139 //-----------------------------------------------------------------------
140 140 // Comm base class
141 141 //-----------------------------------------------------------------------
142 142
143 143 var Comm = function (target_name, comm_id) {
144 144 this.target_name = target_name;
145 145 this.comm_id = comm_id || utils.uuid();
146 146 this._msg_callback = this._close_callback = null;
147 147 };
148 148
149 149 // methods for sending messages
150 150 Comm.prototype.open = function (data, callbacks, metadata) {
151 151 var content = {
152 152 comm_id : this.comm_id,
153 153 target_name : this.target_name,
154 154 data : data || {},
155 155 };
156 156 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
157 157 };
158 158
159 159 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
160 160 var content = {
161 161 comm_id : this.comm_id,
162 162 data : data || {},
163 163 };
164 164 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
165 165 };
166 166
167 167 Comm.prototype.close = function (data, callbacks, metadata) {
168 168 var content = {
169 169 comm_id : this.comm_id,
170 170 data : data || {},
171 171 };
172 172 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
173 173 };
174 174
175 175 // methods for registering callbacks for incoming messages
176 176 Comm.prototype._register_callback = function (key, callback) {
177 177 this['_' + key + '_callback'] = callback;
178 178 };
179 179
180 180 Comm.prototype.on_msg = function (callback) {
181 181 this._register_callback('msg', callback);
182 182 };
183 183
184 184 Comm.prototype.on_close = function (callback) {
185 185 this._register_callback('close', callback);
186 186 };
187 187
188 188 // methods for handling incoming messages
189 189
190 190 Comm.prototype._callback = function (key, msg) {
191 191 var callback = this['_' + key + '_callback'];
192 192 if (callback) {
193 193 try {
194 194 callback(msg);
195 195 } catch (e) {
196 196 console.log("Exception in Comm callback", e, e.stack, msg);
197 197 }
198 198 }
199 199 };
200 200
201 201 Comm.prototype.handle_msg = function (msg) {
202 202 this._callback('msg', msg);
203 203 };
204 204
205 205 Comm.prototype.handle_close = function (msg) {
206 206 this._callback('close', msg);
207 207 };
208 208
209 209 // For backwards compatability.
210 210 IPython.CommManager = CommManager;
211 211 IPython.Comm = Comm;
212 212
213 213 return {
214 214 'CommManager': CommManager,
215 215 'Comm': Comm
216 216 };
217 217 });
@@ -1,28 +1,34 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "widgets/js/manager",
6 "widgets/js/widget_link",
6 7 "widgets/js/widget_bool",
7 8 "widgets/js/widget_button",
8 9 "widgets/js/widget_box",
9 10 "widgets/js/widget_float",
10 11 "widgets/js/widget_image",
11 12 "widgets/js/widget_int",
12 13 "widgets/js/widget_output",
13 14 "widgets/js/widget_selection",
14 15 "widgets/js/widget_selectioncontainer",
15 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 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 26 for (var target_name in arguments[i]) {
21 27 if (arguments[i].hasOwnProperty(target_name)) {
22 28 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
23 29 }
24 30 }
25 31 }
26 32
27 33 return {'WidgetManager': widgetmanager.WidgetManager};
28 34 });
@@ -1,29 +1,30 b''
1 1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
2 2
3 3 from .widget_bool import Checkbox, ToggleButton
4 4 from .widget_button import Button
5 5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
6 6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
7 7 from .widget_image import Image
8 8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 9 from .widget_output import Output
10 10 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
11 11 from .widget_selectioncontainer import Tab, Accordion
12 12 from .widget_string import HTML, Latex, Text, Textarea
13 13 from .interaction import interact, interactive, fixed, interact_manual
14 from .widget_link import Link, link, DirectionalLink, dlink
14 15
15 16 # Deprecated classes
16 17 from .widget_bool import CheckboxWidget, ToggleButtonWidget
17 18 from .widget_button import ButtonWidget
18 19 from .widget_box import ContainerWidget, PopupWidget
19 20 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
20 21 from .widget_image import ImageWidget
21 22 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
22 23 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
23 24 from .widget_selectioncontainer import TabWidget, AccordionWidget
24 25 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
25 26
26 27 # Warn on import
27 28 from IPython.utils.warn import warn
28 29 warn("""The widget API is still considered experimental and
29 30 may change by the next major release of IPython.""")
@@ -1,228 +1,383 b''
1 1 {
2 2 "cells": [
3 3 {
4 4 "cell_type": "markdown",
5 5 "metadata": {},
6 6 "source": [
7 7 "[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
8 8 ]
9 9 },
10 10 {
11 11 "cell_type": "markdown",
12 12 "metadata": {
13 13 "slideshow": {
14 14 "slide_type": "slide"
15 15 }
16 16 },
17 17 "source": [
18 18 "# Widget Events"
19 19 ]
20 20 },
21 21 {
22 22 "cell_type": "markdown",
23 23 "metadata": {},
24 24 "source": [
25 25 "## Special events"
26 26 ]
27 27 },
28 28 {
29 29 "cell_type": "code",
30 30 "execution_count": null,
31 31 "metadata": {
32 32 "collapsed": false
33 33 },
34 34 "outputs": [],
35 35 "source": [
36 36 "from __future__ import print_function"
37 37 ]
38 38 },
39 39 {
40 40 "cell_type": "markdown",
41 41 "metadata": {},
42 42 "source": [
43 43 "The `Button` is not used to represent a data type. Instead the button widget is used to **handle mouse clicks**. The **`on_click` method** of the `Button` can be used to register function to be called when the button is clicked. The doc string of the `on_click` can be seen below."
44 44 ]
45 45 },
46 46 {
47 47 "cell_type": "code",
48 48 "execution_count": null,
49 49 "metadata": {
50 50 "collapsed": false
51 51 },
52 52 "outputs": [],
53 53 "source": [
54 54 "from IPython.html import widgets\n",
55 55 "print(widgets.Button.on_click.__doc__)"
56 56 ]
57 57 },
58 58 {
59 59 "cell_type": "markdown",
60 60 "metadata": {
61 61 "slideshow": {
62 62 "slide_type": "slide"
63 63 }
64 64 },
65 65 "source": [
66 66 "### Example"
67 67 ]
68 68 },
69 69 {
70 70 "cell_type": "markdown",
71 71 "metadata": {},
72 72 "source": [
73 73 "Since button clicks are **stateless**, they 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."
74 74 ]
75 75 },
76 76 {
77 77 "cell_type": "code",
78 78 "execution_count": null,
79 79 "metadata": {
80 80 "collapsed": false
81 81 },
82 82 "outputs": [],
83 83 "source": [
84 84 "from IPython.display import display\n",
85 85 "button = widgets.Button(description=\"Click Me!\")\n",
86 86 "display(button)\n",
87 87 "\n",
88 88 "def on_button_clicked(b):\n",
89 89 " print(\"Button clicked.\")\n",
90 90 "\n",
91 91 "button.on_click(on_button_clicked)"
92 92 ]
93 93 },
94 94 {
95 95 "cell_type": "markdown",
96 96 "metadata": {
97 97 "slideshow": {
98 98 "slide_type": "slide"
99 99 }
100 100 },
101 101 "source": [
102 102 "### on_sumbit"
103 103 ]
104 104 },
105 105 {
106 106 "cell_type": "markdown",
107 107 "metadata": {},
108 108 "source": [
109 109 "The **`Text`** also has a special **`on_submit` event**. The `on_submit` event **fires when the user hits return**."
110 110 ]
111 111 },
112 112 {
113 113 "cell_type": "code",
114 114 "execution_count": null,
115 115 "metadata": {
116 116 "collapsed": false
117 117 },
118 118 "outputs": [],
119 119 "source": [
120 120 "text = widgets.Text()\n",
121 121 "display(text)\n",
122 122 "\n",
123 123 "def handle_submit(sender):\n",
124 124 " print(text.value)\n",
125 125 "\n",
126 126 "text.on_submit(handle_submit)"
127 127 ]
128 128 },
129 129 {
130 130 "cell_type": "markdown",
131 131 "metadata": {
132 132 "slideshow": {
133 133 "slide_type": "slide"
134 134 }
135 135 },
136 136 "source": [
137 137 "## Traitlet events"
138 138 ]
139 139 },
140 140 {
141 141 "cell_type": "markdown",
142 142 "metadata": {},
143 143 "source": [
144 144 "**Widget properties are IPython traitlets** and **traitlets are eventful**. To handle changes, the **`on_trait_change` method** of the widget can be used to **register a callback**. The doc string for `on_trait_change` can be seen below."
145 145 ]
146 146 },
147 147 {
148 148 "cell_type": "code",
149 149 "execution_count": null,
150 150 "metadata": {
151 151 "collapsed": false
152 152 },
153 153 "outputs": [],
154 154 "source": [
155 155 "print(widgets.Widget.on_trait_change.__doc__)"
156 156 ]
157 157 },
158 158 {
159 159 "cell_type": "markdown",
160 160 "metadata": {
161 161 "slideshow": {
162 162 "slide_type": "slide"
163 163 }
164 164 },
165 165 "source": [
166 166 "### Signatures"
167 167 ]
168 168 },
169 169 {
170 170 "cell_type": "markdown",
171 171 "metadata": {},
172 172 "source": [
173 173 "Mentioned in the doc string, the callback registered can have **4 possible signatures**:\n",
174 174 "\n",
175 175 "- callback()\n",
176 176 "- callback(trait_name)\n",
177 177 "- callback(trait_name, new_value)\n",
178 178 "- callback(trait_name, old_value, new_value)\n",
179 179 "\n",
180 180 "Using this method, an example of how to output an `IntSlider`'s value as it is changed can be seen below."
181 181 ]
182 182 },
183 183 {
184 184 "cell_type": "code",
185 185 "execution_count": null,
186 186 "metadata": {
187 187 "collapsed": false
188 188 },
189 189 "outputs": [],
190 190 "source": [
191 191 "int_range = widgets.IntSlider()\n",
192 192 "display(int_range)\n",
193 193 "\n",
194 194 "def on_value_change(name, value):\n",
195 195 " print(value)\n",
196 196 "\n",
197 197 "int_range.on_trait_change(on_value_change, 'value')"
198 198 ]
199 199 },
200 200 {
201 201 "cell_type": "markdown",
202 202 "metadata": {},
203 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 352 "[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
205 353 ]
206 354 }
207 355 ],
208 356 "metadata": {
209 357 "cell_tags": [
210 358 [
211 359 "<None>",
212 360 null
213 361 ]
214 362 ],
215 363 "kernelspec": {
364 "display_name": "Python 2",
365 "name": "python2"
366 },
367 "language_info": {
216 368 "codemirror_mode": {
217 "name": "python",
369 "name": "ipython",
218 370 "version": 2
219 371 },
220 "display_name": "Python 2",
221 "language": "python",
222 "name": "python2"
372 "file_extension": ".py",
373 "mimetype": "text/x-python",
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 381 "nbformat": 4,
227 382 "nbformat_minor": 0
228 383 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now