##// END OF EJS Templates
Work on widget tutorials
Brian E. Granger -
Show More
@@ -1,349 +1,335 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 9 "name": "",
10 "signature": "sha256:25599ae01786ec88374ddc5bd814583b1c0cd9fbe69f59fefaeba5c3b1507735"
10 "signature": "sha256:19f2a57c9ac75d472a9e403515009613fac3fbc733219f7fd4f39a0616ca619f"
11 11 },
12 12 "nbformat": 3,
13 13 "nbformat_minor": 0,
14 14 "worksheets": [
15 15 {
16 16 "cells": [
17 17 {
18 "cell_type": "markdown",
19 "metadata": {},
20 "source": [
21 "[< Back to Part 4](Part 4 - Styles.ipynb) or [Index](index.ipynb)"
22 ]
23 },
24 {
25 18 "cell_type": "code",
26 19 "collapsed": false,
27 20 "input": [
28 21 "from IPython.html import widgets # Widget definitions\n",
29 22 "from IPython.display import display # Used to display widgets in the notebook"
30 23 ],
31 24 "language": "python",
32 25 "metadata": {},
33 26 "outputs": [],
34 27 "prompt_number": 1
35 28 },
36 29 {
37 30 "cell_type": "heading",
38 31 "level": 1,
39 32 "metadata": {},
40 33 "source": [
41 34 "Alignment"
42 35 ]
43 36 },
44 37 {
45 38 "cell_type": "markdown",
46 39 "metadata": {},
47 40 "source": [
48 41 "Most widgets have a `description` attribute, which allows a label for the widget to be defined.\n",
49 42 "The label of the widget has a fixed minimum width.\n",
50 43 "The text of the label is always right aligned and the widget is left aligned:"
51 44 ]
52 45 },
53 46 {
54 47 "cell_type": "code",
55 48 "collapsed": false,
56 49 "input": [
57 50 "display(widgets.TextWidget(description=\"a:\"))\n",
58 51 "display(widgets.TextWidget(description=\"aa:\"))\n",
59 52 "display(widgets.TextWidget(description=\"aaa:\"))"
60 53 ],
61 54 "language": "python",
62 55 "metadata": {},
63 56 "outputs": [],
64 57 "prompt_number": 2
65 58 },
66 59 {
67 60 "cell_type": "markdown",
68 61 "metadata": {},
69 62 "source": [
70 63 "If a label is longer than the minimum width, the widget is shifted to the right:"
71 64 ]
72 65 },
73 66 {
74 67 "cell_type": "code",
75 68 "collapsed": false,
76 69 "input": [
77 70 "display(widgets.TextWidget(description=\"a:\"))\n",
78 71 "display(widgets.TextWidget(description=\"aa:\"))\n",
79 72 "display(widgets.TextWidget(description=\"aaa:\"))\n",
80 73 "display(widgets.TextWidget(description=\"aaaaaaaaaaaaaaaaaa:\"))"
81 74 ],
82 75 "language": "python",
83 76 "metadata": {},
84 77 "outputs": [],
85 78 "prompt_number": 3
86 79 },
87 80 {
88 81 "cell_type": "markdown",
89 82 "metadata": {},
90 83 "source": [
91 84 "If a `description` is not set for the widget, the label is not displayed:"
92 85 ]
93 86 },
94 87 {
95 88 "cell_type": "code",
96 89 "collapsed": false,
97 90 "input": [
98 91 "display(widgets.TextWidget(description=\"a:\"))\n",
99 92 "display(widgets.TextWidget(description=\"aa:\"))\n",
100 93 "display(widgets.TextWidget(description=\"aaa:\"))\n",
101 94 "display(widgets.TextWidget())"
102 95 ],
103 96 "language": "python",
104 97 "metadata": {},
105 98 "outputs": [],
106 99 "prompt_number": 4
107 100 },
108 101 {
109 102 "cell_type": "heading",
110 103 "level": 1,
111 104 "metadata": {},
112 105 "source": [
113 106 "Custom Alignment"
114 107 ]
115 108 },
116 109 {
117 110 "cell_type": "markdown",
118 111 "metadata": {},
119 112 "source": [
120 113 "`ContainerWidget`s allow for custom alignment of widgets.\n",
121 114 "The `hbox` and `vbox` CSS classes cause the `ContainerWidget` to horizontally or vertically align its children."
122 115 ]
123 116 },
124 117 {
125 118 "cell_type": "code",
126 119 "collapsed": false,
127 120 "input": [
128 121 "child_style = {\n",
129 122 " 'background': '#77CC77',\n",
130 123 " 'padding': '25px',\n",
131 124 " 'margin': '5px',\n",
132 125 " 'font-size': 'xx-large',\n",
133 126 " 'color': 'white',\n",
134 127 "}\n",
135 128 "\n",
136 129 "def make_container(title):\n",
137 130 " header = widgets.LatexWidget(value=title) \n",
138 131 " display(header)\n",
139 132 " header.set_css({\n",
140 133 " 'font-size': '30pt',\n",
141 134 " 'margin-top': '40pt',\n",
142 135 " 'margin-bottom': '20pt',\n",
143 136 " })\n",
144 137 " \n",
145 138 " container = widgets.ContainerWidget()\n",
146 139 " container.set_css({\n",
147 140 " 'background': '#999999',\n",
148 141 " 'width': '100%',\n",
149 142 " })\n",
150 143 " display(container)\n",
151 144 " return container\n",
152 145 "\n",
153 146 "def fill_container(container):\n",
154 147 " components = []\n",
155 148 " for i in range(3):\n",
156 149 " components.append(widgets.LatexWidget(value=\"ABC\"[i]))\n",
157 150 " components[i].set_css(child_style)\n",
158 151 " container.children = components\n",
159 152 " \n",
160 153 "container = make_container('VBox')\n",
161 154 "container.add_class('vbox')\n",
162 155 "fill_container(container)\n",
163 156 "\n",
164 157 "container = make_container('HBox')\n",
165 158 "container.remove_class('vbox')\n",
166 159 "container.add_class('hbox')\n",
167 160 "fill_container(container)\n"
168 161 ],
169 162 "language": "python",
170 163 "metadata": {},
171 164 "outputs": [],
172 165 "prompt_number": 5
173 166 },
174 167 {
175 168 "cell_type": "markdown",
176 169 "metadata": {},
177 170 "source": [
178 171 "The `start`, `center`, and `end` classes adjust the alignment of the widgets on the axis where they are being rendered.\n",
179 172 "Below is an example of the different alignments."
180 173 ]
181 174 },
182 175 {
183 176 "cell_type": "code",
184 177 "collapsed": false,
185 178 "input": [
186 179 "container = make_container('HBox Pack Start')\n",
187 180 "container.remove_class('vbox')\n",
188 181 "container.add_class('hbox')\n",
189 182 "container.add_class('start')\n",
190 183 "fill_container(container)\n",
191 184 " \n",
192 185 "container = make_container('HBox Pack Center')\n",
193 186 "container.remove_class('vbox')\n",
194 187 "container.add_class('hbox')\n",
195 188 "container.add_class('center')\n",
196 189 "fill_container(container)\n",
197 190 " \n",
198 191 "container = make_container('HBox Pack End')\n",
199 192 "container.remove_class('vbox')\n",
200 193 "container.add_class('hbox')\n",
201 194 "container.add_class('end')\n",
202 195 "fill_container(container)"
203 196 ],
204 197 "language": "python",
205 198 "metadata": {},
206 199 "outputs": [],
207 200 "prompt_number": 6
208 201 },
209 202 {
210 203 "cell_type": "markdown",
211 204 "metadata": {},
212 205 "source": [
213 206 "The `box-flex0`, `box-flex1`, and `box-flex2` DOM classes modify the container's flexibility. Changing a container flexibility affects how and if the container will occupy the remaining space. Applying `box-flex0` has the same result as not applying flex. Below is an example of different flex configurations. The number on the boxes correspond to the applied flex."
214 207 ]
215 208 },
216 209 {
217 210 "cell_type": "code",
218 211 "collapsed": false,
219 212 "input": [
220 213 "def fill_container(container, flexes):\n",
221 214 " components = []\n",
222 215 " for i in range(len(flexes)):\n",
223 216 " components.append(widgets.ContainerWidget())\n",
224 217 " components[i].set_css(child_style)\n",
225 218 " \n",
226 219 " label = widgets.LatexWidget(value=str(flexes[i]))\n",
227 220 " components[i].children = [label]\n",
228 221 " container.children = components\n",
229 222 " \n",
230 223 " for i in range(len(flexes)):\n",
231 224 " if flexes[i] == 0:\n",
232 225 " components[i].add_class('box-flex0')\n",
233 226 " elif flexes[i] == 1:\n",
234 227 " components[i].add_class('box-flex1')\n",
235 228 " elif flexes[i] == 2:\n",
236 229 " components[i].add_class('box-flex2')\n",
237 230 " \n",
238 231 "container = make_container('Different Flex Configurations')\n",
239 232 "container.remove_class('vbox')\n",
240 233 "container.add_class('hbox')\n",
241 234 "fill_container(container, [0, 0, 0])\n",
242 235 " \n",
243 236 "container = make_container('')\n",
244 237 "container.remove_class('vbox')\n",
245 238 "container.add_class('hbox')\n",
246 239 "fill_container(container, [0, 0, 1])\n",
247 240 " \n",
248 241 "container = make_container('')\n",
249 242 "container.remove_class('vbox')\n",
250 243 "container.add_class('hbox')\n",
251 244 "fill_container(container, [0, 1, 1])\n",
252 245 " \n",
253 246 "container = make_container('')\n",
254 247 "container.remove_class('vbox')\n",
255 248 "container.add_class('hbox')\n",
256 249 "fill_container(container, [0, 2, 2])\n",
257 250 " \n",
258 251 "container = make_container('')\n",
259 252 "container.remove_class('vbox')\n",
260 253 "container.add_class('hbox')\n",
261 254 "fill_container(container, [0, 1, 2])\n",
262 255 " \n",
263 256 "container = make_container('')\n",
264 257 "container.remove_class('vbox')\n",
265 258 "container.add_class('hbox')\n",
266 259 "fill_container(container, [1, 1, 2])"
267 260 ],
268 261 "language": "python",
269 262 "metadata": {},
270 263 "outputs": [],
271 264 "prompt_number": 7
272 265 },
273 266 {
274 267 "cell_type": "markdown",
275 268 "metadata": {},
276 269 "source": [
277 270 "The `align_start`, `align_center`, and `align_end` DOM classes adjust the alignment of the widgets on the axis perpindicular to the one that they are being rendered on. Below is an example of the different alignments."
278 271 ]
279 272 },
280 273 {
281 274 "cell_type": "code",
282 275 "collapsed": false,
283 276 "input": [
284 277 "def fill_container(container):\n",
285 278 " components = []\n",
286 279 " for i in range(3):\n",
287 280 " components.append(widgets.LatexWidget(parent=container, value=\"ABC\"[i]))\n",
288 281 " components[i].set_css(child_style)\n",
289 282 " components[i].set_css('height', str((i+1) * 50) + 'px')\n",
290 283 " container.children = components\n",
291 284 "\n",
292 285 "container = make_container('HBox Align Start')\n",
293 286 "container.remove_class('vbox')\n",
294 287 "container.add_class(\"hbox\")\n",
295 288 "container.add_class(\"align-start\")\n",
296 289 "fill_container(container)\n",
297 290 " \n",
298 291 "container = make_container('HBox Align Center')\n",
299 292 "container.remove_class('vbox')\n",
300 293 "container.add_class(\"hbox\")\n",
301 294 "container.add_class(\"align-center\")\n",
302 295 "fill_container(container)\n",
303 296 " \n",
304 297 "container = make_container('HBox Align End')\n",
305 298 "container.remove_class('vbox')\n",
306 299 "container.add_class(\"hbox\")\n",
307 300 "container.add_class(\"align-end\")\n",
308 301 "fill_container(container)"
309 302 ],
310 303 "language": "python",
311 304 "metadata": {},
312 305 "outputs": [],
313 306 "prompt_number": 8
314 307 },
315 308 {
316 309 "cell_type": "markdown",
317 310 "metadata": {},
318 311 "source": [
319 312 "By default the widget area is a `vbox`; however, there are many uses for a `hbox`. The example below uses a `hbox` to display a set of vertical sliders, like an equalizer."
320 313 ]
321 314 },
322 315 {
323 316 "cell_type": "code",
324 317 "collapsed": false,
325 318 "input": [
326 319 "container = widgets.ContainerWidget()\n",
327 320 "container.children=[widgets.FloatSliderWidget(orientation='vertical', description=str(i+1), value=50.0) \n",
328 321 " for i in range(15)]\n",
329 322 "display(container)\n",
330 323 "container.remove_class('vbox')\n",
331 324 "container.add_class('hbox')"
332 325 ],
333 326 "language": "python",
334 327 "metadata": {},
335 328 "outputs": [],
336 329 "prompt_number": 9
337 },
338 {
339 "cell_type": "markdown",
340 "metadata": {},
341 "source": [
342 "In [Part 6](Part 6 - Custom Widget.ipynb) of this [series](index.ipynb), you will learn how to create your own custom widget."
343 ]
344 330 }
345 331 ],
346 332 "metadata": {}
347 333 }
348 334 ]
349 335 } No newline at end of file
@@ -1,314 +1,306 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 "name": ""
9 "name": "",
10 "signature": "sha256:9a70b52f0b16861d1cd6a8342b233247958977a52bde8d3efd69d21131ce1926"
10 11 },
11 12 "nbformat": 3,
12 13 "nbformat_minor": 0,
13 14 "worksheets": [
14 15 {
15 16 "cells": [
16 17 {
17 18 "cell_type": "markdown",
18 19 "metadata": {},
19 20 "source": [
20 "[Index](index.ipynb)\n",
21 "\n",
22 21 "To use IPython widgets in the notebook, the widget namespace needs to be imported."
23 22 ]
24 23 },
25 24 {
26 25 "cell_type": "code",
27 26 "collapsed": false,
28 27 "input": [
29 28 "from IPython.html import widgets # Widget definitions\n",
30 29 "from IPython.display import display # Used to display widgets in the notebook"
31 30 ],
32 31 "language": "python",
33 32 "metadata": {},
34 33 "outputs": [],
35 34 "prompt_number": 1
36 35 },
37 36 {
38 37 "cell_type": "heading",
39 38 "level": 1,
40 39 "metadata": {},
41 40 "source": [
42 41 "Basic Widgets"
43 42 ]
44 43 },
45 44 {
46 45 "cell_type": "markdown",
47 46 "metadata": {},
48 47 "source": [
49 48 "IPython comes with basic widgets that represent common interactive controls. These widgets are\n",
50 49 "\n",
51 50 "- CheckboxWidget\n",
52 51 "- ToggleButtonWidget\n",
53 52 "- FloatSliderWidget\n",
54 53 "- BoundedFloatTextWidget\n",
55 54 "- FloatProgressWidget\n",
56 55 "- FloatTextWidget\n",
57 56 "- ImageWidget\n",
58 57 "- IntSliderWidget\n",
59 58 "- BoundedIntTextWidget\n",
60 59 "- IntProgressWidget\n",
61 60 "- IntTextWidget\n",
62 61 "- ToggleButtonsWidget\n",
63 62 "- RadioButtonsWidget\n",
64 63 "- DropdownWidget\n",
65 64 "- SelectWidget\n",
66 65 "- HTMLWidget\n",
67 66 "- LatexWidget\n",
68 67 "- TextareaWidget\n",
69 68 "- TextWidget\n",
70 69 "- ButtonWidget\n",
71 70 "\n",
72 71 "A few special widgets are also included, that can be used to capture events and change how other widgets are displayed. These widgets are\n",
73 72 "\n",
74 73 "- ContainerWidget\n",
75 74 "- PopupWidget\n",
76 75 "- AccordionWidget\n",
77 76 "- TabWidget\n",
78 77 "\n",
79 78 "To see the complete list of widgets, one can execute the following"
80 79 ]
81 80 },
82 81 {
83 82 "cell_type": "code",
84 83 "collapsed": false,
85 84 "input": [
86 85 "[widget for widget in dir(widgets) if widget.endswith('Widget')]"
87 86 ],
88 87 "language": "python",
89 88 "metadata": {},
90 89 "outputs": [
91 90 {
92 91 "metadata": {},
93 92 "output_type": "pyout",
94 93 "prompt_number": 2,
95 94 "text": [
96 95 "['AccordionWidget',\n",
97 96 " 'BoundedFloatTextWidget',\n",
98 97 " 'BoundedIntTextWidget',\n",
99 98 " 'ButtonWidget',\n",
100 99 " 'CheckboxWidget',\n",
101 100 " 'ContainerWidget',\n",
102 101 " 'DOMWidget',\n",
103 102 " 'DropdownWidget',\n",
104 103 " 'FloatProgressWidget',\n",
105 104 " 'FloatSliderWidget',\n",
106 105 " 'FloatTextWidget',\n",
107 106 " 'HTMLWidget',\n",
108 107 " 'ImageWidget',\n",
109 108 " 'IntProgressWidget',\n",
110 109 " 'IntSliderWidget',\n",
111 110 " 'IntTextWidget',\n",
112 111 " 'LatexWidget',\n",
113 112 " 'PopupWidget',\n",
114 113 " 'RadioButtonsWidget',\n",
115 114 " 'SelectWidget',\n",
116 115 " 'TabWidget',\n",
117 116 " 'TextWidget',\n",
118 117 " 'TextareaWidget',\n",
119 118 " 'ToggleButtonWidget',\n",
120 119 " 'ToggleButtonsWidget',\n",
121 120 " 'Widget']"
122 121 ]
123 122 }
124 123 ],
125 124 "prompt_number": 2
126 125 },
127 126 {
128 127 "cell_type": "markdown",
129 128 "metadata": {},
130 129 "source": [
131 130 "The basic widgets all have sensible default values. Create a *FloatSliderWidget* without displaying it:"
132 131 ]
133 132 },
134 133 {
135 134 "cell_type": "code",
136 135 "collapsed": false,
137 136 "input": [
138 137 "mywidget = widgets.FloatSliderWidget()"
139 138 ],
140 139 "language": "python",
141 140 "metadata": {},
142 141 "outputs": [],
143 142 "prompt_number": 3
144 143 },
145 144 {
146 145 "cell_type": "markdown",
147 146 "metadata": {},
148 147 "source": [
149 148 "Constructing a widget does not display it on the page. To display a widget, the widget must be passed to the IPython `display(object)` method or must be returned as the last item in the cell. `mywidget` is displayed by"
150 149 ]
151 150 },
152 151 {
153 152 "cell_type": "code",
154 153 "collapsed": false,
155 154 "input": [
156 155 "display(mywidget)"
157 156 ],
158 157 "language": "python",
159 158 "metadata": {},
160 159 "outputs": [],
161 160 "prompt_number": 4
162 161 },
163 162 {
164 163 "cell_type": "markdown",
165 164 "metadata": {},
166 165 "source": [
167 166 "or"
168 167 ]
169 168 },
170 169 {
171 170 "cell_type": "code",
172 171 "collapsed": false,
173 172 "input": [
174 173 "mywidget"
175 174 ],
176 175 "language": "python",
177 176 "metadata": {},
178 177 "outputs": [],
179 178 "prompt_number": 5
180 179 },
181 180 {
182 181 "cell_type": "markdown",
183 182 "metadata": {},
184 183 "source": [
185 184 "It's important to realize that widgets are not the same as output, even though they are displayed with `display`. Widgets are drawn in a special widget area. That area is marked with a close button which allows you to collapse the widgets. Widgets cannot be interleaved with output. Doing so would break the ability to make simple animations using `clear_output`.\n",
186 185 "\n",
187 186 "Widgets are manipulated via special instance attributes (traitlets). The names of these traitlets are listed in the widget's `keys` attribute (as seen below). A few of these attributes are common to most widgets. The basic attributes are `value`, `description`, `visible`, and `disabled`. `_css` and `_view_name` are private attributes that exist in all widgets and should not be modified."
188 187 ]
189 188 },
190 189 {
191 190 "cell_type": "code",
192 191 "collapsed": false,
193 192 "input": [
194 193 "mywidget.keys"
195 194 ],
196 195 "language": "python",
197 196 "metadata": {},
198 197 "outputs": [
199 198 {
200 199 "metadata": {},
201 200 "output_type": "pyout",
202 201 "prompt_number": 6,
203 202 "text": [
204 203 "['_view_name',\n",
205 204 " 'orientation',\n",
206 205 " 'min',\n",
207 206 " 'max',\n",
208 207 " '_css',\n",
209 208 " 'value',\n",
210 209 " 'disabled',\n",
211 210 " 'visible',\n",
212 211 " 'step',\n",
213 212 " 'description']"
214 213 ]
215 214 }
216 215 ],
217 216 "prompt_number": 6
218 217 },
219 218 {
220 219 "cell_type": "markdown",
221 220 "metadata": {},
222 221 "source": [
223 222 "Changing a widget's attribute will automatically update that widget everywhere it is displayed in the notebook. Here, the `value` attribute of `mywidget` is set. The slider shown above updates automatically with the new value. Syncing also works in the other direction - changing the value of the displayed widget will update the property's value."
224 223 ]
225 224 },
226 225 {
227 226 "cell_type": "code",
228 227 "collapsed": false,
229 228 "input": [
230 229 "mywidget.value = 25.0"
231 230 ],
232 231 "language": "python",
233 232 "metadata": {},
234 233 "outputs": [],
235 234 "prompt_number": 7
236 235 },
237 236 {
238 237 "cell_type": "markdown",
239 238 "metadata": {},
240 239 "source": [
241 240 "After changing the widget's value in the notebook by hand to 0.0 (sliding the bar to the far left)."
242 241 ]
243 242 },
244 243 {
245 244 "cell_type": "code",
246 245 "collapsed": false,
247 246 "input": [
248 247 "mywidget.value"
249 248 ],
250 249 "language": "python",
251 250 "metadata": {},
252 251 "outputs": [
253 252 {
254 253 "metadata": {},
255 254 "output_type": "pyout",
256 255 "prompt_number": 8,
257 256 "text": [
258 257 "25.0"
259 258 ]
260 259 }
261 260 ],
262 261 "prompt_number": 8
263 262 },
264 263 {
265 264 "cell_type": "markdown",
266 265 "metadata": {},
267 266 "source": [
268 267 "Widget values can also be set with kwargs during the construction of the widget (as seen below)."
269 268 ]
270 269 },
271 270 {
272 271 "cell_type": "code",
273 272 "collapsed": false,
274 273 "input": [
275 274 "mysecondwidget = widgets.RadioButtonsWidget(values=[\"Item A\", \"Item B\", \"Item C\"], value=\"Item A\")\n",
276 275 "display(mysecondwidget)"
277 276 ],
278 277 "language": "python",
279 278 "metadata": {},
280 279 "outputs": [],
281 280 "prompt_number": 9
282 281 },
283 282 {
284 283 "cell_type": "code",
285 284 "collapsed": false,
286 285 "input": [
287 286 "mysecondwidget.value"
288 287 ],
289 288 "language": "python",
290 289 "metadata": {},
291 290 "outputs": [
292 291 {
293 292 "metadata": {},
294 293 "output_type": "pyout",
295 294 "prompt_number": 10,
296 295 "text": [
297 296 "'Item A'"
298 297 ]
299 298 }
300 299 ],
301 300 "prompt_number": 10
302 },
303 {
304 "cell_type": "markdown",
305 "metadata": {},
306 "source": [
307 "In [Part 2](Part 2 - Events.ipynb) of this [series](index.ipynb), you will learn about widget events."
308 ]
309 301 }
310 302 ],
311 303 "metadata": {}
312 304 }
313 305 ]
314 306 } No newline at end of file
@@ -1,1058 +1,1049 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 9 "name": ""
10 10 },
11 11 "nbformat": 3,
12 12 "nbformat_minor": 0,
13 13 "worksheets": [
14 14 {
15 15 "cells": [
16 16 {
17 17 "cell_type": "markdown",
18 18 "metadata": {},
19 19 "source": [
20 "[< Back to Part 5](Part 5 - Alignment.ipynb) or [Index](index.ipynb)\n",
21 "\n",
22 20 "Before reading, make sure to review\n",
23 21 "\n",
24 22 "- [MVC prgramming](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)\n",
25 23 "- [Backbone.js](https://www.codeschool.com/courses/anatomy-of-backbonejs)\n",
26 24 "- [The widget IPEP](https://github.com/ipython/ipython/wiki/IPEP-23%3A-Backbone.js-Widgets)\n",
27 25 "- [The original widget PR discussion](https://github.com/ipython/ipython/pull/4374)"
28 26 ]
29 27 },
30 28 {
31 29 "cell_type": "code",
32 30 "collapsed": false,
33 31 "input": [
34 32 "from __future__ import print_function # For py 2.7 compat\n",
35 33 "\n",
36 34 "from IPython.html import widgets # Widget definitions\n",
37 35 "from IPython.display import display # Used to display widgets in the notebook\n",
38 36 "from IPython.utils.traitlets import Unicode # Used to declare attributes of our widget"
39 37 ],
40 38 "language": "python",
41 39 "metadata": {},
42 40 "outputs": [],
43 41 "prompt_number": 1
44 42 },
45 43 {
46 44 "cell_type": "heading",
47 45 "level": 1,
48 46 "metadata": {},
49 47 "source": [
50 48 "Abstract"
51 49 ]
52 50 },
53 51 {
54 52 "cell_type": "markdown",
55 53 "metadata": {},
56 54 "source": [
57 55 "This notebook implements a custom date picker widget,\n",
58 56 "in order to demonstrate the widget creation process.\n",
59 57 "\n",
60 58 "To create a custom widget, both Python and JavaScript code is required."
61 59 ]
62 60 },
63 61 {
64 62 "cell_type": "heading",
65 63 "level": 1,
66 64 "metadata": {},
67 65 "source": [
68 66 "Section 1 - Basics"
69 67 ]
70 68 },
71 69 {
72 70 "cell_type": "heading",
73 71 "level": 2,
74 72 "metadata": {},
75 73 "source": [
76 74 "Python"
77 75 ]
78 76 },
79 77 {
80 78 "cell_type": "markdown",
81 79 "metadata": {},
82 80 "source": [
83 81 "When starting a project like this, it is often easiest to make a simple base implementation,\n",
84 82 "to verify that the underlying framework is working as expected.\n",
85 83 "To start, we will create an empty widget and make sure that it can be rendered.\n",
86 84 "The first step is to define the widget in Python."
87 85 ]
88 86 },
89 87 {
90 88 "cell_type": "code",
91 89 "collapsed": false,
92 90 "input": [
93 91 "class DateWidget(widgets.DOMWidget):\n",
94 92 " _view_name = Unicode('DatePickerView', sync=True)"
95 93 ],
96 94 "language": "python",
97 95 "metadata": {},
98 96 "outputs": [],
99 97 "prompt_number": 2
100 98 },
101 99 {
102 100 "cell_type": "markdown",
103 101 "metadata": {},
104 102 "source": [
105 103 "Our widget inherits from `widgets.DOMWidget` since it is intended that it will be displayed in the notebook directly.\n",
106 104 "The `_view_name` trait is special; the widget framework will read the `_view_name` trait to determine what Backbone view the widget is associated with.\n",
107 105 "**Using `sync=True` is very important** because it tells the widget framework that that specific traitlet should be synced between the front- and back-ends."
108 106 ]
109 107 },
110 108 {
111 109 "cell_type": "heading",
112 110 "level": 2,
113 111 "metadata": {},
114 112 "source": [
115 113 "JavaScript"
116 114 ]
117 115 },
118 116 {
119 117 "cell_type": "markdown",
120 118 "metadata": {},
121 119 "source": [
122 120 "In the IPython notebook [require.js](http://requirejs.org/) is used to load JavaScript dependencies.\n",
123 121 "All IPython widget code depends on `widgets/js/widget.js`,\n",
124 122 "where the base widget model and base view are defined.\n",
125 123 "We use require.js to load this file:"
126 124 ]
127 125 },
128 126 {
129 127 "cell_type": "code",
130 128 "collapsed": false,
131 129 "input": [
132 130 "%%javascript\n",
133 131 "\n",
134 132 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
135 133 "\n",
136 134 "});"
137 135 ],
138 136 "language": "python",
139 137 "metadata": {},
140 138 "outputs": [
141 139 {
142 140 "javascript": [
143 141 "\n",
144 142 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
145 143 "\n",
146 144 "});"
147 145 ],
148 146 "metadata": {},
149 147 "output_type": "display_data",
150 148 "text": [
151 149 "<IPython.core.display.Javascript at 0x109491690>"
152 150 ]
153 151 }
154 152 ],
155 153 "prompt_number": 3
156 154 },
157 155 {
158 156 "cell_type": "markdown",
159 157 "metadata": {},
160 158 "source": [
161 159 "Now we need to define a view that can be used to represent the model.\n",
162 160 "To do this, the `IPython.DOMWidgetView` is extended.\n",
163 161 "**A render function must be defined**.\n",
164 162 "The render function is used to render a widget view instance to the DOM.\n",
165 163 "For now, the render function renders a div that contains the text *Hello World!*\n",
166 164 "Lastly, the view needs to be registered with the widget manager.\n",
167 165 "\n",
168 166 "**Final JavaScript code below:**"
169 167 ]
170 168 },
171 169 {
172 170 "cell_type": "code",
173 171 "collapsed": false,
174 172 "input": [
175 173 "%%javascript\n",
176 174 "\n",
177 175 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
178 176 " \n",
179 177 " // Define the DatePickerView\n",
180 178 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
181 179 " render: function(){ this.$el.text('Hello World!'); },\n",
182 180 " });\n",
183 181 " \n",
184 182 " // Register the DatePickerView with the widget manager.\n",
185 183 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
186 184 "});"
187 185 ],
188 186 "language": "python",
189 187 "metadata": {},
190 188 "outputs": [
191 189 {
192 190 "javascript": [
193 191 "\n",
194 192 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
195 193 " \n",
196 194 " // Define the DatePickerView\n",
197 195 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
198 196 " render: function(){ this.$el.text('Hello World!'); },\n",
199 197 " });\n",
200 198 " \n",
201 199 " // Register the DatePickerView with the widget manager.\n",
202 200 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
203 201 "});"
204 202 ],
205 203 "metadata": {},
206 204 "output_type": "display_data",
207 205 "text": [
208 206 "<IPython.core.display.Javascript at 0x1094917d0>"
209 207 ]
210 208 }
211 209 ],
212 210 "prompt_number": 4
213 211 },
214 212 {
215 213 "cell_type": "heading",
216 214 "level": 2,
217 215 "metadata": {},
218 216 "source": [
219 217 "Test"
220 218 ]
221 219 },
222 220 {
223 221 "cell_type": "markdown",
224 222 "metadata": {},
225 223 "source": [
226 224 "To test what we have so far, create the widget, just like you would the builtin widgets:"
227 225 ]
228 226 },
229 227 {
230 228 "cell_type": "code",
231 229 "collapsed": false,
232 230 "input": [
233 231 "DateWidget()"
234 232 ],
235 233 "language": "python",
236 234 "metadata": {},
237 235 "outputs": [],
238 236 "prompt_number": 5
239 237 },
240 238 {
241 239 "cell_type": "heading",
242 240 "level": 1,
243 241 "metadata": {},
244 242 "source": [
245 243 "Section 2 - Something useful"
246 244 ]
247 245 },
248 246 {
249 247 "cell_type": "heading",
250 248 "level": 2,
251 249 "metadata": {},
252 250 "source": [
253 251 "Python"
254 252 ]
255 253 },
256 254 {
257 255 "cell_type": "markdown",
258 256 "metadata": {},
259 257 "source": [
260 258 "In the last section we created a simple widget that displayed *Hello World!*\n",
261 259 "To make an actual date widget, we need to add a property that will be synced between the Python model and the JavaScript model.\n",
262 260 "The new attribute must be a traitlet, so the widget machinery can handle it.\n",
263 261 "The traitlet must be constructed with a `sync=True` keyword argument, to tell the widget machinery knows to synchronize it with the front-end.\n",
264 262 "Adding this to the code from the last section:"
265 263 ]
266 264 },
267 265 {
268 266 "cell_type": "code",
269 267 "collapsed": false,
270 268 "input": [
271 269 "class DateWidget(widgets.DOMWidget):\n",
272 270 " _view_name = Unicode('DatePickerView', sync=True)\n",
273 271 " value = Unicode(sync=True)"
274 272 ],
275 273 "language": "python",
276 274 "metadata": {},
277 275 "outputs": [],
278 276 "prompt_number": 6
279 277 },
280 278 {
281 279 "cell_type": "heading",
282 280 "level": 2,
283 281 "metadata": {},
284 282 "source": [
285 283 "JavaScript"
286 284 ]
287 285 },
288 286 {
289 287 "cell_type": "markdown",
290 288 "metadata": {},
291 289 "source": [
292 290 "In the JavaScript, there is no need to define counterparts to the traitlets.\n",
293 291 "When the JavaScript model is created for the first time,\n",
294 292 "it copies all of the traitlet `sync=True` attributes from the Python model.\n",
295 293 "We need to replace *Hello World!* with an actual HTML date picker widget."
296 294 ]
297 295 },
298 296 {
299 297 "cell_type": "code",
300 298 "collapsed": false,
301 299 "input": [
302 300 "%%javascript\n",
303 301 "\n",
304 302 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
305 303 " \n",
306 304 " // Define the DatePickerView\n",
307 305 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
308 306 " render: function(){\n",
309 307 " \n",
310 308 " // Create the date picker control.\n",
311 309 " this.$date = $('<input />')\n",
312 310 " .attr('type', 'date')\n",
313 311 " .appendTo(this.$el);\n",
314 312 " },\n",
315 313 " });\n",
316 314 " \n",
317 315 " // Register the DatePickerView with the widget manager.\n",
318 316 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
319 317 "});"
320 318 ],
321 319 "language": "python",
322 320 "metadata": {},
323 321 "outputs": [
324 322 {
325 323 "javascript": [
326 324 "\n",
327 325 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
328 326 " \n",
329 327 " // Define the DatePickerView\n",
330 328 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
331 329 " render: function(){\n",
332 330 " \n",
333 331 " // Create the date picker control.\n",
334 332 " this.$date = $('<input />')\n",
335 333 " .attr('type', 'date')\n",
336 334 " .appendTo(this.$el);\n",
337 335 " },\n",
338 336 " });\n",
339 337 " \n",
340 338 " // Register the DatePickerView with the widget manager.\n",
341 339 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
342 340 "});"
343 341 ],
344 342 "metadata": {},
345 343 "output_type": "display_data",
346 344 "text": [
347 345 "<IPython.core.display.Javascript at 0x109491750>"
348 346 ]
349 347 }
350 348 ],
351 349 "prompt_number": 7
352 350 },
353 351 {
354 352 "cell_type": "markdown",
355 353 "metadata": {},
356 354 "source": [
357 355 "In order to get the HTML date picker to update itself with the value set in the back-end, we need to implement an `update()` method."
358 356 ]
359 357 },
360 358 {
361 359 "cell_type": "code",
362 360 "collapsed": false,
363 361 "input": [
364 362 "%%javascript\n",
365 363 "\n",
366 364 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
367 365 " \n",
368 366 " // Define the DatePickerView\n",
369 367 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
370 368 " render: function(){\n",
371 369 " \n",
372 370 " // Create the date picker control.\n",
373 371 " this.$date = $('<input />')\n",
374 372 " .attr('type', 'date')\n",
375 373 " .appendTo(this.$el);\n",
376 374 " },\n",
377 375 " \n",
378 376 " update: function() {\n",
379 377 " \n",
380 378 " // Set the value of the date control and then call base.\n",
381 379 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
382 380 " return DatePickerView.__super__.update.apply(this);\n",
383 381 " },\n",
384 382 " });\n",
385 383 " \n",
386 384 " // Register the DatePickerView with the widget manager.\n",
387 385 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
388 386 "});"
389 387 ],
390 388 "language": "python",
391 389 "metadata": {},
392 390 "outputs": [
393 391 {
394 392 "javascript": [
395 393 "\n",
396 394 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
397 395 " \n",
398 396 " // Define the DatePickerView\n",
399 397 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
400 398 " render: function(){\n",
401 399 " \n",
402 400 " // Create the date picker control.\n",
403 401 " this.$date = $('<input />')\n",
404 402 " .attr('type', 'date')\n",
405 403 " .appendTo(this.$el);\n",
406 404 " },\n",
407 405 " \n",
408 406 " update: function() {\n",
409 407 " \n",
410 408 " // Set the value of the date control and then call base.\n",
411 409 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
412 410 " return DatePickerView.__super__.update.apply(this);\n",
413 411 " },\n",
414 412 " });\n",
415 413 " \n",
416 414 " // Register the DatePickerView with the widget manager.\n",
417 415 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
418 416 "});"
419 417 ],
420 418 "metadata": {},
421 419 "output_type": "display_data",
422 420 "text": [
423 421 "<IPython.core.display.Javascript at 0x109491750>"
424 422 ]
425 423 }
426 424 ],
427 425 "prompt_number": 8
428 426 },
429 427 {
430 428 "cell_type": "markdown",
431 429 "metadata": {},
432 430 "source": [
433 431 "To get the changed value from the frontend to publish itself to the backend,\n",
434 432 "we need to listen to the change event triggered by the HTM date control and set the value in the model.\n",
435 433 "After the date change event fires and the new value is set in the model,\n",
436 434 "it is very important that we call `this.touch()` to let the widget machinery know which view changed the model.\n",
437 435 "This is important because the widget machinery needs to know which cell to route the message callbacks to.\n",
438 436 "\n",
439 437 "**Final JavaScript code below:**"
440 438 ]
441 439 },
442 440 {
443 441 "cell_type": "code",
444 442 "collapsed": false,
445 443 "input": [
446 444 "%%javascript\n",
447 445 "\n",
448 446 "\n",
449 447 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
450 448 " \n",
451 449 " // Define the DatePickerView\n",
452 450 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
453 451 " render: function(){\n",
454 452 " \n",
455 453 " // Create the date picker control.\n",
456 454 " this.$date = $('<input />')\n",
457 455 " .attr('type', 'date')\n",
458 456 " .appendTo(this.$el);\n",
459 457 " },\n",
460 458 " \n",
461 459 " update: function() {\n",
462 460 " \n",
463 461 " // Set the value of the date control and then call base.\n",
464 462 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
465 463 " return DatePickerView.__super__.update.apply(this);\n",
466 464 " },\n",
467 465 " \n",
468 466 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
469 467 " events: {\"change\": \"handle_date_change\"},\n",
470 468 " \n",
471 469 " // Callback for when the date is changed.\n",
472 470 " handle_date_change: function(event) {\n",
473 471 " this.model.set('value', this.$date.val());\n",
474 472 " this.touch();\n",
475 473 " },\n",
476 474 " });\n",
477 475 " \n",
478 476 " // Register the DatePickerView with the widget manager.\n",
479 477 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
480 478 "});"
481 479 ],
482 480 "language": "python",
483 481 "metadata": {},
484 482 "outputs": [
485 483 {
486 484 "javascript": [
487 485 "\n",
488 486 "\n",
489 487 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
490 488 " \n",
491 489 " // Define the DatePickerView\n",
492 490 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
493 491 " render: function(){\n",
494 492 " \n",
495 493 " // Create the date picker control.\n",
496 494 " this.$date = $('<input />')\n",
497 495 " .attr('type', 'date')\n",
498 496 " .appendTo(this.$el);\n",
499 497 " },\n",
500 498 " \n",
501 499 " update: function() {\n",
502 500 " \n",
503 501 " // Set the value of the date control and then call base.\n",
504 502 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
505 503 " return DatePickerView.__super__.update.apply(this);\n",
506 504 " },\n",
507 505 " \n",
508 506 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
509 507 " events: {\"change\": \"handle_date_change\"},\n",
510 508 " \n",
511 509 " // Callback for when the date is changed.\n",
512 510 " handle_date_change: function(event) {\n",
513 511 " this.model.set('value', this.$date.val());\n",
514 512 " this.touch();\n",
515 513 " },\n",
516 514 " });\n",
517 515 " \n",
518 516 " // Register the DatePickerView with the widget manager.\n",
519 517 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
520 518 "});"
521 519 ],
522 520 "metadata": {},
523 521 "output_type": "display_data",
524 522 "text": [
525 523 "<IPython.core.display.Javascript at 0x109491b10>"
526 524 ]
527 525 }
528 526 ],
529 527 "prompt_number": 9
530 528 },
531 529 {
532 530 "cell_type": "heading",
533 531 "level": 2,
534 532 "metadata": {},
535 533 "source": [
536 534 "Test"
537 535 ]
538 536 },
539 537 {
540 538 "cell_type": "markdown",
541 539 "metadata": {},
542 540 "source": [
543 541 "To test, create the widget the same way that the other widgets are created."
544 542 ]
545 543 },
546 544 {
547 545 "cell_type": "code",
548 546 "collapsed": false,
549 547 "input": [
550 548 "my_widget = DateWidget()\n",
551 549 "display(my_widget)"
552 550 ],
553 551 "language": "python",
554 552 "metadata": {},
555 553 "outputs": [],
556 554 "prompt_number": 10
557 555 },
558 556 {
559 557 "cell_type": "markdown",
560 558 "metadata": {},
561 559 "source": [
562 560 "Display the widget again to make sure that both views remain in sync."
563 561 ]
564 562 },
565 563 {
566 564 "cell_type": "code",
567 565 "collapsed": false,
568 566 "input": [
569 567 "my_widget"
570 568 ],
571 569 "language": "python",
572 570 "metadata": {},
573 571 "outputs": [],
574 572 "prompt_number": 11
575 573 },
576 574 {
577 575 "cell_type": "markdown",
578 576 "metadata": {},
579 577 "source": [
580 578 "Read the date from Python"
581 579 ]
582 580 },
583 581 {
584 582 "cell_type": "code",
585 583 "collapsed": false,
586 584 "input": [
587 585 "my_widget.value"
588 586 ],
589 587 "language": "python",
590 588 "metadata": {},
591 589 "outputs": [
592 590 {
593 591 "metadata": {},
594 592 "output_type": "pyout",
595 593 "prompt_number": 12,
596 594 "text": [
597 595 "u''"
598 596 ]
599 597 }
600 598 ],
601 599 "prompt_number": 12
602 600 },
603 601 {
604 602 "cell_type": "markdown",
605 603 "metadata": {},
606 604 "source": [
607 605 "Set the date from Python"
608 606 ]
609 607 },
610 608 {
611 609 "cell_type": "code",
612 610 "collapsed": false,
613 611 "input": [
614 612 "my_widget.value = \"1998-12-01\" # December 1st, 1998"
615 613 ],
616 614 "language": "python",
617 615 "metadata": {},
618 616 "outputs": [],
619 617 "prompt_number": 13
620 618 },
621 619 {
622 620 "cell_type": "heading",
623 621 "level": 1,
624 622 "metadata": {},
625 623 "source": [
626 624 "Section 3 - Extra credit"
627 625 ]
628 626 },
629 627 {
630 628 "cell_type": "markdown",
631 629 "metadata": {},
632 630 "source": [
633 631 "The 3rd party `dateutil` library is required to continue. https://pypi.python.org/pypi/python-dateutil"
634 632 ]
635 633 },
636 634 {
637 635 "cell_type": "code",
638 636 "collapsed": false,
639 637 "input": [
640 638 "# Import the dateutil library to parse date strings.\n",
641 639 "from dateutil import parser"
642 640 ],
643 641 "language": "python",
644 642 "metadata": {},
645 643 "outputs": [],
646 644 "prompt_number": 14
647 645 },
648 646 {
649 647 "cell_type": "markdown",
650 648 "metadata": {},
651 649 "source": [
652 650 "In the last section we created a fully working date picker widget.\n",
653 651 "Now we will add custom validation and support for labels.\n",
654 652 "So far, only the ISO date format \"YYYY-MM-DD\" is supported.\n",
655 653 "Now, we will add support for all of the date formats recognized by the Python dateutil library."
656 654 ]
657 655 },
658 656 {
659 657 "cell_type": "heading",
660 658 "level": 2,
661 659 "metadata": {},
662 660 "source": [
663 661 "Python"
664 662 ]
665 663 },
666 664 {
667 665 "cell_type": "markdown",
668 666 "metadata": {},
669 667 "source": [
670 668 "The standard property name used for widget labels is `description`.\n",
671 669 "In the code block below, `description` has been added to the Python widget."
672 670 ]
673 671 },
674 672 {
675 673 "cell_type": "code",
676 674 "collapsed": false,
677 675 "input": [
678 676 "class DateWidget(widgets.DOMWidget):\n",
679 677 " _view_name = Unicode('DatePickerView', sync=True)\n",
680 678 " value = Unicode(sync=True)\n",
681 679 " description = Unicode(sync=True)"
682 680 ],
683 681 "language": "python",
684 682 "metadata": {},
685 683 "outputs": [],
686 684 "prompt_number": 15
687 685 },
688 686 {
689 687 "cell_type": "markdown",
690 688 "metadata": {},
691 689 "source": [
692 690 "The traitlet machinery searches the class that the trait is defined in for methods with \"`_changed`\" suffixed onto their names. Any method with the format \"`_X_changed`\" will be called when \"`X`\" is modified.\n",
693 691 "We can take advantage of this to perform validation and parsing of different date string formats.\n",
694 692 "Below, a method that listens to value has been added to the DateWidget."
695 693 ]
696 694 },
697 695 {
698 696 "cell_type": "code",
699 697 "collapsed": false,
700 698 "input": [
701 699 "class DateWidget(widgets.DOMWidget):\n",
702 700 " _view_name = Unicode('DatePickerView', sync=True)\n",
703 701 " value = Unicode(sync=True)\n",
704 702 " description = Unicode(sync=True)\n",
705 703 "\n",
706 704 " # This function automatically gets called by the traitlet machinery when\n",
707 705 " # value is modified because of this function's name.\n",
708 706 " def _value_changed(self, name, old_value, new_value):\n",
709 707 " pass"
710 708 ],
711 709 "language": "python",
712 710 "metadata": {},
713 711 "outputs": [],
714 712 "prompt_number": 16
715 713 },
716 714 {
717 715 "cell_type": "markdown",
718 716 "metadata": {},
719 717 "source": [
720 718 "Now the function parses the date string,\n",
721 719 "and only sets the value in the correct format."
722 720 ]
723 721 },
724 722 {
725 723 "cell_type": "code",
726 724 "collapsed": false,
727 725 "input": [
728 726 "class DateWidget(widgets.DOMWidget):\n",
729 727 " _view_name = Unicode('DatePickerView', sync=True)\n",
730 728 " value = Unicode(sync=True)\n",
731 729 " description = Unicode(sync=True)\n",
732 730 " \n",
733 731 " # This function automatically gets called by the traitlet machinery when\n",
734 732 " # value is modified because of this function's name.\n",
735 733 " def _value_changed(self, name, old_value, new_value):\n",
736 734 " \n",
737 735 " # Parse the date time value.\n",
738 736 " try:\n",
739 737 " parsed_date = parser.parse(new_value)\n",
740 738 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
741 739 " except:\n",
742 740 " parsed_date_string = ''\n",
743 741 " \n",
744 742 " # Set the parsed date string if the current date string is different.\n",
745 743 " if self.value != parsed_date_string:\n",
746 744 " self.value = parsed_date_string"
747 745 ],
748 746 "language": "python",
749 747 "metadata": {},
750 748 "outputs": [],
751 749 "prompt_number": 17
752 750 },
753 751 {
754 752 "cell_type": "markdown",
755 753 "metadata": {},
756 754 "source": [
757 755 "Finally, a `CallbackDispatcher` is added so the user can perform custom validation.\n",
758 756 "If any one of the callbacks registered with the dispatcher returns False,\n",
759 757 "the new date is not set.\n",
760 758 "\n",
761 759 "**Final Python code below:**"
762 760 ]
763 761 },
764 762 {
765 763 "cell_type": "code",
766 764 "collapsed": false,
767 765 "input": [
768 766 "class DateWidget(widgets.DOMWidget):\n",
769 767 " _view_name = Unicode('DatePickerView', sync=True)\n",
770 768 " value = Unicode(sync=True)\n",
771 769 " description = Unicode(sync=True)\n",
772 770 " \n",
773 771 " def __init__(self, **kwargs):\n",
774 772 " super(DateWidget, self).__init__(**kwargs)\n",
775 773 " \n",
776 774 " self.validate = widgets.CallbackDispatcher()\n",
777 775 " \n",
778 776 " # This function automatically gets called by the traitlet machinery when\n",
779 777 " # value is modified because of this function's name.\n",
780 778 " def _value_changed(self, name, old_value, new_value):\n",
781 779 " \n",
782 780 " # Parse the date time value.\n",
783 781 " try:\n",
784 782 " parsed_date = parser.parse(new_value)\n",
785 783 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
786 784 " except:\n",
787 785 " parsed_date_string = ''\n",
788 786 " \n",
789 787 " # Set the parsed date string if the current date string is different.\n",
790 788 " if old_value != new_value:\n",
791 789 " valid = self.validate(parsed_date)\n",
792 790 " if valid in (None, True):\n",
793 791 " self.value = parsed_date_string\n",
794 792 " else:\n",
795 793 " self.value = old_value\n",
796 794 " self.send_state() # The traitlet event won't fire since the value isn't changing.\n",
797 795 " # We need to force the back-end to send the front-end the state\n",
798 796 " # to make sure that the date control date doesn't change."
799 797 ],
800 798 "language": "python",
801 799 "metadata": {},
802 800 "outputs": [],
803 801 "prompt_number": 18
804 802 },
805 803 {
806 804 "cell_type": "heading",
807 805 "level": 2,
808 806 "metadata": {},
809 807 "source": [
810 808 "JavaScript"
811 809 ]
812 810 },
813 811 {
814 812 "cell_type": "markdown",
815 813 "metadata": {},
816 814 "source": [
817 815 "Using the Javascript code from the last section,\n",
818 816 "we add a label to the date time object.\n",
819 817 "The label is a div with the `widget-hlabel` class applied to it.\n",
820 818 "`widget-hlabel` is a class provided by the widget framework that applies special styling to a div to make it look like the rest of the horizontal labels used with the built-in widgets.\n",
821 819 "Similar to the `widget-hlabel` class is the `widget-hbox-single` class.\n",
822 820 "The `widget-hbox-single` class applies special styling to widget containers that store a single line horizontal widget.\n",
823 821 "\n",
824 822 "We hide the label if the description value is blank."
825 823 ]
826 824 },
827 825 {
828 826 "cell_type": "code",
829 827 "collapsed": false,
830 828 "input": [
831 829 "%%javascript\n",
832 830 "\n",
833 831 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
834 832 " \n",
835 833 " // Define the DatePickerView\n",
836 834 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
837 835 " render: function(){\n",
838 836 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
839 837 " it fit with the other built in widgets.*/\n",
840 838 " // Create a label.\n",
841 839 " this.$label = $('<div />')\n",
842 840 " .addClass('widget-hlabel')\n",
843 841 " .appendTo(this.$el)\n",
844 842 " .hide(); // Hide the label by default.\n",
845 843 " \n",
846 844 " // Create the date picker control.\n",
847 845 " this.$date = $('<input />')\n",
848 846 " .attr('type', 'date')\n",
849 847 " .appendTo(this.$el);\n",
850 848 " },\n",
851 849 " \n",
852 850 " update: function() {\n",
853 851 " \n",
854 852 " // Set the value of the date control and then call base.\n",
855 853 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
856 854 " \n",
857 855 " // Hide or show the label depending on the existance of a description.\n",
858 856 " var description = this.model.get('description');\n",
859 857 " if (description == undefined || description == '') {\n",
860 858 " this.$label.hide();\n",
861 859 " } else {\n",
862 860 " this.$label.show();\n",
863 861 " this.$label.text(description);\n",
864 862 " }\n",
865 863 " \n",
866 864 " return DatePickerView.__super__.update.apply(this);\n",
867 865 " },\n",
868 866 " \n",
869 867 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
870 868 " events: {\"change\": \"handle_date_change\"},\n",
871 869 " \n",
872 870 " // Callback for when the date is changed.\n",
873 871 " handle_date_change: function(event) {\n",
874 872 " this.model.set('value', this.$date.val());\n",
875 873 " this.touch();\n",
876 874 " },\n",
877 875 " });\n",
878 876 " \n",
879 877 " // Register the DatePickerView with the widget manager.\n",
880 878 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
881 879 "});"
882 880 ],
883 881 "language": "python",
884 882 "metadata": {},
885 883 "outputs": [
886 884 {
887 885 "javascript": [
888 886 "\n",
889 887 "require([\"widgets/js/widget\"], function(WidgetManager){\n",
890 888 " \n",
891 889 " // Define the DatePickerView\n",
892 890 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
893 891 " render: function(){\n",
894 892 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
895 893 " it fit with the other built in widgets.*/\n",
896 894 " // Create a label.\n",
897 895 " this.$label = $('<div />')\n",
898 896 " .addClass('widget-hlabel')\n",
899 897 " .appendTo(this.$el)\n",
900 898 " .hide(); // Hide the label by default.\n",
901 899 " \n",
902 900 " // Create the date picker control.\n",
903 901 " this.$date = $('<input />')\n",
904 902 " .attr('type', 'date')\n",
905 903 " .appendTo(this.$el);\n",
906 904 " },\n",
907 905 " \n",
908 906 " update: function() {\n",
909 907 " \n",
910 908 " // Set the value of the date control and then call base.\n",
911 909 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
912 910 " \n",
913 911 " // Hide or show the label depending on the existance of a description.\n",
914 912 " var description = this.model.get('description');\n",
915 913 " if (description == undefined || description == '') {\n",
916 914 " this.$label.hide();\n",
917 915 " } else {\n",
918 916 " this.$label.show();\n",
919 917 " this.$label.text(description);\n",
920 918 " }\n",
921 919 " \n",
922 920 " return DatePickerView.__super__.update.apply(this);\n",
923 921 " },\n",
924 922 " \n",
925 923 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
926 924 " events: {\"change\": \"handle_date_change\"},\n",
927 925 " \n",
928 926 " // Callback for when the date is changed.\n",
929 927 " handle_date_change: function(event) {\n",
930 928 " this.model.set('value', this.$date.val());\n",
931 929 " this.touch();\n",
932 930 " },\n",
933 931 " });\n",
934 932 " \n",
935 933 " // Register the DatePickerView with the widget manager.\n",
936 934 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
937 935 "});"
938 936 ],
939 937 "metadata": {},
940 938 "output_type": "display_data",
941 939 "text": [
942 940 "<IPython.core.display.Javascript at 0x1094eef90>"
943 941 ]
944 942 }
945 943 ],
946 944 "prompt_number": 19
947 945 },
948 946 {
949 947 "cell_type": "heading",
950 948 "level": 2,
951 949 "metadata": {},
952 950 "source": [
953 951 "Test"
954 952 ]
955 953 },
956 954 {
957 955 "cell_type": "markdown",
958 956 "metadata": {},
959 957 "source": [
960 958 "To test the drawing of the label we create the widget like normal but supply the additional description property a value."
961 959 ]
962 960 },
963 961 {
964 962 "cell_type": "code",
965 963 "collapsed": false,
966 964 "input": [
967 965 "# Add some additional widgets for aesthetic purpose\n",
968 966 "display(widgets.TextWidget(description=\"First:\"))\n",
969 967 "display(widgets.TextWidget(description=\"Last:\"))\n",
970 968 "\n",
971 969 "my_widget = DateWidget()\n",
972 970 "display(my_widget)\n",
973 971 "my_widget.description=\"DOB:\""
974 972 ],
975 973 "language": "python",
976 974 "metadata": {},
977 975 "outputs": [],
978 976 "prompt_number": 20
979 977 },
980 978 {
981 979 "cell_type": "markdown",
982 980 "metadata": {},
983 981 "source": [
984 982 "Now we will try to create a widget that only accepts dates in the year 2014. We render the widget without a description to verify that it can still render without a label."
985 983 ]
986 984 },
987 985 {
988 986 "cell_type": "code",
989 987 "collapsed": false,
990 988 "input": [
991 989 "my_widget = DateWidget()\n",
992 990 "display(my_widget)\n",
993 991 "\n",
994 992 "def require_2014(date):\n",
995 993 " return not date is None and date.year == 2014\n",
996 994 "my_widget.validate.register_callback(require_2014)"
997 995 ],
998 996 "language": "python",
999 997 "metadata": {},
1000 998 "outputs": [],
1001 999 "prompt_number": 21
1002 1000 },
1003 1001 {
1004 1002 "cell_type": "code",
1005 1003 "collapsed": false,
1006 1004 "input": [
1007 1005 "# Try setting a valid date\n",
1008 1006 "my_widget.value = \"December 2, 2014\""
1009 1007 ],
1010 1008 "language": "python",
1011 1009 "metadata": {},
1012 1010 "outputs": [],
1013 1011 "prompt_number": 22
1014 1012 },
1015 1013 {
1016 1014 "cell_type": "code",
1017 1015 "collapsed": false,
1018 1016 "input": [
1019 1017 "# Try setting an invalid date\n",
1020 1018 "my_widget.value = \"June 12, 1999\""
1021 1019 ],
1022 1020 "language": "python",
1023 1021 "metadata": {},
1024 1022 "outputs": [],
1025 1023 "prompt_number": 23
1026 1024 },
1027 1025 {
1028 1026 "cell_type": "code",
1029 1027 "collapsed": false,
1030 1028 "input": [
1031 1029 "my_widget.value"
1032 1030 ],
1033 1031 "language": "python",
1034 1032 "metadata": {},
1035 1033 "outputs": [
1036 1034 {
1037 1035 "metadata": {},
1038 1036 "output_type": "pyout",
1039 1037 "prompt_number": 24,
1040 1038 "text": [
1041 1039 "u'2014-12-02'"
1042 1040 ]
1043 1041 }
1044 1042 ],
1045 1043 "prompt_number": 24
1046 },
1047 {
1048 "cell_type": "markdown",
1049 "metadata": {},
1050 "source": [
1051 "This concludes Part 6 of the [series](index.ipynb)."
1052 ]
1053 1044 }
1054 1045 ],
1055 1046 "metadata": {}
1056 1047 }
1057 1048 ]
1058 }
1049 } No newline at end of file
@@ -1,261 +1,248 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 "name": ""
9 "name": "",
10 "signature": "sha256:8cade57fabc6819dc950bc28502028554861fb1440d5d832922b95fd2b8bf25c"
10 11 },
11 12 "nbformat": 3,
12 13 "nbformat_minor": 0,
13 14 "worksheets": [
14 15 {
15 16 "cells": [
16 17 {
17 "cell_type": "markdown",
18 "metadata": {},
19 "source": [
20 "[< Back to Part 1](Part 1 - Basics.ipynb) or [Index](index.ipynb)"
21 ]
22 },
23 {
24 18 "cell_type": "code",
25 19 "collapsed": false,
26 20 "input": [
27 21 "from __future__ import print_function # 2.7 compatability\n",
28 22 "\n",
29 23 "from IPython.html import widgets # Widget definitions\n",
30 24 "from IPython.display import display # Used to display widgets in the notebook"
31 25 ],
32 26 "language": "python",
33 27 "metadata": {},
34 28 "outputs": [],
35 29 "prompt_number": 1
36 30 },
37 31 {
38 32 "cell_type": "heading",
39 33 "level": 1,
40 34 "metadata": {},
41 35 "source": [
42 36 "Traitlet Events"
43 37 ]
44 38 },
45 39 {
46 40 "cell_type": "markdown",
47 41 "metadata": {},
48 42 "source": [
49 43 "As mentioned in Part 1, the widget attributes are IPython traitlets. Traitlets are eventful. To handle changes, the `on_trait_change` method of the widget can be used to register a callback. The docstring for `on_trait_change` can be seen below. Both the `name` and `remove` properties are optional."
50 44 ]
51 45 },
52 46 {
53 47 "cell_type": "code",
54 48 "collapsed": false,
55 49 "input": [
56 50 "print(widgets.Widget.on_trait_change.__doc__)"
57 51 ],
58 52 "language": "python",
59 53 "metadata": {},
60 54 "outputs": [
61 55 {
62 56 "output_type": "stream",
63 57 "stream": "stdout",
64 58 "text": [
65 59 "Setup a handler to be called when a trait changes.\n",
66 60 "\n",
67 61 " This is used to setup dynamic notifications of trait changes.\n",
68 62 "\n",
69 63 " Static handlers can be created by creating methods on a HasTraits\n",
70 64 " subclass with the naming convention '_[traitname]_changed'. Thus,\n",
71 65 " to create static handler for the trait 'a', create the method\n",
72 66 " _a_changed(self, name, old, new) (fewer arguments can be used, see\n",
73 67 " below).\n",
74 68 "\n",
75 69 " Parameters\n",
76 70 " ----------\n",
77 71 " handler : callable\n",
78 72 " A callable that is called when a trait changes. Its\n",
79 73 " signature can be handler(), handler(name), handler(name, new)\n",
80 74 " or handler(name, old, new).\n",
81 75 " name : list, str, None\n",
82 76 " If None, the handler will apply to all traits. If a list\n",
83 77 " of str, handler will apply to all names in the list. If a\n",
84 78 " str, the handler will apply just to that name.\n",
85 79 " remove : bool\n",
86 80 " If False (the default), then install the handler. If True\n",
87 81 " then unintall it.\n",
88 82 " \n"
89 83 ]
90 84 }
91 85 ],
92 86 "prompt_number": 2
93 87 },
94 88 {
95 89 "cell_type": "markdown",
96 90 "metadata": {},
97 91 "source": [
98 92 "Mentioned in the doc string, the callback registered can have 4 possible signatures:\n",
99 93 "\n",
100 94 "- callback()\n",
101 95 "- callback(trait_name)\n",
102 96 "- callback(trait_name, new_value)\n",
103 97 "- callback(trait_name, old_value, new_value)\n",
104 98 "\n",
105 99 "Using this method, an example of how to output an IntSliderWiget's value as it is changed can be seen below."
106 100 ]
107 101 },
108 102 {
109 103 "cell_type": "code",
110 104 "collapsed": false,
111 105 "input": [
112 106 "int_range = widgets.IntSliderWidget()\n",
113 107 "display(int_range)\n",
114 108 "\n",
115 109 "def on_value_change(name, value):\n",
116 110 " print(value)\n",
117 111 "\n",
118 112 "int_range.on_trait_change(on_value_change, 'value')"
119 113 ],
120 114 "language": "python",
121 115 "metadata": {},
122 116 "outputs": [],
123 117 "prompt_number": 3
124 118 },
125 119 {
126 120 "cell_type": "heading",
127 121 "level": 1,
128 122 "metadata": {},
129 123 "source": [
130 124 "Specialized Events"
131 125 ]
132 126 },
133 127 {
134 128 "cell_type": "heading",
135 129 "level": 2,
136 130 "metadata": {},
137 131 "source": [
138 132 "Button Click Event"
139 133 ]
140 134 },
141 135 {
142 136 "cell_type": "markdown",
143 137 "metadata": {},
144 138 "source": [
145 139 "The `ButtonWidget` is a special widget, like the `ContainerWidget` and `TabWidget`, that isn't used to represent a data type. Instead the button widget is used to handle mouse clicks. The `on_click` method of the `ButtonWidget` can be used to register function to be called when the button is clicked. The docstring of the `on_click` can be seen below."
146 140 ]
147 141 },
148 142 {
149 143 "cell_type": "code",
150 144 "collapsed": false,
151 145 "input": [
152 146 "print(widgets.ButtonWidget.on_click.__doc__)"
153 147 ],
154 148 "language": "python",
155 149 "metadata": {},
156 150 "outputs": [
157 151 {
158 152 "output_type": "stream",
159 153 "stream": "stdout",
160 154 "text": [
161 155 "Register a callback to execute when the button is clicked.\n",
162 156 "\n",
163 157 " The callback will be called with one argument,\n",
164 158 " the clicked button widget instance.\n",
165 159 "\n",
166 160 " Parameters\n",
167 161 " ----------\n",
168 162 " remove : bool (optional)\n",
169 163 " Set to true to remove the callback from the list of callbacks.\n"
170 164 ]
171 165 }
172 166 ],
173 167 "prompt_number": 4
174 168 },
175 169 {
176 170 "cell_type": "markdown",
177 171 "metadata": {},
178 172 "source": [
179 173 "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."
180 174 ]
181 175 },
182 176 {
183 177 "cell_type": "code",
184 178 "collapsed": false,
185 179 "input": [
186 180 "button = widgets.ButtonWidget(description=\"Click Me!\")\n",
187 181 "display(button)\n",
188 182 "\n",
189 183 "def on_button_clicked(b):\n",
190 184 " print(\"Button clicked.\")\n",
191 185 "\n",
192 186 "button.on_click(on_button_clicked)"
193 187 ],
194 188 "language": "python",
195 189 "metadata": {},
196 190 "outputs": [
197 191 {
198 192 "output_type": "stream",
199 193 "stream": "stdout",
200 194 "text": [
201 195 "Button clicked.\n"
202 196 ]
203 197 },
204 198 {
205 199 "output_type": "stream",
206 200 "stream": "stdout",
207 201 "text": [
208 202 "Button clicked.\n"
209 203 ]
210 204 },
211 205 {
212 206 "output_type": "stream",
213 207 "stream": "stdout",
214 208 "text": [
215 209 "Button clicked.\n"
216 210 ]
217 211 }
218 212 ],
219 213 "prompt_number": 5
220 214 },
221 215 {
222 216 "cell_type": "markdown",
223 217 "metadata": {},
224 218 "source": [
225 219 "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."
226 220 ]
227 221 },
228 222 {
229 223 "cell_type": "code",
230 224 "collapsed": false,
231 225 "input": [
232 226 "def new_button(clicked):\n",
233 227 " button = widgets.ButtonWidget()\n",
234 228 " button.clicks = 0\n",
235 229 " clicked.clicks += 1\n",
236 230 " button.description = \"%d\" % clicked.clicks\n",
237 231 " display(button)\n",
238 232 " button.on_click(new_button)\n",
239 233 "button = widgets.ButtonWidget(description = \"Start\")\n",
240 234 "button.clicks = 0\n",
241 235 "display(button)\n",
242 236 "button.on_click(new_button)\n",
243 237 " "
244 238 ],
245 239 "language": "python",
246 240 "metadata": {},
247 241 "outputs": [],
248 242 "prompt_number": 6
249 },
250 {
251 "cell_type": "markdown",
252 "metadata": {},
253 "source": [
254 "In [Part 3](Part 3 - Placement.ipynb) of this [series](index.ipynb), you will learn about widget placement."
255 ]
256 243 }
257 244 ],
258 245 "metadata": {}
259 246 }
260 247 ]
261 248 } No newline at end of file
@@ -1,73 +1,65 b''
1 1 {
2 2 "metadata": {
3 3 "name": "",
4 "signature": "sha256:554c73fb7038db942bd25f77ad2217699e1f3fda6535fad67103256dc1beda0b"
4 "signature": "sha256:a2f4e040950648308ac7ec5f95d83948b4499ffc68e6bfd8f6fe8784955f52fb"
5 5 },
6 6 "nbformat": 3,
7 7 "nbformat_minor": 0,
8 8 "worksheets": [
9 9 {
10 10 "cells": [
11 11 {
12 12 "cell_type": "heading",
13 13 "level": 1,
14 14 "metadata": {},
15 15 "source": [
16 16 "Widget Tutorials"
17 17 ]
18 18 },
19 19 {
20 20 "cell_type": "markdown",
21 21 "metadata": {},
22 22 "source": [
23 23 "This directory includes a tutorials related to the IPython notebook widget framework."
24 24 ]
25 25 },
26 26 {
27 27 "cell_type": "heading",
28 28 "level": 2,
29 29 "metadata": {},
30 30 "source": [
31 31 "Interact"
32 32 ]
33 33 },
34 34 {
35 35 "cell_type": "markdown",
36 36 "metadata": {},
37 37 "source": [
38 38 "* [Using Interact](Using Interact.ipynb)"
39 39 ]
40 40 },
41 41 {
42 42 "cell_type": "heading",
43 43 "level": 2,
44 44 "metadata": {},
45 45 "source": [
46 46 "Widgets"
47 47 ]
48 48 },
49 49 {
50 50 "cell_type": "markdown",
51 51 "metadata": {},
52 52 "source": [
53 53 "- [Basics](Basics.ipynb) \n",
54 54 "- [Events](Events.ipynb) \n",
55 55 "- [Placement](Placement.ipynb) \n",
56 56 "- [Styles](Styles.ipynb) \n",
57 57 "- [Alignment](Alignment.ipynb) \n",
58 58 "- [Custom Widgets](Custom Widgets.ipynb) "
59 59 ]
60 },
61 {
62 "cell_type": "code",
63 "collapsed": false,
64 "input": [],
65 "language": "python",
66 "metadata": {},
67 "outputs": []
68 60 }
69 61 ],
70 62 "metadata": {}
71 63 }
72 64 ]
73 65 } No newline at end of file
@@ -1,187 +1,174 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 "name": ""
9 "name": "",
10 "signature": "sha256:60ff1a42e4dbc911ae15b409033e993257425c982809950746a567a4419b9484"
10 11 },
11 12 "nbformat": 3,
12 13 "nbformat_minor": 0,
13 14 "worksheets": [
14 15 {
15 16 "cells": [
16 17 {
17 "cell_type": "markdown",
18 "metadata": {},
19 "source": [
20 "[< Back to Part 2](Part 2 - Events.ipynb) or [Index](index.ipynb)"
21 ]
22 },
23 {
24 18 "cell_type": "code",
25 19 "collapsed": false,
26 20 "input": [
27 21 "from IPython.html import widgets # Widget definitions\n",
28 22 "from IPython.display import display # Used to display widgets in the notebook"
29 23 ],
30 24 "language": "python",
31 25 "metadata": {},
32 26 "outputs": [],
33 27 "prompt_number": 1
34 28 },
35 29 {
36 30 "cell_type": "heading",
37 31 "level": 1,
38 32 "metadata": {},
39 33 "source": [
40 34 "Parent/Child Relationships"
41 35 ]
42 36 },
43 37 {
44 38 "cell_type": "markdown",
45 39 "metadata": {},
46 40 "source": [
47 41 "To display widget A inside widget B, widget A must be a child of widget B. Only one instance of any particular widget can be child of another. In other words, *widget A* cannot have *widget B* listed twice in it's list of children.\n",
48 42 "\n",
49 43 "Widgets that can contain other widgets have a `children` attribute. This attribute can be set via a kwarg in the widget's constructor or after construction. Calling display on an object with children automatically displays those children, too."
50 44 ]
51 45 },
52 46 {
53 47 "cell_type": "code",
54 48 "collapsed": false,
55 49 "input": [
56 50 "float_range = widgets.FloatSliderWidget()\n",
57 51 "string = widgets.TextWidget(value='hi')\n",
58 52 "container = widgets.ContainerWidget(children=[float_range, string])\n",
59 53 "\n",
60 54 "display(container) # Displays the `container` and all of it's children."
61 55 ],
62 56 "language": "python",
63 57 "metadata": {},
64 58 "outputs": [],
65 59 "prompt_number": 2
66 60 },
67 61 {
68 62 "cell_type": "markdown",
69 63 "metadata": {},
70 64 "source": [
71 65 "Children can also be added to parents after the parent has been displayed. The parent is responsible for rendering its children."
72 66 ]
73 67 },
74 68 {
75 69 "cell_type": "code",
76 70 "collapsed": false,
77 71 "input": [
78 72 "container = widgets.ContainerWidget()\n",
79 73 "display(container)\n",
80 74 "\n",
81 75 "int_range = widgets.IntSliderWidget()\n",
82 76 "container.children=[int_range]\n"
83 77 ],
84 78 "language": "python",
85 79 "metadata": {},
86 80 "outputs": [],
87 81 "prompt_number": 3
88 82 },
89 83 {
90 84 "cell_type": "heading",
91 85 "level": 1,
92 86 "metadata": {},
93 87 "source": [
94 88 "Visibility"
95 89 ]
96 90 },
97 91 {
98 92 "cell_type": "markdown",
99 93 "metadata": {},
100 94 "source": [
101 95 "Sometimes it is necessary to hide or show widgets in place, without having to redisplay the widget.\n",
102 96 "The `visibility` property of widgets can be used to hide or show widgets that have already been displayed (as seen below)."
103 97 ]
104 98 },
105 99 {
106 100 "cell_type": "code",
107 101 "collapsed": false,
108 102 "input": [
109 103 "string = widgets.LatexWidget(value=\"Hello World!\")\n",
110 104 "display(string) "
111 105 ],
112 106 "language": "python",
113 107 "metadata": {},
114 108 "outputs": [],
115 109 "prompt_number": 4
116 110 },
117 111 {
118 112 "cell_type": "code",
119 113 "collapsed": false,
120 114 "input": [
121 115 "string.visible=False"
122 116 ],
123 117 "language": "python",
124 118 "metadata": {},
125 119 "outputs": [],
126 120 "prompt_number": 5
127 121 },
128 122 {
129 123 "cell_type": "code",
130 124 "collapsed": false,
131 125 "input": [
132 126 "string.visible=True"
133 127 ],
134 128 "language": "python",
135 129 "metadata": {},
136 130 "outputs": [],
137 131 "prompt_number": 6
138 132 },
139 133 {
140 134 "cell_type": "markdown",
141 135 "metadata": {},
142 136 "source": [
143 137 "In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
144 138 ]
145 139 },
146 140 {
147 141 "cell_type": "code",
148 142 "collapsed": false,
149 143 "input": [
150 144 "form = widgets.ContainerWidget()\n",
151 145 "first = widgets.TextWidget(description=\"First Name:\")\n",
152 146 "last = widgets.TextWidget(description=\"Last Name:\")\n",
153 147 "\n",
154 148 "student = widgets.CheckboxWidget(description=\"Student:\", value=False)\n",
155 149 "school_info = widgets.ContainerWidget(visible=False, children=[\n",
156 150 " widgets.TextWidget(description=\"School:\"),\n",
157 151 " widgets.IntTextWidget(description=\"Grade:\", min=0, max=12)\n",
158 152 " ])\n",
159 153 "\n",
160 154 "pet = widgets.TextWidget(description=\"Pet's Name:\")\n",
161 155 "form.children = [first, last, student, school_info, pet]\n",
162 156 "display(form)\n",
163 157 "\n",
164 158 "def on_student_toggle(name, value):\n",
165 159 " if value:\n",
166 160 " school_info.visible = True\n",
167 161 " else:\n",
168 162 " school_info.visible = False\n",
169 163 "student.on_trait_change(on_student_toggle, 'value')\n"
170 164 ],
171 165 "language": "python",
172 166 "metadata": {},
173 167 "outputs": [],
174 168 "prompt_number": 7
175 },
176 {
177 "cell_type": "markdown",
178 "metadata": {},
179 "source": [
180 "In [Part 4](Part 4 - Styles.ipynb) of this [series](index.ipynb), you will learn about widget styling."
181 ]
182 169 }
183 170 ],
184 171 "metadata": {}
185 172 }
186 173 ]
187 174 } No newline at end of file
@@ -1,368 +1,355 b''
1 1 {
2 2 "metadata": {
3 3 "cell_tags": [
4 4 [
5 5 "<None>",
6 6 null
7 7 ]
8 8 ],
9 "name": ""
9 "name": "",
10 "signature": "sha256:cd7d3d42126bdbf20c087014460779dfbdb0a63dcb8f489ba7ebfc230a685edd"
10 11 },
11 12 "nbformat": 3,
12 13 "nbformat_minor": 0,
13 14 "worksheets": [
14 15 {
15 16 "cells": [
16 17 {
17 "cell_type": "markdown",
18 "metadata": {},
19 "source": [
20 "[< Back to Part 3](Part 3 - Placement.ipynb) or [Index](index.ipynb)"
21 ]
22 },
23 {
24 18 "cell_type": "code",
25 19 "collapsed": false,
26 20 "input": [
27 21 "from IPython.html import widgets # Widget definitions\n",
28 22 "from IPython.display import display # Used to display widgets in the notebook"
29 23 ],
30 24 "language": "python",
31 25 "metadata": {},
32 26 "outputs": [],
33 27 "prompt_number": 1
34 28 },
35 29 {
36 30 "cell_type": "heading",
37 31 "level": 1,
38 32 "metadata": {},
39 33 "source": [
40 34 "CSS"
41 35 ]
42 36 },
43 37 {
44 38 "cell_type": "markdown",
45 39 "metadata": {},
46 40 "source": [
47 41 "When trying to design an attractive widget GUI, styling becomes important.\n",
48 42 "Most widget views are DOM (document object model) elements that can be controlled with CSS.\n",
49 43 "There are two helper methods that allow the manipulation of the widget's CSS.\n",
50 44 "The first is the `Widget.set_css` method.\n",
51 45 "This method allows one or more CSS attributes to be set at once. "
52 46 ]
53 47 },
54 48 {
55 49 "cell_type": "code",
56 50 "collapsed": false,
57 51 "input": [
58 52 "print(widgets.DOMWidget.set_css.__doc__)"
59 53 ],
60 54 "language": "python",
61 55 "metadata": {},
62 56 "outputs": [
63 57 {
64 58 "output_type": "stream",
65 59 "stream": "stdout",
66 60 "text": [
67 61 "Set one or more CSS properties of the widget.\n",
68 62 "\n",
69 63 " This function has two signatures:\n",
70 64 " - set_css(css_dict, selector='')\n",
71 65 " - set_css(key, value, selector='')\n",
72 66 "\n",
73 67 " Parameters\n",
74 68 " ----------\n",
75 69 " css_dict : dict\n",
76 70 " CSS key/value pairs to apply\n",
77 71 " key: unicode\n",
78 72 " CSS key\n",
79 73 " value:\n",
80 74 " CSS value\n",
81 75 " selector: unicode (optional, kwarg only)\n",
82 76 " JQuery selector to use to apply the CSS key/value. If no selector \n",
83 77 " is provided, an empty selector is used. An empty selector makes the \n",
84 78 " front-end try to apply the css to a default element. The default\n",
85 79 " element is an attribute unique to each view, which is a DOM element\n",
86 80 " of the view that should be styled with common CSS (see \n",
87 81 " `$el_to_style` in the Javascript code).\n",
88 82 " \n"
89 83 ]
90 84 }
91 85 ],
92 86 "prompt_number": 2
93 87 },
94 88 {
95 89 "cell_type": "markdown",
96 90 "metadata": {},
97 91 "source": [
98 92 "The second is `get_css` which allows CSS attributesto be read.\n",
99 93 "Note that this method will only read CSS attributes that have been set using the `set_css` method."
100 94 ]
101 95 },
102 96 {
103 97 "cell_type": "code",
104 98 "collapsed": false,
105 99 "input": [
106 100 "print(widgets.DOMWidget.get_css.__doc__)"
107 101 ],
108 102 "language": "python",
109 103 "metadata": {},
110 104 "outputs": [
111 105 {
112 106 "output_type": "stream",
113 107 "stream": "stdout",
114 108 "text": [
115 109 "Get a CSS property of the widget.\n",
116 110 "\n",
117 111 " Note: This function does not actually request the CSS from the \n",
118 112 " front-end; Only properties that have been set with set_css can be read.\n",
119 113 "\n",
120 114 " Parameters\n",
121 115 " ----------\n",
122 116 " key: unicode\n",
123 117 " CSS key\n",
124 118 " selector: unicode (optional)\n",
125 119 " JQuery selector used when the CSS key/value was set.\n",
126 120 " \n"
127 121 ]
128 122 }
129 123 ],
130 124 "prompt_number": 3
131 125 },
132 126 {
133 127 "cell_type": "markdown",
134 128 "metadata": {},
135 129 "source": [
136 130 "Below is an example that applies CSS attributes to a container to emphasize text."
137 131 ]
138 132 },
139 133 {
140 134 "cell_type": "code",
141 135 "collapsed": false,
142 136 "input": [
143 137 "label = widgets.LatexWidget()\n",
144 138 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
145 139 "container = widgets.ContainerWidget(children=[label])\n",
146 140 "\n",
147 141 "# set_css used to set a single CSS attribute.\n",
148 142 "container.set_css('border', '3px solid black') # Border the container\n",
149 143 "\n",
150 144 "# set_css used to set multiple CSS attributes.\n",
151 145 "container.set_css({'padding': '6px', # Add padding to the container\n",
152 146 " 'background': 'yellow'}) # Fill the container yellow\n",
153 147 "\n",
154 148 "display(container)"
155 149 ],
156 150 "language": "python",
157 151 "metadata": {},
158 152 "outputs": [],
159 153 "prompt_number": 4
160 154 },
161 155 {
162 156 "cell_type": "heading",
163 157 "level": 1,
164 158 "metadata": {},
165 159 "source": [
166 160 "CSS Classes"
167 161 ]
168 162 },
169 163 {
170 164 "cell_type": "markdown",
171 165 "metadata": {},
172 166 "source": [
173 167 "In some cases, it is necessary to apply CSS classes to your widgets.\n",
174 168 "CSS classes allow DOM elements to be indentified in Javascript and CSS.\n",
175 169 "The notebook defines its own set of classes to stylize its elements.\n",
176 170 "The `add_class` widget method allows you to add CSS classes to your widget."
177 171 ]
178 172 },
179 173 {
180 174 "cell_type": "code",
181 175 "collapsed": false,
182 176 "input": [
183 177 "print(widgets.DOMWidget.add_class.__doc__)"
184 178 ],
185 179 "language": "python",
186 180 "metadata": {},
187 181 "outputs": [
188 182 {
189 183 "output_type": "stream",
190 184 "stream": "stdout",
191 185 "text": [
192 186 "Add class[es] to a DOM element.\n",
193 187 "\n",
194 188 " Parameters\n",
195 189 " ----------\n",
196 190 " class_names: unicode or list\n",
197 191 " Class name(s) to add to the DOM element(s).\n",
198 192 " selector: unicode (optional)\n",
199 193 " JQuery selector to select the DOM element(s) that the class(es) will\n",
200 194 " be added to.\n",
201 195 " \n"
202 196 ]
203 197 }
204 198 ],
205 199 "prompt_number": 5
206 200 },
207 201 {
208 202 "cell_type": "markdown",
209 203 "metadata": {},
210 204 "source": [
211 205 "Since `add_class` is a DOM operation, **it will only affect widgets that have already been displayed**.\n",
212 206 "`add_class` must be called after the widget has been displayed.\n",
213 207 "Extending the example above, the corners of the container can be rounded by adding the `corner-all` CSS class to the container."
214 208 ]
215 209 },
216 210 {
217 211 "cell_type": "code",
218 212 "collapsed": false,
219 213 "input": [
220 214 "container = widgets.ContainerWidget()\n",
221 215 "container.set_css({'border': '3px solid black',\n",
222 216 " 'padding': '6px', \n",
223 217 " 'background': 'yellow'}) \n",
224 218 "\n",
225 219 "label = widgets.LatexWidget()\n",
226 220 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
227 221 "container.children = [label]\n",
228 222 "display(container)\n",
229 223 "container.add_class('corner-all') # Must be called AFTER display"
230 224 ],
231 225 "language": "python",
232 226 "metadata": {},
233 227 "outputs": [],
234 228 "prompt_number": 6
235 229 },
236 230 {
237 231 "cell_type": "markdown",
238 232 "metadata": {},
239 233 "source": [
240 234 "The IPython notebook uses [bootstrap](http://getbootstrap.com/\u200e) for styling.\n",
241 235 "The example above can be simplified by using a bootstrap class:"
242 236 ]
243 237 },
244 238 {
245 239 "cell_type": "code",
246 240 "collapsed": false,
247 241 "input": [
248 242 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
249 243 "display(label)\n",
250 244 "\n",
251 245 "# Apply twitter bootstrap alert class to the label.\n",
252 246 "label.add_class(\"alert\")"
253 247 ],
254 248 "language": "python",
255 249 "metadata": {},
256 250 "outputs": [],
257 251 "prompt_number": 7
258 252 },
259 253 {
260 254 "cell_type": "markdown",
261 255 "metadata": {},
262 256 "source": [
263 257 "The example below shows how bootstrap classes can be used to change button apearance."
264 258 ]
265 259 },
266 260 {
267 261 "cell_type": "code",
268 262 "collapsed": false,
269 263 "input": [
270 264 "# List of the bootstrap button styles\n",
271 265 "button_classes = ['Default', 'btn-primary', 'btn-info', 'btn-success', \n",
272 266 " 'btn-warning', 'btn-danger', 'btn-inverse', 'btn-link']\n",
273 267 "\n",
274 268 "# Create each button and apply the style. Also add margin to the buttons so they space\n",
275 269 "# themselves nicely.\n",
276 270 "for i in range(8):\n",
277 271 " button = widgets.ButtonWidget(description=button_classes[i])\n",
278 272 " button.set_css(\"margin\", \"5px\")\n",
279 273 " display(button)\n",
280 274 " if i > 0: # Don't add a class the first button.\n",
281 275 " button.add_class(button_classes[i])\n",
282 276 " "
283 277 ],
284 278 "language": "python",
285 279 "metadata": {},
286 280 "outputs": [],
287 281 "prompt_number": 8
288 282 },
289 283 {
290 284 "cell_type": "markdown",
291 285 "metadata": {},
292 286 "source": [
293 287 "It is also useful to be able to remove CSS classes from widgets.\n",
294 288 "The `remove_class` method allows you to remove classes from widgets that have been displayed.\n",
295 289 "Like `add_class`, it must be called after the widget has been displayed."
296 290 ]
297 291 },
298 292 {
299 293 "cell_type": "code",
300 294 "collapsed": false,
301 295 "input": [
302 296 "print(widgets.DOMWidget.remove_class.__doc__)"
303 297 ],
304 298 "language": "python",
305 299 "metadata": {},
306 300 "outputs": [
307 301 {
308 302 "output_type": "stream",
309 303 "stream": "stdout",
310 304 "text": [
311 305 "Remove class[es] from a DOM element.\n",
312 306 "\n",
313 307 " Parameters\n",
314 308 " ----------\n",
315 309 " class_names: unicode or list\n",
316 310 " Class name(s) to remove from the DOM element(s).\n",
317 311 " selector: unicode (optional)\n",
318 312 " JQuery selector to select the DOM element(s) that the class(es) will\n",
319 313 " be removed from.\n",
320 314 " \n"
321 315 ]
322 316 }
323 317 ],
324 318 "prompt_number": 9
325 319 },
326 320 {
327 321 "cell_type": "markdown",
328 322 "metadata": {},
329 323 "source": [
330 324 "The example below animates an alert using different bootstrap styles."
331 325 ]
332 326 },
333 327 {
334 328 "cell_type": "code",
335 329 "collapsed": false,
336 330 "input": [
337 331 "import time\n",
338 332 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
339 333 "display(label)\n",
340 334 "\n",
341 335 "# Apply twitter bootstrap alert class to the label.\n",
342 336 "label.add_class(\"alert\")\n",
343 337 "\n",
344 338 "# Animate through additional bootstrap label styles 3 times\n",
345 339 "additional_alert_styles = ['alert-error', 'alert-info', 'alert-success']\n",
346 340 "for i in range(3 * len(additional_alert_styles)):\n",
347 341 " label.add_class(additional_alert_styles[i % 3])\n",
348 342 " label.remove_class(additional_alert_styles[(i-1) % 3])\n",
349 343 " time.sleep(1)\n",
350 344 " "
351 345 ],
352 346 "language": "python",
353 347 "metadata": {},
354 348 "outputs": [],
355 349 "prompt_number": 10
356 },
357 {
358 "cell_type": "markdown",
359 "metadata": {},
360 "source": [
361 "In [Part 5](Part 5 - Alignment.ipynb) of this [series](index.ipynb), you will learn about widget alignment."
362 ]
363 350 }
364 351 ],
365 352 "metadata": {}
366 353 }
367 354 ]
368 355 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now