##// END OF EJS Templates
review pass on widget examples
MinRK -
Show More
@@ -1,314 +1,314 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 20 "[Index](index.ipynb)\n",
21 21 "\n",
22 22 "To use IPython widgets in the notebook, the widget namespace needs to be imported."
23 23 ]
24 24 },
25 25 {
26 26 "cell_type": "code",
27 27 "collapsed": false,
28 28 "input": [
29 29 "from IPython.html import widgets # Widget definitions\n",
30 30 "from IPython.display import display # Used to display widgets in the notebook"
31 31 ],
32 32 "language": "python",
33 33 "metadata": {},
34 34 "outputs": [],
35 35 "prompt_number": 1
36 36 },
37 37 {
38 38 "cell_type": "heading",
39 39 "level": 1,
40 40 "metadata": {},
41 41 "source": [
42 42 "Basic Widgets"
43 43 ]
44 44 },
45 45 {
46 46 "cell_type": "markdown",
47 47 "metadata": {},
48 48 "source": [
49 "The IPython notebook comes preloaded with basic widgets that represent common data types. These widgets are\n",
49 "IPython comes with basic widgets that represent common interactive controls. These widgets are\n",
50 50 "\n",
51 51 "- CheckBoxWidget\n",
52 52 "- ToggleButtonWidget\n",
53 53 "- FloatSliderWidget\n",
54 54 "- BoundedFloatTextWidget\n",
55 55 "- FloatProgressWidget\n",
56 56 "- FloatTextWidget\n",
57 57 "- ImageWidget\n",
58 58 "- IntSliderWidget\n",
59 59 "- BoundedIntTextWidget\n",
60 60 "- IntProgressWidget\n",
61 61 "- IntTextWidget\n",
62 62 "- ToggleButtonsWidget\n",
63 63 "- RadioButtonsWidget\n",
64 64 "- DropdownWidget\n",
65 65 "- ListBoxWidget\n",
66 66 "- HTMLWidget\n",
67 67 "- LatexWidget\n",
68 68 "- TextAreaWidget\n",
69 69 "- TextBoxWidget\n",
70 "- ButtonWidget\n",
70 71 "\n",
71 72 "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",
72 73 "\n",
73 "- ButtonWidget\n",
74 74 "- ContainerWidget\n",
75 75 "- PopupWidget\n",
76 76 "- AccordionWidget\n",
77 77 "- TabWidget\n",
78 78 "\n",
79 79 "To see the complete list of widgets, one can execute the following"
80 80 ]
81 81 },
82 82 {
83 83 "cell_type": "code",
84 84 "collapsed": false,
85 85 "input": [
86 86 "[widget for widget in dir(widgets) if widget.endswith('Widget')]"
87 87 ],
88 88 "language": "python",
89 89 "metadata": {},
90 90 "outputs": [
91 91 {
92 92 "metadata": {},
93 93 "output_type": "pyout",
94 94 "prompt_number": 2,
95 95 "text": [
96 96 "['AccordionWidget',\n",
97 97 " 'BoundedFloatTextWidget',\n",
98 98 " 'BoundedIntTextWidget',\n",
99 99 " 'ButtonWidget',\n",
100 100 " 'CheckBoxWidget',\n",
101 101 " 'ContainerWidget',\n",
102 102 " 'DOMWidget',\n",
103 103 " 'DropdownWidget',\n",
104 104 " 'FloatProgressWidget',\n",
105 105 " 'FloatSliderWidget',\n",
106 106 " 'FloatTextWidget',\n",
107 107 " 'HTMLWidget',\n",
108 108 " 'ImageWidget',\n",
109 109 " 'IntProgressWidget',\n",
110 110 " 'IntSliderWidget',\n",
111 111 " 'IntTextWidget',\n",
112 112 " 'LatexWidget',\n",
113 113 " 'ListBoxWidget',\n",
114 114 " 'PopupWidget',\n",
115 115 " 'RadioButtonsWidget',\n",
116 116 " 'TabWidget',\n",
117 117 " 'TextAreaWidget',\n",
118 118 " 'TextBoxWidget',\n",
119 119 " 'ToggleButtonWidget',\n",
120 120 " 'ToggleButtonsWidget',\n",
121 121 " 'Widget']"
122 122 ]
123 123 }
124 124 ],
125 125 "prompt_number": 2
126 126 },
127 127 {
128 128 "cell_type": "markdown",
129 129 "metadata": {},
130 130 "source": [
131 "The basic widgets can all be constructed without arguments. The following creates a *FloatSliderWidget* without displaying it"
131 "The basic widgets all have sensible default values. Create a *FloatSliderWidget* without displaying it:"
132 132 ]
133 133 },
134 134 {
135 135 "cell_type": "code",
136 136 "collapsed": false,
137 137 "input": [
138 138 "mywidget = widgets.FloatSliderWidget()"
139 139 ],
140 140 "language": "python",
141 141 "metadata": {},
142 142 "outputs": [],
143 143 "prompt_number": 3
144 144 },
145 145 {
146 146 "cell_type": "markdown",
147 147 "metadata": {},
148 148 "source": [
149 149 "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 150 ]
151 151 },
152 152 {
153 153 "cell_type": "code",
154 154 "collapsed": false,
155 155 "input": [
156 156 "display(mywidget)"
157 157 ],
158 158 "language": "python",
159 159 "metadata": {},
160 160 "outputs": [],
161 161 "prompt_number": 4
162 162 },
163 163 {
164 164 "cell_type": "markdown",
165 165 "metadata": {},
166 166 "source": [
167 167 "or"
168 168 ]
169 169 },
170 170 {
171 171 "cell_type": "code",
172 172 "collapsed": false,
173 173 "input": [
174 174 "mywidget"
175 175 ],
176 176 "language": "python",
177 177 "metadata": {},
178 178 "outputs": [],
179 179 "prompt_number": 5
180 180 },
181 181 {
182 182 "cell_type": "markdown",
183 183 "metadata": {},
184 184 "source": [
185 185 "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 186 "\n",
187 "Widgets are manipulated via special instance properties (traitlets). The names of these instance properties are listed in the widget's `keys` property (as seen below). A few of these properties are common to most, if not all, widgets. The common properties are `value`, `description`, `visible`, and `disabled`. `_css` and `_view_name` are internal properties that exist in all widgets and should not be modified."
187 "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 188 ]
189 189 },
190 190 {
191 191 "cell_type": "code",
192 192 "collapsed": false,
193 193 "input": [
194 194 "mywidget.keys"
195 195 ],
196 196 "language": "python",
197 197 "metadata": {},
198 198 "outputs": [
199 199 {
200 200 "metadata": {},
201 201 "output_type": "pyout",
202 202 "prompt_number": 6,
203 203 "text": [
204 204 "['_view_name',\n",
205 " 'description',\n",
206 " 'min',\n",
207 205 " 'orientation',\n",
206 " 'min',\n",
208 207 " 'max',\n",
209 208 " '_css',\n",
210 209 " 'value',\n",
211 210 " 'disabled',\n",
212 211 " 'visible',\n",
213 " 'step']"
212 " 'step',\n",
213 " 'description']"
214 214 ]
215 215 }
216 216 ],
217 217 "prompt_number": 6
218 218 },
219 219 {
220 220 "cell_type": "markdown",
221 221 "metadata": {},
222 222 "source": [
223 "Changing a widget's property value will automatically update that widget everywhere it is displayed in the notebook. Here the value of `mywidget` is set. The slider shown above (after inputs 4 and 5) updates automatically to the new value. In reverse, changing the value of the displayed widget will update the property's value."
223 "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 224 ]
225 225 },
226 226 {
227 227 "cell_type": "code",
228 228 "collapsed": false,
229 229 "input": [
230 230 "mywidget.value = 25.0"
231 231 ],
232 232 "language": "python",
233 233 "metadata": {},
234 234 "outputs": [],
235 235 "prompt_number": 7
236 236 },
237 237 {
238 238 "cell_type": "markdown",
239 239 "metadata": {},
240 240 "source": [
241 241 "After changing the widget's value in the notebook by hand to 0.0 (sliding the bar to the far left)."
242 242 ]
243 243 },
244 244 {
245 245 "cell_type": "code",
246 246 "collapsed": false,
247 247 "input": [
248 248 "mywidget.value"
249 249 ],
250 250 "language": "python",
251 251 "metadata": {},
252 252 "outputs": [
253 253 {
254 254 "metadata": {},
255 255 "output_type": "pyout",
256 256 "prompt_number": 8,
257 257 "text": [
258 "0.0"
258 "25.0"
259 259 ]
260 260 }
261 261 ],
262 262 "prompt_number": 8
263 263 },
264 264 {
265 265 "cell_type": "markdown",
266 266 "metadata": {},
267 267 "source": [
268 "Widget property values can also be set with kwargs during the construction of the widget (as seen below)."
268 "Widget values can also be set with kwargs during the construction of the widget (as seen below)."
269 269 ]
270 270 },
271 271 {
272 272 "cell_type": "code",
273 273 "collapsed": false,
274 274 "input": [
275 275 "mysecondwidget = widgets.RadioButtonsWidget(values=[\"Item A\", \"Item B\", \"Item C\"], value=\"Item A\")\n",
276 276 "display(mysecondwidget)"
277 277 ],
278 278 "language": "python",
279 279 "metadata": {},
280 280 "outputs": [],
281 281 "prompt_number": 9
282 282 },
283 283 {
284 284 "cell_type": "code",
285 285 "collapsed": false,
286 286 "input": [
287 287 "mysecondwidget.value"
288 288 ],
289 289 "language": "python",
290 290 "metadata": {},
291 291 "outputs": [
292 292 {
293 293 "metadata": {},
294 294 "output_type": "pyout",
295 295 "prompt_number": 10,
296 296 "text": [
297 297 "'Item A'"
298 298 ]
299 299 }
300 300 ],
301 301 "prompt_number": 10
302 302 },
303 303 {
304 304 "cell_type": "markdown",
305 305 "metadata": {},
306 306 "source": [
307 307 "In [Part 2](Part 2 - Events.ipynb) of this [series](index.ipynb), you will learn about widget events."
308 308 ]
309 309 }
310 310 ],
311 311 "metadata": {}
312 312 }
313 313 ]
314 314 } No newline at end of file
@@ -1,286 +1,276 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 20 "[< Back to Part 1](Part 1 - Basics.ipynb) or [Index](index.ipynb)"
21 21 ]
22 22 },
23 23 {
24 24 "cell_type": "code",
25 25 "collapsed": false,
26 26 "input": [
27 27 "from __future__ import print_function # 2.7 compatability\n",
28 28 "\n",
29 29 "from IPython.html import widgets # Widget definitions\n",
30 30 "from IPython.display import display # Used to display widgets in the notebook"
31 31 ],
32 32 "language": "python",
33 33 "metadata": {},
34 34 "outputs": [],
35 35 "prompt_number": 1
36 36 },
37 37 {
38 38 "cell_type": "heading",
39 39 "level": 1,
40 40 "metadata": {},
41 41 "source": [
42 42 "Traitlet Events"
43 43 ]
44 44 },
45 45 {
46 46 "cell_type": "markdown",
47 47 "metadata": {},
48 48 "source": [
49 "As mentioned in Part 1, the widget properties are IPython traitlets. Traitlets are eventful. To handle property value changes, the `on_trait_change` method of the widget can be used to register an event handling callback. The doc string for `on_trait_change` can be seen below. Both the `name` and `remove` properties are optional."
49 "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 50 ]
51 51 },
52 52 {
53 53 "cell_type": "code",
54 54 "collapsed": false,
55 55 "input": [
56 56 "print(widgets.Widget.on_trait_change.__doc__)"
57 57 ],
58 58 "language": "python",
59 59 "metadata": {},
60 60 "outputs": [
61 61 {
62 62 "output_type": "stream",
63 63 "stream": "stdout",
64 64 "text": [
65 65 "Setup a handler to be called when a trait changes.\n",
66 66 "\n",
67 67 " This is used to setup dynamic notifications of trait changes.\n",
68 68 "\n",
69 69 " Static handlers can be created by creating methods on a HasTraits\n",
70 70 " subclass with the naming convention '_[traitname]_changed'. Thus,\n",
71 71 " to create static handler for the trait 'a', create the method\n",
72 72 " _a_changed(self, name, old, new) (fewer arguments can be used, see\n",
73 73 " below).\n",
74 74 "\n",
75 75 " Parameters\n",
76 76 " ----------\n",
77 77 " handler : callable\n",
78 78 " A callable that is called when a trait changes. Its\n",
79 79 " signature can be handler(), handler(name), handler(name, new)\n",
80 80 " or handler(name, old, new).\n",
81 81 " name : list, str, None\n",
82 82 " If None, the handler will apply to all traits. If a list\n",
83 83 " of str, handler will apply to all names in the list. If a\n",
84 84 " str, the handler will apply just to that name.\n",
85 85 " remove : bool\n",
86 86 " If False (the default), then install the handler. If True\n",
87 87 " then unintall it.\n",
88 88 " \n"
89 89 ]
90 90 }
91 91 ],
92 92 "prompt_number": 2
93 93 },
94 94 {
95 95 "cell_type": "markdown",
96 96 "metadata": {},
97 97 "source": [
98 98 "Mentioned in the doc string, the callback registered can have 4 possible signatures:\n",
99 99 "\n",
100 100 "- callback()\n",
101 101 "- callback(trait_name)\n",
102 102 "- callback(trait_name, new_value)\n",
103 103 "- callback(trait_name, old_value, new_value)\n",
104 104 "\n",
105 105 "Using this method, an example of how to output an IntSliderWiget's value as it is changed can be seen below."
106 106 ]
107 107 },
108 108 {
109 109 "cell_type": "code",
110 110 "collapsed": false,
111 111 "input": [
112 "intrange = widgets.IntSliderWidget()\n",
113 "display(intrange)\n",
112 "int_range = widgets.IntSliderWidget()\n",
113 "display(int_range)\n",
114 114 "\n",
115 115 "def on_value_change(name, value):\n",
116 116 " print(value)\n",
117 117 "\n",
118 "intrange.on_trait_change(on_value_change, 'value')"
118 "int_range.on_trait_change(on_value_change, 'value')"
119 119 ],
120 120 "language": "python",
121 121 "metadata": {},
122 122 "outputs": [
123 123 {
124 124 "output_type": "stream",
125 125 "stream": "stdout",
126 126 "text": [
127 "34\n"
127 "1\n"
128 128 ]
129 129 },
130 130 {
131 131 "output_type": "stream",
132 132 "stream": "stdout",
133 133 "text": [
134 "74\n"
134 "2\n"
135 135 ]
136 136 },
137 137 {
138 138 "output_type": "stream",
139 139 "stream": "stdout",
140 140 "text": [
141 "98\n"
141 "3\n"
142 142 ]
143 143 }
144 144 ],
145 145 "prompt_number": 3
146 146 },
147 147 {
148 148 "cell_type": "heading",
149 149 "level": 1,
150 150 "metadata": {},
151 151 "source": [
152 152 "Specialized Events"
153 153 ]
154 154 },
155 155 {
156 156 "cell_type": "heading",
157 157 "level": 2,
158 158 "metadata": {},
159 159 "source": [
160 "Button On Click Event"
160 "Button Click Event"
161 161 ]
162 162 },
163 163 {
164 164 "cell_type": "markdown",
165 165 "metadata": {},
166 166 "source": [
167 "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 a click even handler. The doc string of the `on_click` can be seen below."
167 "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."
168 168 ]
169 169 },
170 170 {
171 171 "cell_type": "code",
172 172 "collapsed": false,
173 173 "input": [
174 174 "print(widgets.ButtonWidget.on_click.__doc__)"
175 175 ],
176 176 "language": "python",
177 177 "metadata": {},
178 178 "outputs": [
179 179 {
180 180 "output_type": "stream",
181 181 "stream": "stdout",
182 182 "text": [
183 "Register a callback to execute when the button is clicked. \n",
183 "Register a callback to execute when the button is clicked.\n",
184 184 "\n",
185 " The callback can either accept no parameters or one sender parameter:\n",
186 " - callback()\n",
187 " - callback(sender)\n",
188 " If the callback has a sender parameter, the ButtonWidget instance that\n",
189 " called the callback will be passed into the method as the sender.\n",
185 " The callback will be called with one argument,\n",
186 " the clicked button widget instance.\n",
190 187 "\n",
191 188 " Parameters\n",
192 189 " ----------\n",
193 190 " remove : bool (optional)\n",
194 191 " Set to true to remove the callback from the list of callbacks.\n"
195 192 ]
196 193 }
197 194 ],
198 195 "prompt_number": 4
199 196 },
200 197 {
201 198 "cell_type": "markdown",
202 199 "metadata": {},
203 200 "source": [
204 201 "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."
205 202 ]
206 203 },
207 204 {
208 205 "cell_type": "code",
209 206 "collapsed": false,
210 207 "input": [
211 208 "button = widgets.ButtonWidget(description=\"Click Me!\")\n",
212 209 "display(button)\n",
213 210 "\n",
214 "def on_button_clicked(sender):\n",
211 "def on_button_clicked(b):\n",
215 212 " print(\"Button clicked.\")\n",
216 213 "\n",
217 214 "button.on_click(on_button_clicked)"
218 215 ],
219 216 "language": "python",
220 217 "metadata": {},
221 218 "outputs": [
222 219 {
223 220 "output_type": "stream",
224 221 "stream": "stdout",
225 222 "text": [
226 223 "Button clicked.\n"
227 224 ]
228 225 },
229 226 {
230 227 "output_type": "stream",
231 228 "stream": "stdout",
232 229 "text": [
233 230 "Button clicked.\n"
234 231 ]
235 },
236 {
237 "output_type": "stream",
238 "stream": "stdout",
239 "text": [
240 "Button clicked.\n"
241 ]
242 232 }
243 233 ],
244 234 "prompt_number": 5
245 235 },
246 236 {
247 237 "cell_type": "markdown",
248 238 "metadata": {},
249 239 "source": [
250 240 "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."
251 241 ]
252 242 },
253 243 {
254 244 "cell_type": "code",
255 245 "collapsed": false,
256 246 "input": [
257 "def show_button(sender):\n",
247 "def new_button(clicked):\n",
258 248 " button = widgets.ButtonWidget()\n",
259 249 " button.clicks = 0\n",
260 " sender.clicks += 1\n",
261 " button.description = \"%d\" % sender.clicks\n",
250 " clicked.clicks += 1\n",
251 " button.description = \"%d\" % clicked.clicks\n",
262 252 " display(button)\n",
263 " button.on_click(show_button)\n",
253 " button.on_click(new_button)\n",
264 254 "button = widgets.ButtonWidget(description = \"Start\")\n",
265 255 "button.clicks = 0\n",
266 256 "display(button)\n",
267 "button.on_click(show_button)\n",
257 "button.on_click(new_button)\n",
268 258 " "
269 259 ],
270 260 "language": "python",
271 261 "metadata": {},
272 262 "outputs": [],
273 263 "prompt_number": 6
274 264 },
275 265 {
276 266 "cell_type": "markdown",
277 267 "metadata": {},
278 268 "source": [
279 269 "In [Part 3](Part 3 - Placement.ipynb) of this [series](index.ipynb), you will learn about widget placement."
280 270 ]
281 271 }
282 272 ],
283 273 "metadata": {}
284 274 }
285 275 ]
286 276 } No newline at end of file
@@ -1,186 +1,187 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 20 "[< Back to Part 2](Part 2 - Events.ipynb) or [Index](index.ipynb)"
21 21 ]
22 22 },
23 23 {
24 24 "cell_type": "code",
25 25 "collapsed": false,
26 26 "input": [
27 27 "from IPython.html import widgets # Widget definitions\n",
28 28 "from IPython.display import display # Used to display widgets in the notebook"
29 29 ],
30 30 "language": "python",
31 31 "metadata": {},
32 32 "outputs": [],
33 33 "prompt_number": 1
34 34 },
35 35 {
36 36 "cell_type": "heading",
37 37 "level": 1,
38 38 "metadata": {},
39 39 "source": [
40 40 "Parent/Child Relationships"
41 41 ]
42 42 },
43 43 {
44 44 "cell_type": "markdown",
45 45 "metadata": {},
46 46 "source": [
47 "To display widget A inside widget B, widget A must be a child of widget B. Only one instance of any particular model can be child of another. In other words, *widget A* cannot have *widget B* listed twice in it's children list.\n",
47 "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 48 "\n",
49 "Widgets that can contain other widgets have a `children` property. This property 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 (as seen below)."
49 "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 50 ]
51 51 },
52 52 {
53 53 "cell_type": "code",
54 54 "collapsed": false,
55 55 "input": [
56 "floatrange = widgets.FloatSliderWidget()\n",
56 "float_range = widgets.FloatSliderWidget()\n",
57 57 "string = widgets.TextBoxWidget(value='hi')\n",
58 "container = widgets.ContainerWidget(children=[floatrange, string])\n",
58 "container = widgets.ContainerWidget(children=[float_range, string])\n",
59 59 "\n",
60 60 "display(container) # Displays the `container` and all of it's children."
61 61 ],
62 62 "language": "python",
63 63 "metadata": {},
64 64 "outputs": [],
65 65 "prompt_number": 2
66 66 },
67 67 {
68 68 "cell_type": "markdown",
69 69 "metadata": {},
70 70 "source": [
71 71 "Children can also be added to parents after the parent has been displayed. The parent is responsible for rendering its children."
72 72 ]
73 73 },
74 74 {
75 75 "cell_type": "code",
76 76 "collapsed": false,
77 77 "input": [
78 78 "container = widgets.ContainerWidget()\n",
79 79 "display(container)\n",
80 80 "\n",
81 "intrange = widgets.IntSliderWidget()\n",
82 "container.children=[intrange]\n"
81 "int_range = widgets.IntSliderWidget()\n",
82 "container.children=[int_range]\n"
83 83 ],
84 84 "language": "python",
85 85 "metadata": {},
86 86 "outputs": [],
87 87 "prompt_number": 3
88 88 },
89 89 {
90 90 "cell_type": "heading",
91 91 "level": 1,
92 92 "metadata": {},
93 93 "source": [
94 94 "Visibility"
95 95 ]
96 96 },
97 97 {
98 98 "cell_type": "markdown",
99 99 "metadata": {},
100 100 "source": [
101 "Sometimes it's necessary to hide/show widget views in place, without having to redisplay the widget views. The `visibility` property of widgets can be used to hide/show widgets that have already been displayed (as seen below)."
101 "Sometimes it is necessary to hide or show widgets in place, without having to redisplay the widget.\n",
102 "The `visibility` property of widgets can be used to hide or show widgets that have already been displayed (as seen below)."
102 103 ]
103 104 },
104 105 {
105 106 "cell_type": "code",
106 107 "collapsed": false,
107 108 "input": [
108 109 "string = widgets.LatexWidget(value=\"Hello World!\")\n",
109 110 "display(string) "
110 111 ],
111 112 "language": "python",
112 113 "metadata": {},
113 114 "outputs": [],
114 115 "prompt_number": 4
115 116 },
116 117 {
117 118 "cell_type": "code",
118 119 "collapsed": false,
119 120 "input": [
120 121 "string.visible=False"
121 122 ],
122 123 "language": "python",
123 124 "metadata": {},
124 125 "outputs": [],
125 126 "prompt_number": 5
126 127 },
127 128 {
128 129 "cell_type": "code",
129 130 "collapsed": false,
130 131 "input": [
131 132 "string.visible=True"
132 133 ],
133 134 "language": "python",
134 135 "metadata": {},
135 136 "outputs": [],
136 137 "prompt_number": 6
137 138 },
138 139 {
139 140 "cell_type": "markdown",
140 141 "metadata": {},
141 142 "source": [
142 "In the example below, a form is rendered which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
143 "In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
143 144 ]
144 145 },
145 146 {
146 147 "cell_type": "code",
147 148 "collapsed": false,
148 149 "input": [
149 150 "form = widgets.ContainerWidget()\n",
150 151 "first = widgets.TextBoxWidget(description=\"First Name:\")\n",
151 152 "last = widgets.TextBoxWidget(description=\"Last Name:\")\n",
152 153 "\n",
153 154 "student = widgets.CheckBoxWidget(description=\"Student:\", value=False)\n",
154 155 "school_info = widgets.ContainerWidget(visible=False, children=[\n",
155 156 " widgets.TextBoxWidget(description=\"School:\"),\n",
156 157 " widgets.IntTextWidget(description=\"Grade:\", min=0, max=12)\n",
157 158 " ])\n",
158 159 "\n",
159 160 "pet = widgets.TextBoxWidget(description=\"Pet's Name:\")\n",
160 161 "form.children = [first, last, student, school_info, pet]\n",
161 162 "display(form)\n",
162 163 "\n",
163 164 "def on_student_toggle(name, value):\n",
164 165 " if value:\n",
165 166 " school_info.visible = True\n",
166 167 " else:\n",
167 168 " school_info.visible = False\n",
168 169 "student.on_trait_change(on_student_toggle, 'value')\n"
169 170 ],
170 171 "language": "python",
171 172 "metadata": {},
172 173 "outputs": [],
173 174 "prompt_number": 7
174 175 },
175 176 {
176 177 "cell_type": "markdown",
177 178 "metadata": {},
178 179 "source": [
179 180 "In [Part 4](Part 4 - Styles.ipynb) of this [series](index.ipynb), you will learn about widget styling."
180 181 ]
181 182 }
182 183 ],
183 184 "metadata": {}
184 185 }
185 186 ]
186 187 } No newline at end of file
@@ -1,355 +1,368 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 20 "[< Back to Part 3](Part 3 - Placement.ipynb) or [Index](index.ipynb)"
21 21 ]
22 22 },
23 23 {
24 24 "cell_type": "code",
25 25 "collapsed": false,
26 26 "input": [
27 27 "from IPython.html import widgets # Widget definitions\n",
28 28 "from IPython.display import display # Used to display widgets in the notebook"
29 29 ],
30 30 "language": "python",
31 31 "metadata": {},
32 32 "outputs": [],
33 33 "prompt_number": 1
34 34 },
35 35 {
36 36 "cell_type": "heading",
37 37 "level": 1,
38 38 "metadata": {},
39 39 "source": [
40 40 "CSS"
41 41 ]
42 42 },
43 43 {
44 44 "cell_type": "markdown",
45 45 "metadata": {},
46 46 "source": [
47 "When trying to design an attractive widget GUI, styling becomes important. Most Widgets views are DOM (document object model) elements that can be controlled with CSS. There are two helper methods defined on widget that allow the manipulation of the widget's CSS. The first is the `set_css` method, whos doc string is displayed below. This method allows one or more CSS attributes to be set at once. "
47 "When trying to design an attractive widget GUI, styling becomes important.\n",
48 "Most widget views are DOM (document object model) elements that can be controlled with CSS.\n",
49 "There are two helper methods that allow the manipulation of the widget's CSS.\n",
50 "The first is the `Widget.set_css` method.\n",
51 "This method allows one or more CSS attributes to be set at once. "
48 52 ]
49 53 },
50 54 {
51 55 "cell_type": "code",
52 56 "collapsed": false,
53 57 "input": [
54 58 "print(widgets.DOMWidget.set_css.__doc__)"
55 59 ],
56 60 "language": "python",
57 61 "metadata": {},
58 62 "outputs": [
59 63 {
60 64 "output_type": "stream",
61 65 "stream": "stdout",
62 66 "text": [
63 67 "Set one or more CSS properties of the widget.\n",
64 68 "\n",
65 69 " This function has two signatures:\n",
66 70 " - set_css(css_dict, selector='')\n",
67 71 " - set_css(key, value, selector='')\n",
68 72 "\n",
69 73 " Parameters\n",
70 74 " ----------\n",
71 75 " css_dict : dict\n",
72 76 " CSS key/value pairs to apply\n",
73 77 " key: unicode\n",
74 78 " CSS key\n",
75 " value\n",
79 " value:\n",
76 80 " CSS value\n",
77 " selector: unicode (optional)\n",
81 " selector: unicode (optional, kwarg only)\n",
78 82 " JQuery selector to use to apply the CSS key/value. If no selector \n",
79 83 " is provided, an empty selector is used. An empty selector makes the \n",
80 84 " front-end try to apply the css to a default element. The default\n",
81 85 " element is an attribute unique to each view, which is a DOM element\n",
82 86 " of the view that should be styled with common CSS (see \n",
83 87 " `$el_to_style` in the Javascript code).\n",
84 88 " \n"
85 89 ]
86 90 }
87 91 ],
88 92 "prompt_number": 2
89 93 },
90 94 {
91 95 "cell_type": "markdown",
92 96 "metadata": {},
93 97 "source": [
94 "The second is `get_css` which allows CSS attributes that have been set to be read. Note that this method will only read CSS attributes that have been set using the `set_css` method. `get_css`'s doc string is displayed below."
98 "The second is `get_css` which allows CSS attributesto be read.\n",
99 "Note that this method will only read CSS attributes that have been set using the `set_css` method."
95 100 ]
96 101 },
97 102 {
98 103 "cell_type": "code",
99 104 "collapsed": false,
100 105 "input": [
101 106 "print(widgets.DOMWidget.get_css.__doc__)"
102 107 ],
103 108 "language": "python",
104 109 "metadata": {},
105 110 "outputs": [
106 111 {
107 112 "output_type": "stream",
108 113 "stream": "stdout",
109 114 "text": [
110 115 "Get a CSS property of the widget.\n",
111 116 "\n",
112 117 " Note: This function does not actually request the CSS from the \n",
113 118 " front-end; Only properties that have been set with set_css can be read.\n",
114 119 "\n",
115 120 " Parameters\n",
116 121 " ----------\n",
117 122 " key: unicode\n",
118 123 " CSS key\n",
119 124 " selector: unicode (optional)\n",
120 125 " JQuery selector used when the CSS key/value was set.\n",
121 126 " \n"
122 127 ]
123 128 }
124 129 ],
125 130 "prompt_number": 3
126 131 },
127 132 {
128 133 "cell_type": "markdown",
129 134 "metadata": {},
130 135 "source": [
131 136 "Below is an example that applies CSS attributes to a container to emphasize text."
132 137 ]
133 138 },
134 139 {
135 140 "cell_type": "code",
136 141 "collapsed": false,
137 142 "input": [
138 143 "label = widgets.LatexWidget()\n",
139 144 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
140 145 "container = widgets.ContainerWidget(children=[label])\n",
141 146 "\n",
142 147 "# set_css used to set a single CSS attribute.\n",
143 148 "container.set_css('border', '3px solid black') # Border the container\n",
144 149 "\n",
145 150 "# set_css used to set multiple CSS attributes.\n",
146 151 "container.set_css({'padding': '6px', # Add padding to the container\n",
147 152 " 'background': 'yellow'}) # Fill the container yellow\n",
148 153 "\n",
149 154 "display(container)"
150 155 ],
151 156 "language": "python",
152 157 "metadata": {},
153 158 "outputs": [],
154 159 "prompt_number": 4
155 160 },
156 161 {
157 162 "cell_type": "heading",
158 163 "level": 1,
159 164 "metadata": {},
160 165 "source": [
161 "DOM Classes"
166 "CSS Classes"
162 167 ]
163 168 },
164 169 {
165 170 "cell_type": "markdown",
166 171 "metadata": {},
167 172 "source": [
168 "In some cases it's necessary to apply DOM classes to your widgets. DOM classes allow DOM elements to be indentified by Javascript and CSS. The notebook defines its own set of classes to stylize its elements. The `add_class` widget method allows you to add DOM classes to your widget's definition. The `add_class` method's doc string can be seen below."
173 "In some cases, it is necessary to apply CSS classes to your widgets.\n",
174 "CSS classes allow DOM elements to be indentified in Javascript and CSS.\n",
175 "The notebook defines its own set of classes to stylize its elements.\n",
176 "The `add_class` widget method allows you to add CSS classes to your widget."
169 177 ]
170 178 },
171 179 {
172 180 "cell_type": "code",
173 181 "collapsed": false,
174 182 "input": [
175 183 "print(widgets.DOMWidget.add_class.__doc__)"
176 184 ],
177 185 "language": "python",
178 186 "metadata": {},
179 187 "outputs": [
180 188 {
181 189 "output_type": "stream",
182 190 "stream": "stdout",
183 191 "text": [
184 192 "Add class[es] to a DOM element.\n",
185 193 "\n",
186 194 " Parameters\n",
187 195 " ----------\n",
188 196 " class_names: unicode or list\n",
189 197 " Class name(s) to add to the DOM element(s).\n",
190 198 " selector: unicode (optional)\n",
191 199 " JQuery selector to select the DOM element(s) that the class(es) will\n",
192 200 " be added to.\n",
193 201 " \n"
194 202 ]
195 203 }
196 204 ],
197 205 "prompt_number": 5
198 206 },
199 207 {
200 208 "cell_type": "markdown",
201 209 "metadata": {},
202 210 "source": [
203 "Since `add_class` if a DOM operation, **it will only affect widgets that have already been displayed**. `add_class` must be called after the widget has been displayed. Extending the example above, the corners of the container can be rounded by adding the `corner-all` notebook class to the container (as seen below). "
211 "Since `add_class` is a DOM operation, **it will only affect widgets that have already been displayed**.\n",
212 "`add_class` must be called after the widget has been displayed.\n",
213 "Extending the example above, the corners of the container can be rounded by adding the `corner-all` CSS class to the container."
204 214 ]
205 215 },
206 216 {
207 217 "cell_type": "code",
208 218 "collapsed": false,
209 219 "input": [
210 220 "container = widgets.ContainerWidget()\n",
211 221 "container.set_css({'border': '3px solid black',\n",
212 222 " 'padding': '6px', \n",
213 223 " 'background': 'yellow'}) \n",
214 224 "\n",
215 225 "label = widgets.LatexWidget()\n",
216 226 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
217 227 "container.children = [label]\n",
218 228 "display(container)\n",
219 229 "container.add_class('corner-all') # Must be called AFTER display"
220 230 ],
221 231 "language": "python",
222 232 "metadata": {},
223 233 "outputs": [],
224 234 "prompt_number": 6
225 235 },
226 236 {
227 237 "cell_type": "markdown",
228 238 "metadata": {},
229 239 "source": [
230 "The IPython notebook uses bootstrap for styling. The example above can be simplified by using a bootstrap class (as seen below). Bootstrap documentation can be found at http://getbootstrap.com/\u200e ."
240 "The IPython notebook uses [bootstrap](http://getbootstrap.com/\u200e) for styling.\n",
241 "The example above can be simplified by using a bootstrap class:"
231 242 ]
232 243 },
233 244 {
234 245 "cell_type": "code",
235 246 "collapsed": false,
236 247 "input": [
237 248 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
238 249 "display(label)\n",
239 250 "\n",
240 251 "# Apply twitter bootstrap alert class to the label.\n",
241 252 "label.add_class(\"alert\")"
242 253 ],
243 254 "language": "python",
244 255 "metadata": {},
245 256 "outputs": [],
246 257 "prompt_number": 7
247 258 },
248 259 {
249 260 "cell_type": "markdown",
250 261 "metadata": {},
251 262 "source": [
252 263 "The example below shows how bootstrap classes can be used to change button apearance."
253 264 ]
254 265 },
255 266 {
256 267 "cell_type": "code",
257 268 "collapsed": false,
258 269 "input": [
259 270 "# List of the bootstrap button styles\n",
260 271 "button_classes = ['Default', 'btn-primary', 'btn-info', 'btn-success', \n",
261 272 " 'btn-warning', 'btn-danger', 'btn-inverse', 'btn-link']\n",
262 273 "\n",
263 274 "# Create each button and apply the style. Also add margin to the buttons so they space\n",
264 275 "# themselves nicely.\n",
265 276 "for i in range(8):\n",
266 277 " button = widgets.ButtonWidget(description=button_classes[i])\n",
267 278 " button.set_css(\"margin\", \"5px\")\n",
268 279 " display(button)\n",
269 280 " if i > 0: # Don't add a class the first button.\n",
270 281 " button.add_class(button_classes[i])\n",
271 282 " "
272 283 ],
273 284 "language": "python",
274 285 "metadata": {},
275 286 "outputs": [],
276 287 "prompt_number": 8
277 288 },
278 289 {
279 290 "cell_type": "markdown",
280 291 "metadata": {},
281 292 "source": [
282 "It's also useful to be able to remove DOM classes from widgets. The `remove_class` widget method allows you to remove classes from widgets that have been displayed. Like `add_class`, it must be called after the widget has been displayed. The doc string of `remove_class` can be seen below."
293 "It is also useful to be able to remove CSS classes from widgets.\n",
294 "The `remove_class` method allows you to remove classes from widgets that have been displayed.\n",
295 "Like `add_class`, it must be called after the widget has been displayed."
283 296 ]
284 297 },
285 298 {
286 299 "cell_type": "code",
287 300 "collapsed": false,
288 301 "input": [
289 302 "print(widgets.DOMWidget.remove_class.__doc__)"
290 303 ],
291 304 "language": "python",
292 305 "metadata": {},
293 306 "outputs": [
294 307 {
295 308 "output_type": "stream",
296 309 "stream": "stdout",
297 310 "text": [
298 311 "Remove class[es] from a DOM element.\n",
299 312 "\n",
300 313 " Parameters\n",
301 314 " ----------\n",
302 315 " class_names: unicode or list\n",
303 316 " Class name(s) to remove from the DOM element(s).\n",
304 317 " selector: unicode (optional)\n",
305 318 " JQuery selector to select the DOM element(s) that the class(es) will\n",
306 319 " be removed from.\n",
307 320 " \n"
308 321 ]
309 322 }
310 323 ],
311 324 "prompt_number": 9
312 325 },
313 326 {
314 327 "cell_type": "markdown",
315 328 "metadata": {},
316 329 "source": [
317 330 "The example below animates an alert using different bootstrap styles."
318 331 ]
319 332 },
320 333 {
321 334 "cell_type": "code",
322 335 "collapsed": false,
323 336 "input": [
324 337 "import time\n",
325 338 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
326 339 "display(label)\n",
327 340 "\n",
328 341 "# Apply twitter bootstrap alert class to the label.\n",
329 342 "label.add_class(\"alert\")\n",
330 343 "\n",
331 344 "# Animate through additional bootstrap label styles 3 times\n",
332 345 "additional_alert_styles = ['alert-error', 'alert-info', 'alert-success']\n",
333 346 "for i in range(3 * len(additional_alert_styles)):\n",
334 347 " label.add_class(additional_alert_styles[i % 3])\n",
335 348 " label.remove_class(additional_alert_styles[(i-1) % 3])\n",
336 349 " time.sleep(1)\n",
337 350 " "
338 351 ],
339 352 "language": "python",
340 353 "metadata": {},
341 354 "outputs": [],
342 355 "prompt_number": 10
343 356 },
344 357 {
345 358 "cell_type": "markdown",
346 359 "metadata": {},
347 360 "source": [
348 361 "In [Part 5](Part 5 - Alignment.ipynb) of this [series](index.ipynb), you will learn about widget alignment."
349 362 ]
350 363 }
351 364 ],
352 365 "metadata": {}
353 366 }
354 367 ]
355 368 } No newline at end of file
@@ -1,327 +1,331 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 20 "[< Back to Part 4](Part 4 - Styles.ipynb) or [Index](index.ipynb)"
21 21 ]
22 22 },
23 23 {
24 24 "cell_type": "code",
25 25 "collapsed": false,
26 26 "input": [
27 27 "from IPython.html import widgets # Widget definitions\n",
28 28 "from IPython.display import display # Used to display widgets in the notebook"
29 29 ],
30 30 "language": "python",
31 31 "metadata": {},
32 32 "outputs": [],
33 33 "prompt_number": 1
34 34 },
35 35 {
36 36 "cell_type": "heading",
37 37 "level": 1,
38 38 "metadata": {},
39 39 "source": [
40 40 "Alignment"
41 41 ]
42 42 },
43 43 {
44 44 "cell_type": "markdown",
45 45 "metadata": {},
46 46 "source": [
47 "Most widgets have a `description` property which allows a label for the widget to be defined. The label of the widget has a fixed minimum width. The text of the label is always right aligned and the widget is left aligned (as seen below) "
47 "Most widgets have a `description` attribute, which allows a label for the widget to be defined.\n",
48 "The label of the widget has a fixed minimum width.\n",
49 "The text of the label is always right aligned and the widget is left aligned:"
48 50 ]
49 51 },
50 52 {
51 53 "cell_type": "code",
52 54 "collapsed": false,
53 55 "input": [
54 56 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
55 57 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
56 58 "display(widgets.TextBoxWidget(description=\"aaa:\"))"
57 59 ],
58 60 "language": "python",
59 61 "metadata": {},
60 62 "outputs": [],
61 "prompt_number": 2
63 "prompt_number": 3
62 64 },
63 65 {
64 66 "cell_type": "markdown",
65 67 "metadata": {},
66 68 "source": [
67 "If a label is longer than the minimum width, the widget is shifted to the right (as seen below)."
69 "If a label is longer than the minimum width, the widget is shifted to the right:"
68 70 ]
69 71 },
70 72 {
71 73 "cell_type": "code",
72 74 "collapsed": false,
73 75 "input": [
74 76 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
75 77 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
76 78 "display(widgets.TextBoxWidget(description=\"aaa:\"))\n",
77 79 "display(widgets.TextBoxWidget(description=\"aaaaaaaaaaaaaaaaaa:\"))"
78 80 ],
79 81 "language": "python",
80 82 "metadata": {},
81 83 "outputs": [],
82 "prompt_number": 3
84 "prompt_number": 4
83 85 },
84 86 {
85 87 "cell_type": "markdown",
86 88 "metadata": {},
87 89 "source": [
88 "If a `description` is not set for the widget, the label is not displayed (as seen below)."
90 "If a `description` is not set for the widget, the label is not displayed:"
89 91 ]
90 92 },
91 93 {
92 94 "cell_type": "code",
93 95 "collapsed": false,
94 96 "input": [
95 97 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
96 98 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
97 99 "display(widgets.TextBoxWidget(description=\"aaa:\"))\n",
98 100 "display(widgets.TextBoxWidget())"
99 101 ],
100 102 "language": "python",
101 103 "metadata": {},
102 104 "outputs": [],
103 "prompt_number": 4
105 "prompt_number": 5
104 106 },
105 107 {
106 108 "cell_type": "heading",
107 109 "level": 1,
108 110 "metadata": {},
109 111 "source": [
110 112 "Custom Alignment"
111 113 ]
112 114 },
113 115 {
114 116 "cell_type": "markdown",
115 117 "metadata": {},
116 118 "source": [
117 "`ContainerWidget`s allow for custom alignment of widgets. The `hbox` and `vbox` DOM classes cause the `ContainerWidget` to both horizontally and vertically align its children. The following example compares `vbox` to `hbox`."
119 "`ContainerWidget`s allow for custom alignment of widgets.\n",
120 "The `hbox` and `vbox` CSS classes cause the `ContainerWidget` to horizontally or vertically align its children."
118 121 ]
119 122 },
120 123 {
121 124 "cell_type": "code",
122 125 "collapsed": false,
123 126 "input": [
124 127 "child_style = {\n",
125 128 " 'background': '#77CC77',\n",
126 129 " 'padding': '25px',\n",
127 130 " 'margin': '5px',\n",
128 131 " 'font-size': 'xx-large',\n",
129 132 " 'color': 'white',\n",
130 133 "}\n",
131 134 "\n",
132 135 "def make_container(title):\n",
133 136 " header = widgets.LatexWidget(value=title) \n",
134 137 " display(header)\n",
135 138 " header.set_css({\n",
136 139 " 'font-size': '30pt',\n",
137 140 " 'margin-top': '40pt',\n",
138 141 " 'margin-bottom': '20pt',\n",
139 142 " })\n",
140 143 " \n",
141 144 " container = widgets.ContainerWidget()\n",
142 145 " container.set_css('background', '#999999')\n",
143 146 " display(container)\n",
144 147 " return container\n",
145 148 "\n",
146 149 "def fill_container(container):\n",
147 150 " components = []\n",
148 151 " for i in range(3):\n",
149 152 " components.append(widgets.LatexWidget(value=\"ABC\"[i]))\n",
150 153 " components[i].set_css(child_style)\n",
151 154 " container.children = components\n",
152 155 " \n",
153 156 "container = make_container('VBox')\n",
154 157 "container.add_class('vbox')\n",
155 158 "fill_container(container)\n",
156 159 "\n",
157 160 "container = make_container('HBox')\n",
158 161 "container.add_class('hbox')\n",
159 162 "fill_container(container)\n"
160 163 ],
161 164 "language": "python",
162 165 "metadata": {},
163 166 "outputs": [],
164 "prompt_number": 5
167 "prompt_number": 6
165 168 },
166 169 {
167 170 "cell_type": "markdown",
168 171 "metadata": {},
169 172 "source": [
170 "The `start`, `center`, and `end` DOM classes adjust the alignment of the widgets on the axis that they are being rendered on. Below is an example of the different alignments."
173 "The `start`, `center`, and `end` classes adjust the alignment of the widgets on the axis where they are being rendered.\n",
174 "Below is an example of the different alignments."
171 175 ]
172 176 },
173 177 {
174 178 "cell_type": "code",
175 179 "collapsed": false,
176 180 "input": [
177 181 "container = make_container('HBox Pack Start')\n",
178 182 "container.add_class('hbox')\n",
179 183 "container.add_class('start')\n",
180 184 "fill_container(container)\n",
181 185 " \n",
182 186 "container = make_container('HBox Pack Center')\n",
183 187 "container.add_class('hbox')\n",
184 188 "container.add_class('center')\n",
185 189 "fill_container(container)\n",
186 190 " \n",
187 191 "container = make_container('HBox Pack End')\n",
188 192 "container.add_class('hbox')\n",
189 193 "container.add_class('end')\n",
190 194 "fill_container(container)"
191 195 ],
192 196 "language": "python",
193 197 "metadata": {},
194 198 "outputs": [],
195 199 "prompt_number": 6
196 200 },
197 201 {
198 202 "cell_type": "markdown",
199 203 "metadata": {},
200 204 "source": [
201 205 "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."
202 206 ]
203 207 },
204 208 {
205 209 "cell_type": "code",
206 210 "collapsed": false,
207 211 "input": [
208 212 "def fill_container(container, flexes):\n",
209 213 " components = []\n",
210 214 " for i in range(len(flexes)):\n",
211 215 " components.append(widgets.ContainerWidget())\n",
212 216 " components[i].set_css(child_style)\n",
213 217 " \n",
214 218 " label = widgets.LatexWidget(value=str(flexes[i]))\n",
215 219 " components[i].children = [label]\n",
216 220 " container.children = components\n",
217 221 " \n",
218 222 " for i in range(len(flexes)):\n",
219 223 " if flexes[i] == 0:\n",
220 224 " components[i].add_class('box-flex0')\n",
221 225 " elif flexes[i] == 1:\n",
222 226 " components[i].add_class('box-flex1')\n",
223 227 " elif flexes[i] == 2:\n",
224 228 " components[i].add_class('box-flex2')\n",
225 229 " \n",
226 230 "container = make_container('Different Flex Configurations')\n",
227 231 "container.add_class('hbox')\n",
228 232 "fill_container(container, [0, 0, 0])\n",
229 233 " \n",
230 234 "container = make_container('')\n",
231 235 "container.add_class('hbox')\n",
232 236 "fill_container(container, [0, 0, 1])\n",
233 237 " \n",
234 238 "container = make_container('')\n",
235 239 "container.add_class('hbox')\n",
236 240 "fill_container(container, [0, 1, 1])\n",
237 241 " \n",
238 242 "container = make_container('')\n",
239 243 "container.add_class('hbox')\n",
240 244 "fill_container(container, [0, 2, 2])\n",
241 245 " \n",
242 246 "container = make_container('')\n",
243 247 "container.add_class('hbox')\n",
244 248 "fill_container(container, [0, 1, 2])\n",
245 249 " \n",
246 250 "container = make_container('')\n",
247 251 "container.add_class('hbox')\n",
248 252 "fill_container(container, [1, 1, 2])"
249 253 ],
250 254 "language": "python",
251 255 "metadata": {},
252 256 "outputs": [],
253 257 "prompt_number": 7
254 258 },
255 259 {
256 260 "cell_type": "markdown",
257 261 "metadata": {},
258 262 "source": [
259 263 "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."
260 264 ]
261 265 },
262 266 {
263 267 "cell_type": "code",
264 268 "collapsed": false,
265 269 "input": [
266 270 "def fill_container(container):\n",
267 271 " components = []\n",
268 272 " for i in range(3):\n",
269 273 " components.append(widgets.LatexWidget(parent=container, value=\"ABC\"[i]))\n",
270 274 " components[i].set_css(child_style)\n",
271 275 " components[i].set_css('height', str((i+1) * 50) + 'px')\n",
272 276 " container.children = components\n",
273 277 "\n",
274 278 "container = make_container('HBox Align Start')\n",
275 279 "container.add_class(\"hbox\")\n",
276 280 "container.add_class(\"align-start\")\n",
277 281 "fill_container(container)\n",
278 282 " \n",
279 283 "container = make_container('HBox Align Center')\n",
280 284 "container.add_class(\"hbox\")\n",
281 285 "container.add_class(\"align-center\")\n",
282 286 "fill_container(container)\n",
283 287 " \n",
284 288 "container = make_container('HBox Align End')\n",
285 289 "container.add_class(\"hbox\")\n",
286 290 "container.add_class(\"align-end\")\n",
287 291 "fill_container(container)"
288 292 ],
289 293 "language": "python",
290 294 "metadata": {},
291 295 "outputs": [],
292 296 "prompt_number": 8
293 297 },
294 298 {
295 299 "cell_type": "markdown",
296 300 "metadata": {},
297 301 "source": [
298 302 "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."
299 303 ]
300 304 },
301 305 {
302 306 "cell_type": "code",
303 307 "collapsed": false,
304 308 "input": [
305 309 "container = widgets.ContainerWidget()\n",
306 310 "container.children=[widgets.FloatSliderWidget(orientation='vertical', description=str(i+1), value=50.0) \n",
307 311 " for i in range(15)]\n",
308 312 "display(container)\n",
309 313 "container.add_class('hbox')"
310 314 ],
311 315 "language": "python",
312 316 "metadata": {},
313 317 "outputs": [],
314 318 "prompt_number": 9
315 319 },
316 320 {
317 321 "cell_type": "markdown",
318 322 "metadata": {},
319 323 "source": [
320 324 "In [Part 6](Part 6 - Custom Widget.ipynb) of this [series](index.ipynb), you will learn how to create your own custom widget."
321 325 ]
322 326 }
323 327 ],
324 328 "metadata": {}
325 329 }
326 330 ]
327 331 } No newline at end of file
@@ -1,1022 +1,1058 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 20 "[< Back to Part 5](Part 5 - Alignment.ipynb) or [Index](index.ipynb)\n",
21 21 "\n",
22 "Before reading, the author recommends the reader to review\n",
22 "Before reading, make sure to review\n",
23 23 "\n",
24 24 "- [MVC prgramming](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)\n",
25 25 "- [Backbone.js](https://www.codeschool.com/courses/anatomy-of-backbonejs)\n",
26 26 "- [The widget IPEP](https://github.com/ipython/ipython/wiki/IPEP-23%3A-Backbone.js-Widgets)\n",
27 27 "- [The original widget PR discussion](https://github.com/ipython/ipython/pull/4374)"
28 28 ]
29 29 },
30 30 {
31 31 "cell_type": "code",
32 32 "collapsed": false,
33 33 "input": [
34 34 "from __future__ import print_function # For py 2.7 compat\n",
35 35 "\n",
36 "from IPython.html import widgets # Widget definitions.\n",
37 "from IPython.display import display # Used to display widgets in the notebook.\n",
38 "from IPython.utils.traitlets import Unicode # Used to declare properties of our widget."
36 "from IPython.html import widgets # Widget definitions\n",
37 "from IPython.display import display # Used to display widgets in the notebook\n",
38 "from IPython.utils.traitlets import Unicode # Used to declare attributes of our widget"
39 39 ],
40 40 "language": "python",
41 41 "metadata": {},
42 42 "outputs": [],
43 43 "prompt_number": 1
44 44 },
45 45 {
46 46 "cell_type": "heading",
47 47 "level": 1,
48 48 "metadata": {},
49 49 "source": [
50 50 "Abstract"
51 51 ]
52 52 },
53 53 {
54 54 "cell_type": "markdown",
55 55 "metadata": {},
56 56 "source": [
57 "This notebook implements a custom date picker widget. The purpose of this notebook is to demonstrate the widget creation process. To create a custom widget, custom Python and JavaScript is required."
57 "This notebook implements a custom date picker widget,\n",
58 "in order to demonstrate the widget creation process.\n",
59 "\n",
60 "To create a custom widget, both Python and JavaScript code is required."
58 61 ]
59 62 },
60 63 {
61 64 "cell_type": "heading",
62 65 "level": 1,
63 66 "metadata": {},
64 67 "source": [
65 68 "Section 1 - Basics"
66 69 ]
67 70 },
68 71 {
69 72 "cell_type": "heading",
70 73 "level": 2,
71 74 "metadata": {},
72 75 "source": [
73 76 "Python"
74 77 ]
75 78 },
76 79 {
77 80 "cell_type": "markdown",
78 81 "metadata": {},
79 82 "source": [
80 "When starting a project like this, it is often easiest to make an overly simplified base to verify that the underlying framework is working as expected. To start we will create an empty widget and make sure that it can be rendered. The first step is to create the widget in Python."
83 "When starting a project like this, it is often easiest to make a simple base implementation,\n",
84 "to verify that the underlying framework is working as expected.\n",
85 "To start, we will create an empty widget and make sure that it can be rendered.\n",
86 "The first step is to define the widget in Python."
81 87 ]
82 88 },
83 89 {
84 90 "cell_type": "code",
85 91 "collapsed": false,
86 92 "input": [
87 93 "class DateWidget(widgets.DOMWidget):\n",
88 94 " _view_name = Unicode('DatePickerView', sync=True)"
89 95 ],
90 96 "language": "python",
91 97 "metadata": {},
92 98 "outputs": [],
93 99 "prompt_number": 2
94 100 },
95 101 {
96 102 "cell_type": "markdown",
97 103 "metadata": {},
98 104 "source": [
99 "Our widget inherits from `widgets.DOMWidget` since it is intended that it will be displayed in the notebook directly. The `_view_name` trait is specially named, the widget framework will read the `_view_name` trait to determine what Backbone view the widget is associated with. **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."
105 "Our widget inherits from `widgets.DOMWidget` since it is intended that it will be displayed in the notebook directly.\n",
106 "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 "**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."
100 108 ]
101 109 },
102 110 {
103 111 "cell_type": "heading",
104 112 "level": 2,
105 113 "metadata": {},
106 114 "source": [
107 115 "JavaScript"
108 116 ]
109 117 },
110 118 {
111 119 "cell_type": "markdown",
112 120 "metadata": {},
113 121 "source": [
114 "In the IPython notebook [require.js](http://requirejs.org/) is used to load JavaScript dependencies. All IPython widget code depends on `notebook/js/widgets/widget.js`. In it the base widget model and base view are defined. We need to use require.js to include this file:"
122 "In the IPython notebook [require.js](http://requirejs.org/) is used to load JavaScript dependencies.\n",
123 "All IPython widget code depends on `notebook/js/widgets/widget.js`,\n",
124 "where the base widget model and base view are defined.\n",
125 "We use require.js to load this file:"
115 126 ]
116 127 },
117 128 {
118 129 "cell_type": "code",
119 130 "collapsed": false,
120 131 "input": [
121 132 "%%javascript\n",
122 133 "\n",
123 134 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
124 135 "\n",
125 136 "});"
126 137 ],
127 138 "language": "python",
128 139 "metadata": {},
129 140 "outputs": [
130 141 {
131 142 "javascript": [
132 143 "\n",
133 144 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
134 145 "\n",
135 146 "});"
136 147 ],
137 148 "metadata": {},
138 149 "output_type": "display_data",
139 150 "text": [
140 "<IPython.core.display.Javascript at 0x2a2e0d0>"
151 "<IPython.core.display.Javascript at 0x109491690>"
141 152 ]
142 153 }
143 154 ],
144 155 "prompt_number": 3
145 156 },
146 157 {
147 158 "cell_type": "markdown",
148 159 "metadata": {},
149 160 "source": [
150 "Now we need to define a view that can be used to represent the model. To do this, the `IPython.DOMWidgetView` is extended. A render function must be defined. The render function is used to render a widget view instance to the DOM. For now the render function renders a div that contains the text *Hello World!* Lastly, the view needs to be registered with the widget manager.\n",
161 "Now we need to define a view that can be used to represent the model.\n",
162 "To do this, the `IPython.DOMWidgetView` is extended.\n",
163 "**A render function must be defined**.\n",
164 "The render function is used to render a widget view instance to the DOM.\n",
165 "For now, the render function renders a div that contains the text *Hello World!*\n",
166 "Lastly, the view needs to be registered with the widget manager.\n",
151 167 "\n",
152 168 "**Final JavaScript code below:**"
153 169 ]
154 170 },
155 171 {
156 172 "cell_type": "code",
157 173 "collapsed": false,
158 174 "input": [
159 175 "%%javascript\n",
160 176 "\n",
161 177 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
162 178 " \n",
163 179 " // Define the DatePickerView\n",
164 180 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
165 181 " render: function(){ this.$el.text('Hello World!'); },\n",
166 182 " });\n",
167 183 " \n",
168 184 " // Register the DatePickerView with the widget manager.\n",
169 185 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
170 186 "});"
171 187 ],
172 188 "language": "python",
173 189 "metadata": {},
174 190 "outputs": [
175 191 {
176 192 "javascript": [
177 193 "\n",
178 194 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
179 195 " \n",
180 196 " // Define the DatePickerView\n",
181 197 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
182 198 " render: function(){ this.$el.text('Hello World!'); },\n",
183 199 " });\n",
184 200 " \n",
185 201 " // Register the DatePickerView with the widget manager.\n",
186 202 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
187 203 "});"
188 204 ],
189 205 "metadata": {},
190 206 "output_type": "display_data",
191 207 "text": [
192 "<IPython.core.display.Javascript at 0x2a2e3d0>"
208 "<IPython.core.display.Javascript at 0x1094917d0>"
193 209 ]
194 210 }
195 211 ],
196 212 "prompt_number": 4
197 213 },
198 214 {
199 215 "cell_type": "heading",
200 216 "level": 2,
201 217 "metadata": {},
202 218 "source": [
203 219 "Test"
204 220 ]
205 221 },
206 222 {
207 223 "cell_type": "markdown",
208 224 "metadata": {},
209 225 "source": [
210 "To test, create the widget the same way that the other widgets are created."
226 "To test what we have so far, create the widget, just like you would the builtin widgets:"
211 227 ]
212 228 },
213 229 {
214 230 "cell_type": "code",
215 231 "collapsed": false,
216 232 "input": [
217 233 "DateWidget()"
218 234 ],
219 235 "language": "python",
220 236 "metadata": {},
221 237 "outputs": [],
222 238 "prompt_number": 5
223 239 },
224 240 {
225 241 "cell_type": "heading",
226 242 "level": 1,
227 243 "metadata": {},
228 244 "source": [
229 245 "Section 2 - Something useful"
230 246 ]
231 247 },
232 248 {
233 249 "cell_type": "heading",
234 250 "level": 2,
235 251 "metadata": {},
236 252 "source": [
237 253 "Python"
238 254 ]
239 255 },
240 256 {
241 257 "cell_type": "markdown",
242 258 "metadata": {},
243 259 "source": [
244 "In the last section we created a simple widget that displayed *Hello World!* To make an actual date widget, we need to add a property that will be synced between the Python model and the JavaScript model. The new property must be a traitlet property so the widget machinery can automatically handle it. The property needs to be constructed with a `sync=True` keyword argument so the widget machinery knows to syncronize it with the front-end. Adding this to the code from the last section:"
260 "In the last section we created a simple widget that displayed *Hello World!*\n",
261 "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 "The new attribute must be a traitlet, so the widget machinery can handle it.\n",
263 "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 "Adding this to the code from the last section:"
245 265 ]
246 266 },
247 267 {
248 268 "cell_type": "code",
249 269 "collapsed": false,
250 270 "input": [
251 271 "class DateWidget(widgets.DOMWidget):\n",
252 272 " _view_name = Unicode('DatePickerView', sync=True)\n",
253 273 " value = Unicode(sync=True)"
254 274 ],
255 275 "language": "python",
256 276 "metadata": {},
257 277 "outputs": [],
258 278 "prompt_number": 6
259 279 },
260 280 {
261 281 "cell_type": "heading",
262 282 "level": 2,
263 283 "metadata": {},
264 284 "source": [
265 285 "JavaScript"
266 286 ]
267 287 },
268 288 {
269 289 "cell_type": "markdown",
270 290 "metadata": {},
271 291 "source": [
272 "In the JavaScript there is no need to define counterparts to the traitlets. When the JavaScript model is created for the first time, it copies all of the traitlet `sync=True` attributes from the Python model. We need to replace *Hello World!* with an actual HTML date picker widget."
292 "In the JavaScript, there is no need to define counterparts to the traitlets.\n",
293 "When the JavaScript model is created for the first time,\n",
294 "it copies all of the traitlet `sync=True` attributes from the Python model.\n",
295 "We need to replace *Hello World!* with an actual HTML date picker widget."
273 296 ]
274 297 },
275 298 {
276 299 "cell_type": "code",
277 300 "collapsed": false,
278 301 "input": [
279 302 "%%javascript\n",
280 303 "\n",
281 304 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
282 305 " \n",
283 306 " // Define the DatePickerView\n",
284 307 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
285 308 " render: function(){\n",
286 309 " \n",
287 310 " // Create the date picker control.\n",
288 311 " this.$date = $('<input />')\n",
289 312 " .attr('type', 'date')\n",
290 313 " .appendTo(this.$el);\n",
291 314 " },\n",
292 315 " });\n",
293 316 " \n",
294 317 " // Register the DatePickerView with the widget manager.\n",
295 318 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
296 319 "});"
297 320 ],
298 321 "language": "python",
299 322 "metadata": {},
300 323 "outputs": [
301 324 {
302 325 "javascript": [
303 326 "\n",
304 327 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
305 328 " \n",
306 329 " // Define the DatePickerView\n",
307 330 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
308 331 " render: function(){\n",
309 332 " \n",
310 333 " // Create the date picker control.\n",
311 334 " this.$date = $('<input />')\n",
312 335 " .attr('type', 'date')\n",
313 336 " .appendTo(this.$el);\n",
314 337 " },\n",
315 338 " });\n",
316 339 " \n",
317 340 " // Register the DatePickerView with the widget manager.\n",
318 341 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
319 342 "});"
320 343 ],
321 344 "metadata": {},
322 345 "output_type": "display_data",
323 346 "text": [
324 "<IPython.core.display.Javascript at 0x2a2e6d0>"
347 "<IPython.core.display.Javascript at 0x109491750>"
325 348 ]
326 349 }
327 350 ],
328 351 "prompt_number": 7
329 352 },
330 353 {
331 354 "cell_type": "markdown",
332 355 "metadata": {},
333 356 "source": [
334 357 "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."
335 358 ]
336 359 },
337 360 {
338 361 "cell_type": "code",
339 362 "collapsed": false,
340 363 "input": [
341 364 "%%javascript\n",
342 365 "\n",
343 366 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
344 367 " \n",
345 368 " // Define the DatePickerView\n",
346 369 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
347 370 " render: function(){\n",
348 371 " \n",
349 372 " // Create the date picker control.\n",
350 373 " this.$date = $('<input />')\n",
351 374 " .attr('type', 'date')\n",
352 375 " .appendTo(this.$el);\n",
353 376 " },\n",
354 377 " \n",
355 378 " update: function() {\n",
356 379 " \n",
357 380 " // Set the value of the date control and then call base.\n",
358 381 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
359 382 " return DatePickerView.__super__.update.apply(this);\n",
360 383 " },\n",
361 384 " });\n",
362 385 " \n",
363 386 " // Register the DatePickerView with the widget manager.\n",
364 387 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
365 388 "});"
366 389 ],
367 390 "language": "python",
368 391 "metadata": {},
369 392 "outputs": [
370 393 {
371 394 "javascript": [
372 395 "\n",
373 396 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
374 397 " \n",
375 398 " // Define the DatePickerView\n",
376 399 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
377 400 " render: function(){\n",
378 401 " \n",
379 402 " // Create the date picker control.\n",
380 403 " this.$date = $('<input />')\n",
381 404 " .attr('type', 'date')\n",
382 405 " .appendTo(this.$el);\n",
383 406 " },\n",
384 407 " \n",
385 408 " update: function() {\n",
386 409 " \n",
387 410 " // Set the value of the date control and then call base.\n",
388 411 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
389 412 " return DatePickerView.__super__.update.apply(this);\n",
390 413 " },\n",
391 414 " });\n",
392 415 " \n",
393 416 " // Register the DatePickerView with the widget manager.\n",
394 417 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
395 418 "});"
396 419 ],
397 420 "metadata": {},
398 421 "output_type": "display_data",
399 422 "text": [
400 "<IPython.core.display.Javascript at 0x2a2e090>"
423 "<IPython.core.display.Javascript at 0x109491750>"
401 424 ]
402 425 }
403 426 ],
404 427 "prompt_number": 8
405 428 },
406 429 {
407 430 "cell_type": "markdown",
408 431 "metadata": {},
409 432 "source": [
410 "To get the changed value from the front-end to publish itself to the back-end, we need to listen to the change event triggered by the HTM date control and set the value in the model. After the date change event fires and the new value is set in the model, it's very important that we call `this.touch()` to let the widget machinery know which view changed the model. This is important because the widget machinery needs to know which cell to route the message callbacks to.\n",
433 "To get the changed value from the frontend to publish itself to the backend,\n",
434 "we need to listen to the change event triggered by the HTM date control and set the value in the model.\n",
435 "After the date change event fires and the new value is set in the model,\n",
436 "it is very important that we call `this.touch()` to let the widget machinery know which view changed the model.\n",
437 "This is important because the widget machinery needs to know which cell to route the message callbacks to.\n",
411 438 "\n",
412 439 "**Final JavaScript code below:**"
413 440 ]
414 441 },
415 442 {
416 443 "cell_type": "code",
417 444 "collapsed": false,
418 445 "input": [
419 446 "%%javascript\n",
420 447 "\n",
421 448 "\n",
422 449 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
423 450 " \n",
424 451 " // Define the DatePickerView\n",
425 452 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
426 453 " render: function(){\n",
427 454 " \n",
428 455 " // Create the date picker control.\n",
429 456 " this.$date = $('<input />')\n",
430 457 " .attr('type', 'date')\n",
431 458 " .appendTo(this.$el);\n",
432 459 " },\n",
433 460 " \n",
434 461 " update: function() {\n",
435 462 " \n",
436 463 " // Set the value of the date control and then call base.\n",
437 464 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
438 465 " return DatePickerView.__super__.update.apply(this);\n",
439 466 " },\n",
440 467 " \n",
441 468 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
442 469 " events: {\"change\": \"handle_date_change\"},\n",
443 470 " \n",
444 471 " // Callback for when the date is changed.\n",
445 472 " handle_date_change: function(event) {\n",
446 473 " this.model.set('value', this.$date.val());\n",
447 474 " this.touch();\n",
448 475 " },\n",
449 476 " });\n",
450 477 " \n",
451 478 " // Register the DatePickerView with the widget manager.\n",
452 479 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
453 480 "});"
454 481 ],
455 482 "language": "python",
456 483 "metadata": {},
457 484 "outputs": [
458 485 {
459 486 "javascript": [
460 487 "\n",
461 488 "\n",
462 489 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
463 490 " \n",
464 491 " // Define the DatePickerView\n",
465 492 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
466 493 " render: function(){\n",
467 494 " \n",
468 495 " // Create the date picker control.\n",
469 496 " this.$date = $('<input />')\n",
470 497 " .attr('type', 'date')\n",
471 498 " .appendTo(this.$el);\n",
472 499 " },\n",
473 500 " \n",
474 501 " update: function() {\n",
475 502 " \n",
476 503 " // Set the value of the date control and then call base.\n",
477 504 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
478 505 " return DatePickerView.__super__.update.apply(this);\n",
479 506 " },\n",
480 507 " \n",
481 508 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
482 509 " events: {\"change\": \"handle_date_change\"},\n",
483 510 " \n",
484 511 " // Callback for when the date is changed.\n",
485 512 " handle_date_change: function(event) {\n",
486 513 " this.model.set('value', this.$date.val());\n",
487 514 " this.touch();\n",
488 515 " },\n",
489 516 " });\n",
490 517 " \n",
491 518 " // Register the DatePickerView with the widget manager.\n",
492 519 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
493 520 "});"
494 521 ],
495 522 "metadata": {},
496 523 "output_type": "display_data",
497 524 "text": [
498 "<IPython.core.display.Javascript at 0x2a2e750>"
525 "<IPython.core.display.Javascript at 0x109491b10>"
499 526 ]
500 527 }
501 528 ],
502 529 "prompt_number": 9
503 530 },
504 531 {
505 532 "cell_type": "heading",
506 533 "level": 2,
507 534 "metadata": {},
508 535 "source": [
509 536 "Test"
510 537 ]
511 538 },
512 539 {
513 540 "cell_type": "markdown",
514 541 "metadata": {},
515 542 "source": [
516 543 "To test, create the widget the same way that the other widgets are created."
517 544 ]
518 545 },
519 546 {
520 547 "cell_type": "code",
521 548 "collapsed": false,
522 549 "input": [
523 550 "my_widget = DateWidget()\n",
524 551 "display(my_widget)"
525 552 ],
526 553 "language": "python",
527 554 "metadata": {},
528 555 "outputs": [],
529 556 "prompt_number": 10
530 557 },
531 558 {
532 559 "cell_type": "markdown",
533 560 "metadata": {},
534 561 "source": [
535 562 "Display the widget again to make sure that both views remain in sync."
536 563 ]
537 564 },
538 565 {
539 566 "cell_type": "code",
540 567 "collapsed": false,
541 568 "input": [
542 569 "my_widget"
543 570 ],
544 571 "language": "python",
545 572 "metadata": {},
546 573 "outputs": [],
547 574 "prompt_number": 11
548 575 },
549 576 {
550 577 "cell_type": "markdown",
551 578 "metadata": {},
552 579 "source": [
553 580 "Read the date from Python"
554 581 ]
555 582 },
556 583 {
557 584 "cell_type": "code",
558 585 "collapsed": false,
559 586 "input": [
560 587 "my_widget.value"
561 588 ],
562 589 "language": "python",
563 590 "metadata": {},
564 591 "outputs": [
565 592 {
566 593 "metadata": {},
567 594 "output_type": "pyout",
568 595 "prompt_number": 12,
569 596 "text": [
570 "u'2014-01-01'"
597 "u''"
571 598 ]
572 599 }
573 600 ],
574 601 "prompt_number": 12
575 602 },
576 603 {
577 604 "cell_type": "markdown",
578 605 "metadata": {},
579 606 "source": [
580 607 "Set the date from Python"
581 608 ]
582 609 },
583 610 {
584 611 "cell_type": "code",
585 612 "collapsed": false,
586 613 "input": [
587 "my_widget.value = \"1998-12-01\" # December 1st, 1999"
614 "my_widget.value = \"1998-12-01\" # December 1st, 1998"
588 615 ],
589 616 "language": "python",
590 617 "metadata": {},
591 618 "outputs": [],
592 619 "prompt_number": 13
593 620 },
594 621 {
595 622 "cell_type": "heading",
596 623 "level": 1,
597 624 "metadata": {},
598 625 "source": [
599 626 "Section 3 - Extra credit"
600 627 ]
601 628 },
602 629 {
603 630 "cell_type": "markdown",
604 631 "metadata": {},
605 632 "source": [
606 633 "The 3rd party `dateutil` library is required to continue. https://pypi.python.org/pypi/python-dateutil"
607 634 ]
608 635 },
609 636 {
610 637 "cell_type": "code",
611 638 "collapsed": false,
612 639 "input": [
613 640 "# Import the dateutil library to parse date strings.\n",
614 641 "from dateutil import parser"
615 642 ],
616 643 "language": "python",
617 644 "metadata": {},
618 645 "outputs": [],
619 646 "prompt_number": 14
620 647 },
621 648 {
622 649 "cell_type": "markdown",
623 650 "metadata": {},
624 651 "source": [
625 "In the last section we created a fully working date picker widget. Now we will add custom validation and support for labels. Currently only the ISO date format \"YYYY-MM-DD\" is supported. We will add support for all of the date formats recognized by the 3rd party Python dateutil library."
652 "In the last section we created a fully working date picker widget.\n",
653 "Now we will add custom validation and support for labels.\n",
654 "So far, only the ISO date format \"YYYY-MM-DD\" is supported.\n",
655 "Now, we will add support for all of the date formats recognized by the Python dateutil library."
626 656 ]
627 657 },
628 658 {
629 659 "cell_type": "heading",
630 660 "level": 2,
631 661 "metadata": {},
632 662 "source": [
633 663 "Python"
634 664 ]
635 665 },
636 666 {
637 667 "cell_type": "markdown",
638 668 "metadata": {},
639 669 "source": [
640 "The standard property name used for widget labels is `description`. In the code block below, `description` has been added to the Python widget."
670 "The standard property name used for widget labels is `description`.\n",
671 "In the code block below, `description` has been added to the Python widget."
641 672 ]
642 673 },
643 674 {
644 675 "cell_type": "code",
645 676 "collapsed": false,
646 677 "input": [
647 678 "class DateWidget(widgets.DOMWidget):\n",
648 679 " _view_name = Unicode('DatePickerView', sync=True)\n",
649 680 " value = Unicode(sync=True)\n",
650 681 " description = Unicode(sync=True)"
651 682 ],
652 683 "language": "python",
653 684 "metadata": {},
654 685 "outputs": [],
655 686 "prompt_number": 15
656 687 },
657 688 {
658 689 "cell_type": "markdown",
659 690 "metadata": {},
660 691 "source": [
661 "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. We can take advantage of this to perform validation and parsing of different date string formats. Below a method that listens to value has been added to the DateWidget."
692 "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 "We can take advantage of this to perform validation and parsing of different date string formats.\n",
694 "Below, a method that listens to value has been added to the DateWidget."
662 695 ]
663 696 },
664 697 {
665 698 "cell_type": "code",
666 699 "collapsed": false,
667 700 "input": [
668 701 "class DateWidget(widgets.DOMWidget):\n",
669 702 " _view_name = Unicode('DatePickerView', sync=True)\n",
670 703 " value = Unicode(sync=True)\n",
671 704 " description = Unicode(sync=True)\n",
672 705 "\n",
673 706 " # This function automatically gets called by the traitlet machinery when\n",
674 707 " # value is modified because of this function's name.\n",
675 708 " def _value_changed(self, name, old_value, new_value):\n",
676 709 " pass"
677 710 ],
678 711 "language": "python",
679 712 "metadata": {},
680 713 "outputs": [],
681 714 "prompt_number": 16
682 715 },
683 716 {
684 717 "cell_type": "markdown",
685 718 "metadata": {},
686 719 "source": [
687 "Now the function that parses the date string and only sets it in the correct format can be added."
720 "Now the function parses the date string,\n",
721 "and only sets the value in the correct format."
688 722 ]
689 723 },
690 724 {
691 725 "cell_type": "code",
692 726 "collapsed": false,
693 727 "input": [
694 728 "class DateWidget(widgets.DOMWidget):\n",
695 729 " _view_name = Unicode('DatePickerView', sync=True)\n",
696 730 " value = Unicode(sync=True)\n",
697 731 " description = Unicode(sync=True)\n",
698 732 " \n",
699 733 " # This function automatically gets called by the traitlet machinery when\n",
700 734 " # value is modified because of this function's name.\n",
701 735 " def _value_changed(self, name, old_value, new_value):\n",
702 736 " \n",
703 737 " # Parse the date time value.\n",
704 738 " try:\n",
705 739 " parsed_date = parser.parse(new_value)\n",
706 740 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
707 741 " except:\n",
708 742 " parsed_date_string = ''\n",
709 743 " \n",
710 744 " # Set the parsed date string if the current date string is different.\n",
711 745 " if self.value != parsed_date_string:\n",
712 746 " self.value = parsed_date_string"
713 747 ],
714 748 "language": "python",
715 749 "metadata": {},
716 750 "outputs": [],
717 751 "prompt_number": 17
718 752 },
719 753 {
720 754 "cell_type": "markdown",
721 755 "metadata": {},
722 756 "source": [
723 "Finally, a `CallbackDispatcher` is added so the user can perform custom validation. If any one of the callbacks registered with the dispatcher returns False, the new date time is not set.\n",
757 "Finally, a `CallbackDispatcher` is added so the user can perform custom validation.\n",
758 "If any one of the callbacks registered with the dispatcher returns False,\n",
759 "the new date is not set.\n",
724 760 "\n",
725 761 "**Final Python code below:**"
726 762 ]
727 763 },
728 764 {
729 765 "cell_type": "code",
730 766 "collapsed": false,
731 767 "input": [
732 768 "class DateWidget(widgets.DOMWidget):\n",
733 769 " _view_name = Unicode('DatePickerView', sync=True)\n",
734 770 " value = Unicode(sync=True)\n",
735 771 " description = Unicode(sync=True)\n",
736 772 " \n",
737 773 " def __init__(self, **kwargs):\n",
738 774 " super(DateWidget, self).__init__(**kwargs)\n",
739 775 " \n",
740 " # Specify the number of positional arguments supported. For \n",
741 " # validation we only are worried about one parameter, the\n",
742 " # new value that should be validated.\n",
743 " self.validation = widgets.CallbackDispatcher(acceptable_nargs=[1])\n",
776 " self.validate = widgets.CallbackDispatcher()\n",
744 777 " \n",
745 778 " # This function automatically gets called by the traitlet machinery when\n",
746 779 " # value is modified because of this function's name.\n",
747 780 " def _value_changed(self, name, old_value, new_value):\n",
748 781 " \n",
749 782 " # Parse the date time value.\n",
750 783 " try:\n",
751 784 " parsed_date = parser.parse(new_value)\n",
752 785 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
753 786 " except:\n",
754 787 " parsed_date_string = ''\n",
755 788 " \n",
756 789 " # Set the parsed date string if the current date string is different.\n",
757 790 " if old_value != new_value:\n",
758 " validation = self.validation(parsed_date)\n",
759 " if validation is None or validation == True:\n",
791 " valid = self.validate(parsed_date)\n",
792 " if valid in (None, True):\n",
760 793 " self.value = parsed_date_string\n",
761 794 " else:\n",
762 795 " self.value = old_value\n",
763 796 " self.send_state() # The traitlet event won't fire since the value isn't changing.\n",
764 797 " # We need to force the back-end to send the front-end the state\n",
765 798 " # to make sure that the date control date doesn't change."
766 799 ],
767 800 "language": "python",
768 801 "metadata": {},
769 802 "outputs": [],
770 803 "prompt_number": 18
771 804 },
772 805 {
773 806 "cell_type": "heading",
774 807 "level": 2,
775 808 "metadata": {},
776 809 "source": [
777 810 "JavaScript"
778 811 ]
779 812 },
780 813 {
781 814 "cell_type": "markdown",
782 815 "metadata": {},
783 816 "source": [
784 "Using the Javascript code from the last section, we add a label to the date time object. The label is a div with the `widget-hlabel` class applied to it. The `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. Similar to the `widget-hlabel` class is the `widget-hbox-single` class. The `widget-hbox-single` class applies special styling to widget containers that store a single line horizontal widget. \n",
817 "Using the Javascript code from the last section,\n",
818 "we add a label to the date time object.\n",
819 "The label is a div with the `widget-hlabel` class applied to it.\n",
820 "`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 "Similar to the `widget-hlabel` class is the `widget-hbox-single` class.\n",
822 "The `widget-hbox-single` class applies special styling to widget containers that store a single line horizontal widget.\n",
785 823 "\n",
786 824 "We hide the label if the description value is blank."
787 825 ]
788 826 },
789 827 {
790 828 "cell_type": "code",
791 829 "collapsed": false,
792 830 "input": [
793 831 "%%javascript\n",
794 832 "\n",
795 "\n",
796 833 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
797 834 " \n",
798 835 " // Define the DatePickerView\n",
799 836 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
800 837 " render: function(){\n",
801 838 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
802 839 " it fit with the other built in widgets.*/\n",
803 840 " // Create a label.\n",
804 841 " this.$label = $('<div />')\n",
805 842 " .addClass('widget-hlabel')\n",
806 843 " .appendTo(this.$el)\n",
807 844 " .hide(); // Hide the label by default.\n",
808 845 " \n",
809 846 " // Create the date picker control.\n",
810 847 " this.$date = $('<input />')\n",
811 848 " .attr('type', 'date')\n",
812 849 " .appendTo(this.$el);\n",
813 850 " },\n",
814 851 " \n",
815 852 " update: function() {\n",
816 853 " \n",
817 854 " // Set the value of the date control and then call base.\n",
818 855 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
819 856 " \n",
820 857 " // Hide or show the label depending on the existance of a description.\n",
821 858 " var description = this.model.get('description');\n",
822 859 " if (description == undefined || description == '') {\n",
823 860 " this.$label.hide();\n",
824 861 " } else {\n",
825 862 " this.$label.show();\n",
826 863 " this.$label.text(description);\n",
827 864 " }\n",
828 865 " \n",
829 866 " return DatePickerView.__super__.update.apply(this);\n",
830 867 " },\n",
831 868 " \n",
832 869 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
833 870 " events: {\"change\": \"handle_date_change\"},\n",
834 871 " \n",
835 872 " // Callback for when the date is changed.\n",
836 873 " handle_date_change: function(event) {\n",
837 874 " this.model.set('value', this.$date.val());\n",
838 875 " this.touch();\n",
839 876 " },\n",
840 877 " });\n",
841 878 " \n",
842 879 " // Register the DatePickerView with the widget manager.\n",
843 880 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
844 881 "});"
845 882 ],
846 883 "language": "python",
847 884 "metadata": {},
848 885 "outputs": [
849 886 {
850 887 "javascript": [
851 888 "\n",
852 "\n",
853 889 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
854 890 " \n",
855 891 " // Define the DatePickerView\n",
856 892 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
857 893 " render: function(){\n",
858 894 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
859 895 " it fit with the other built in widgets.*/\n",
860 896 " // Create a label.\n",
861 897 " this.$label = $('<div />')\n",
862 898 " .addClass('widget-hlabel')\n",
863 899 " .appendTo(this.$el)\n",
864 900 " .hide(); // Hide the label by default.\n",
865 901 " \n",
866 902 " // Create the date picker control.\n",
867 903 " this.$date = $('<input />')\n",
868 904 " .attr('type', 'date')\n",
869 905 " .appendTo(this.$el);\n",
870 906 " },\n",
871 907 " \n",
872 908 " update: function() {\n",
873 909 " \n",
874 910 " // Set the value of the date control and then call base.\n",
875 911 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
876 912 " \n",
877 913 " // Hide or show the label depending on the existance of a description.\n",
878 914 " var description = this.model.get('description');\n",
879 915 " if (description == undefined || description == '') {\n",
880 916 " this.$label.hide();\n",
881 917 " } else {\n",
882 918 " this.$label.show();\n",
883 919 " this.$label.text(description);\n",
884 920 " }\n",
885 921 " \n",
886 922 " return DatePickerView.__super__.update.apply(this);\n",
887 923 " },\n",
888 924 " \n",
889 925 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
890 926 " events: {\"change\": \"handle_date_change\"},\n",
891 927 " \n",
892 928 " // Callback for when the date is changed.\n",
893 929 " handle_date_change: function(event) {\n",
894 930 " this.model.set('value', this.$date.val());\n",
895 931 " this.touch();\n",
896 932 " },\n",
897 933 " });\n",
898 934 " \n",
899 935 " // Register the DatePickerView with the widget manager.\n",
900 936 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
901 937 "});"
902 938 ],
903 939 "metadata": {},
904 940 "output_type": "display_data",
905 941 "text": [
906 "<IPython.core.display.Javascript at 0x2a5a6d0>"
942 "<IPython.core.display.Javascript at 0x1094eef90>"
907 943 ]
908 944 }
909 945 ],
910 946 "prompt_number": 19
911 947 },
912 948 {
913 949 "cell_type": "heading",
914 950 "level": 2,
915 951 "metadata": {},
916 952 "source": [
917 953 "Test"
918 954 ]
919 955 },
920 956 {
921 957 "cell_type": "markdown",
922 958 "metadata": {},
923 959 "source": [
924 960 "To test the drawing of the label we create the widget like normal but supply the additional description property a value."
925 961 ]
926 962 },
927 963 {
928 964 "cell_type": "code",
929 965 "collapsed": false,
930 966 "input": [
931 967 "# Add some additional widgets for aesthetic purpose\n",
932 968 "display(widgets.TextBoxWidget(description=\"First:\"))\n",
933 969 "display(widgets.TextBoxWidget(description=\"Last:\"))\n",
934 970 "\n",
935 971 "my_widget = DateWidget()\n",
936 972 "display(my_widget)\n",
937 973 "my_widget.description=\"DOB:\""
938 974 ],
939 975 "language": "python",
940 976 "metadata": {},
941 977 "outputs": [],
942 978 "prompt_number": 20
943 979 },
944 980 {
945 981 "cell_type": "markdown",
946 982 "metadata": {},
947 983 "source": [
948 984 "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."
949 985 ]
950 986 },
951 987 {
952 988 "cell_type": "code",
953 989 "collapsed": false,
954 990 "input": [
955 991 "my_widget = DateWidget()\n",
956 992 "display(my_widget)\n",
957 993 "\n",
958 "def validate_date(date):\n",
994 "def require_2014(date):\n",
959 995 " return not date is None and date.year == 2014\n",
960 "my_widget.validation.register_callback(validate_date)"
996 "my_widget.validate.register_callback(require_2014)"
961 997 ],
962 998 "language": "python",
963 999 "metadata": {},
964 1000 "outputs": [],
965 1001 "prompt_number": 21
966 1002 },
967 1003 {
968 1004 "cell_type": "code",
969 1005 "collapsed": false,
970 1006 "input": [
971 1007 "# Try setting a valid date\n",
972 1008 "my_widget.value = \"December 2, 2014\""
973 1009 ],
974 1010 "language": "python",
975 1011 "metadata": {},
976 1012 "outputs": [],
977 1013 "prompt_number": 22
978 1014 },
979 1015 {
980 1016 "cell_type": "code",
981 1017 "collapsed": false,
982 1018 "input": [
983 1019 "# Try setting an invalid date\n",
984 1020 "my_widget.value = \"June 12, 1999\""
985 1021 ],
986 1022 "language": "python",
987 1023 "metadata": {},
988 1024 "outputs": [],
989 1025 "prompt_number": 23
990 1026 },
991 1027 {
992 1028 "cell_type": "code",
993 1029 "collapsed": false,
994 1030 "input": [
995 1031 "my_widget.value"
996 1032 ],
997 1033 "language": "python",
998 1034 "metadata": {},
999 1035 "outputs": [
1000 1036 {
1001 1037 "metadata": {},
1002 1038 "output_type": "pyout",
1003 1039 "prompt_number": 24,
1004 1040 "text": [
1005 1041 "u'2014-12-02'"
1006 1042 ]
1007 1043 }
1008 1044 ],
1009 1045 "prompt_number": 24
1010 1046 },
1011 1047 {
1012 1048 "cell_type": "markdown",
1013 1049 "metadata": {},
1014 1050 "source": [
1015 1051 "This concludes Part 6 of the [series](index.ipynb)."
1016 1052 ]
1017 1053 }
1018 1054 ],
1019 1055 "metadata": {}
1020 1056 }
1021 1057 ]
1022 1058 } No newline at end of file
@@ -1,570 +1,203 b''
1 1 {
2 2 "metadata": {
3 3 "name": ""
4 4 },
5 5 "nbformat": 3,
6 6 "nbformat_minor": 0,
7 7 "worksheets": [
8 8 {
9 9 "cells": [
10 10 {
11 11 "cell_type": "heading",
12 12 "level": 1,
13 13 "metadata": {},
14 14 "source": [
15 15 "Variable Inspector Widget"
16 16 ]
17 17 },
18 18 {
19 19 "cell_type": "heading",
20 20 "level": 2,
21 21 "metadata": {},
22 22 "source": [
23 "A short example implementation."
23 "A short example implementation"
24 24 ]
25 25 },
26 26 {
27 27 "cell_type": "markdown",
28 28 "metadata": {},
29 29 "source": [
30 30 "This notebook demonstrates how one can use the widgets already built-in to IPython to create a working variable inspector much like the ones seen in popular commercial scientific computing environments."
31 31 ]
32 32 },
33 33 {
34 34 "cell_type": "code",
35 35 "collapsed": false,
36 36 "input": [
37 "from IPython.html import widgets # Loads the Widget framework.\n",
38 "from IPython.core.magics.namespace import NamespaceMagics # Used to query namespace.\n",
39 "\n",
40 "# For this example, hide these names, just to avoid polluting the namespace further\n",
41 "get_ipython().user_ns_hidden['widgets'] = widgets\n",
42 "get_ipython().user_ns_hidden['NamespaceMagics'] = NamespaceMagics"
43 ],
44 "language": "python",
45 "metadata": {},
46 "outputs": [],
47 "prompt_number": 1
48 },
49 {
50 "cell_type": "code",
51 "collapsed": false,
52 "input": [
37 53 "class VariableInspectorWindow(object):\n",
38 " \n",
39 " # For this example file, the import sit inside the class, just to avoid poluting \n",
40 " # the namespace further.\n",
41 " from IPython.html import widgets # Loads the Widget framework.\n",
42 " from IPython.core.magics.namespace import NamespaceMagics # Used to query namespace.\n",
43 " \n",
44 54 " instance = None\n",
45 55 " \n",
46 56 " def __init__(self, ipython):\n",
47 57 " \"\"\"Public constructor.\"\"\"\n",
48 58 " if VariableInspectorWindow.instance is not None:\n",
49 59 " raise Exception(\"\"\"Only one instance of the Variable Inspector can exist at a \n",
50 60 " time. Call close() on the active instance before creating a new instance.\n",
51 61 " If you have lost the handle to the active instance, you can re-obtain it\n",
52 62 " via `VariableInspectorWindow.instance`.\"\"\")\n",
53 63 " \n",
54 64 " VariableInspectorWindow.instance = self\n",
55 65 " self.closed = False\n",
56 " self.namespace = self.NamespaceMagics()\n",
66 " self.namespace = NamespaceMagics()\n",
57 67 " self.namespace.shell = ipython.kernel.shell\n",
58 68 " \n",
59 " self._popout = self.widgets.PopupWidget()\n",
69 " self._popout = widgets.PopupWidget()\n",
60 70 " self._popout.description = \"Variable Inspector\"\n",
61 71 " self._popout.button_text = self._popout.description\n",
62 72 "\n",
63 " self._modal_body = self.widgets.ContainerWidget()\n",
73 " self._modal_body = widgets.ContainerWidget()\n",
64 74 " self._modal_body.set_css('overflow-y', 'scroll')\n",
65 75 "\n",
66 " self._modal_body_label = self.widgets.HTMLWidget(value = 'Not hooked')\n",
76 " self._modal_body_label = widgets.HTMLWidget(value = 'Not hooked')\n",
67 77 " self._modal_body.children = [self._modal_body_label]\n",
68 78 "\n",
69 " self._modal_footer = self.widgets.ContainerWidget()\n",
70 " self._var_filter = self.widgets.ToggleButtonsWidget(description=\"Method:\", values=['List', 'Details'], value='List')\n",
79 " self._modal_footer = widgets.ContainerWidget()\n",
80 " self._var_filter = widgets.ToggleButtonsWidget(description=\"Method:\", values=['List', 'Details'], value='List')\n",
71 81 " self._modal_footer.children = [self._var_filter]\n",
72 82 "\n",
73 83 " self._popout.children = [\n",
74 84 " self._modal_body, \n",
75 85 " self._modal_footer,\n",
76 86 " ]\n",
77 87 " \n",
78 88 " self._ipython = ipython\n",
79 89 " self._ipython.register_post_execute(self._fill)\n",
80 90 " self._var_filter.on_trait_change(self._fill, 'value')\n",
81 91 "\n",
82 92 " def close(self):\n",
83 93 " \"\"\"Close and remove hooks.\"\"\"\n",
84 94 " if not self.closed:\n",
85 95 " del self._ipython._post_execute[self._fill]\n",
86 96 " self._popout.close()\n",
87 97 " self.closed = True\n",
88 98 " VariableInspectorWindow.instance = None\n",
89 99 "\n",
90 100 " def _fill(self):\n",
91 101 " \"\"\"Fill self with variable information.\"\"\"\n",
92 102 " values = self.namespace.who_ls()\n",
93 103 " mode = self._var_filter.value\n",
94 104 " if mode == \"List\":\n",
95 105 " self._modal_body_label.value = '<br>'.join(values)\n",
96 106 " elif mode == \"Details\":\n",
97 107 " self._modal_body_label.value = '<table class=\"table table-bordered table-striped\"><tr><th>Name</th><th>Type</th><th>Value</th></tr><tr><td>' + \\\n",
98 108 " '</td></tr><tr><td>'.join(['{0}</td><td>{1}</td><td>{2}'.format(v, type(eval(v)).__name__, str(eval(v))) for v in values]) + \\\n",
99 109 " '</td></tr></table>'\n",
100 110 "\n",
101 111 " def _ipython_display_(self):\n",
102 112 " \"\"\"Called when display() or pyout is used to display the Variable \n",
103 113 " Inspector.\"\"\"\n",
104 114 " self._popout._ipython_display_()\n",
105 115 " self._modal_footer.add_class('modal-footer')\n",
106 116 " self._popout.add_class('vbox')\n",
107 117 " self._modal_body.add_class('box-flex1')\n"
108 118 ],
109 119 "language": "python",
110 120 "metadata": {},
111 121 "outputs": [],
112 "prompt_number": 17
122 "prompt_number": 2
113 123 },
114 124 {
115 125 "cell_type": "code",
116 126 "collapsed": false,
117 127 "input": [
118 128 "inspector = VariableInspectorWindow(get_ipython())\n",
119 129 "inspector"
120 130 ],
121 131 "language": "python",
122 132 "metadata": {},
123 133 "outputs": [],
124 "prompt_number": 18
134 "prompt_number": 3
125 135 },
126 136 {
127 137 "cell_type": "heading",
128 138 "level": 1,
129 139 "metadata": {},
130 140 "source": [
131 141 "Test"
132 142 ]
133 143 },
134 144 {
135 145 "cell_type": "code",
136 146 "collapsed": false,
137 147 "input": [
138 148 "a = 5"
139 149 ],
140 150 "language": "python",
141 151 "metadata": {},
142 152 "outputs": [],
143 "prompt_number": 11
153 "prompt_number": 4
144 154 },
145 155 {
146 156 "cell_type": "code",
147 157 "collapsed": false,
148 158 "input": [
149 159 "b = 3.0"
150 160 ],
151 161 "language": "python",
152 162 "metadata": {},
153 163 "outputs": [],
154 "prompt_number": 12
164 "prompt_number": 5
155 165 },
156 166 {
157 167 "cell_type": "code",
158 168 "collapsed": false,
159 169 "input": [
160 170 "c = a * b"
161 171 ],
162 172 "language": "python",
163 173 "metadata": {},
164 174 "outputs": [],
165 "prompt_number": 13
175 "prompt_number": 6
166 176 },
167 177 {
168 178 "cell_type": "code",
169 179 "collapsed": false,
170 180 "input": [
171 181 "d = \"String\""
172 182 ],
173 183 "language": "python",
174 184 "metadata": {},
175 185 "outputs": [],
176 "prompt_number": 14
186 "prompt_number": 7
177 187 },
178 188 {
179 189 "cell_type": "code",
180 190 "collapsed": false,
181 191 "input": [
182 192 "del b"
183 193 ],
184 194 "language": "python",
185 195 "metadata": {},
186 196 "outputs": [],
187 "prompt_number": 15
188 },
189 {
190 "cell_type": "code",
191 "collapsed": false,
192 "input": [
193 "?"
194 ],
195 "language": "python",
196 "metadata": {},
197 "outputs": [],
198 "prompt_number": 16
199 },
200 {
201 "cell_type": "code",
202 "collapsed": false,
203 "input": [
204 "dir()"
205 ],
206 "language": "python",
207 "metadata": {},
208 "outputs": [
209 {
210 "metadata": {},
211 "output_type": "pyout",
212 "prompt_number": 15,
213 "text": [
214 "['In',\n",
215 " 'Out',\n",
216 " 'VariableInspectorWindow',\n",
217 " '_',\n",
218 " '_10',\n",
219 " '_14',\n",
220 " '__',\n",
221 " '___',\n",
222 " '__builtin__',\n",
223 " '__builtins__',\n",
224 " '__doc__',\n",
225 " '__name__',\n",
226 " '__package__',\n",
227 " '_dh',\n",
228 " '_i',\n",
229 " '_i1',\n",
230 " '_i10',\n",
231 " '_i11',\n",
232 " '_i12',\n",
233 " '_i13',\n",
234 " '_i14',\n",
235 " '_i15',\n",
236 " '_i2',\n",
237 " '_i3',\n",
238 " '_i4',\n",
239 " '_i5',\n",
240 " '_i6',\n",
241 " '_i7',\n",
242 " '_i8',\n",
243 " '_i9',\n",
244 " '_ih',\n",
245 " '_ii',\n",
246 " '_iii',\n",
247 " '_oh',\n",
248 " '_sh',\n",
249 " 'exit',\n",
250 " 'get_ipython',\n",
251 " 'quit',\n",
252 " 'widgets']"
253 ]
254 }
255 ],
256 "prompt_number": 15
257 },
258 {
259 "cell_type": "code",
260 "collapsed": false,
261 "input": [
262 "dir(get_ipython().kernel.shell)"
263 ],
264 "language": "python",
265 "metadata": {},
266 "outputs": [
267 {
268 "metadata": {},
269 "output_type": "pyout",
270 "prompt_number": 4,
271 "text": [
272 "['Completer',\n",
273 " 'CustomTB',\n",
274 " 'InteractiveTB',\n",
275 " 'SyntaxTB',\n",
276 " '__class__',\n",
277 " '__delattr__',\n",
278 " '__dict__',\n",
279 " '__doc__',\n",
280 " '__format__',\n",
281 " '__getattribute__',\n",
282 " '__hash__',\n",
283 " '__init__',\n",
284 " '__module__',\n",
285 " '__new__',\n",
286 " '__reduce__',\n",
287 " '__reduce_ex__',\n",
288 " '__repr__',\n",
289 " '__setattr__',\n",
290 " '__sizeof__',\n",
291 " '__str__',\n",
292 " '__subclasshook__',\n",
293 " '__weakref__',\n",
294 " '_add_notifiers',\n",
295 " '_call_pdb',\n",
296 " '_config_changed',\n",
297 " '_exit_now_changed',\n",
298 " '_exiter_default',\n",
299 " '_find_my_config',\n",
300 " '_format_user_obj',\n",
301 " '_get_call_pdb',\n",
302 " '_get_exc_info',\n",
303 " '_indent_current_str',\n",
304 " '_inspect',\n",
305 " '_instance',\n",
306 " '_ipython_dir_changed',\n",
307 " '_last_input_line',\n",
308 " '_load_config',\n",
309 " '_main_mod_cache',\n",
310 " '_notify_trait',\n",
311 " '_object_find',\n",
312 " '_ofind',\n",
313 " '_ofind_property',\n",
314 " '_orig_sys_module_state',\n",
315 " '_orig_sys_modules_main_mod',\n",
316 " '_orig_sys_modules_main_name',\n",
317 " '_post_execute',\n",
318 " '_prompt_in1_changed',\n",
319 " '_prompt_in2_changed',\n",
320 " '_prompt_out_changed',\n",
321 " '_prompt_pad_left_changed',\n",
322 " '_prompt_trait_changed',\n",
323 " '_remove_notifiers',\n",
324 " '_reply_content',\n",
325 " '_run_cached_cell_magic',\n",
326 " '_set_call_pdb',\n",
327 " '_showtraceback',\n",
328 " '_trait_dyn_inits',\n",
329 " '_trait_notifiers',\n",
330 " '_trait_values',\n",
331 " '_user_obj_error',\n",
332 " '_walk_mro',\n",
333 " 'alias_manager',\n",
334 " 'all_ns_refs',\n",
335 " 'ask_exit',\n",
336 " 'ask_yes_no',\n",
337 " 'ast_node_interactivity',\n",
338 " 'ast_transformers',\n",
339 " 'atexit_operations',\n",
340 " 'auto_rewrite_input',\n",
341 " 'autocall',\n",
342 " 'autoindent',\n",
343 " 'automagic',\n",
344 " 'builtin_trap',\n",
345 " 'cache_size',\n",
346 " 'call_pdb',\n",
347 " 'class_config_section',\n",
348 " 'class_get_help',\n",
349 " 'class_get_trait_help',\n",
350 " 'class_print_help',\n",
351 " 'class_trait_names',\n",
352 " 'class_traits',\n",
353 " 'cleanup',\n",
354 " 'clear_instance',\n",
355 " 'clear_main_mod_cache',\n",
356 " 'color_info',\n",
357 " 'colors',\n",
358 " 'colors_force',\n",
359 " 'comm_manager',\n",
360 " 'compile',\n",
361 " 'complete',\n",
362 " 'config',\n",
363 " 'configurables',\n",
364 " 'custom_exceptions',\n",
365 " 'data_pub',\n",
366 " 'data_pub_class',\n",
367 " 'db',\n",
368 " 'debug',\n",
369 " 'debugger',\n",
370 " 'deep_reload',\n",
371 " 'default_user_namespaces',\n",
372 " 'define_macro',\n",
373 " 'define_magic',\n",
374 " 'del_var',\n",
375 " 'dir_stack',\n",
376 " 'disable_failing_post_execute',\n",
377 " 'display_formatter',\n",
378 " 'display_pub',\n",
379 " 'display_pub_class',\n",
380 " 'display_trap',\n",
381 " 'displayhook',\n",
382 " 'displayhook_class',\n",
383 " 'drop_by_id',\n",
384 " 'enable_gui',\n",
385 " 'enable_matplotlib',\n",
386 " 'enable_pylab',\n",
387 " 'ev',\n",
388 " 'ex',\n",
389 " 'excepthook',\n",
390 " 'execution_count',\n",
391 " 'exit_now',\n",
392 " 'exiter',\n",
393 " 'extension_manager',\n",
394 " 'extract_input_lines',\n",
395 " 'filename',\n",
396 " 'find_cell_magic',\n",
397 " 'find_line_magic',\n",
398 " 'find_magic',\n",
399 " 'find_user_code',\n",
400 " 'get_ipython',\n",
401 " 'get_parent',\n",
402 " 'getoutput',\n",
403 " 'has_readline',\n",
404 " 'history_length',\n",
405 " 'history_manager',\n",
406 " 'home_dir',\n",
407 " 'hooks',\n",
408 " 'indent_current_nsp',\n",
409 " 'init_alias',\n",
410 " 'init_builtins',\n",
411 " 'init_comms',\n",
412 " 'init_completer',\n",
413 " 'init_create_namespaces',\n",
414 " 'init_data_pub',\n",
415 " 'init_display_formatter',\n",
416 " 'init_display_pub',\n",
417 " 'init_displayhook',\n",
418 " 'init_encoding',\n",
419 " 'init_environment',\n",
420 " 'init_extension_manager',\n",
421 " 'init_history',\n",
422 " 'init_hooks',\n",
423 " 'init_inspector',\n",
424 " 'init_instance_attrs',\n",
425 " 'init_io',\n",
426 " 'init_ipython_dir',\n",
427 " 'init_latextool',\n",
428 " 'init_logger',\n",
429 " 'init_logstart',\n",
430 " 'init_magics',\n",
431 " 'init_payload',\n",
432 " 'init_pdb',\n",
433 " 'init_prefilter',\n",
434 " 'init_profile_dir',\n",
435 " 'init_prompts',\n",
436 " 'init_pushd_popd_magic',\n",
437 " 'init_readline',\n",
438 " 'init_syntax_highlighting',\n",
439 " 'init_sys_modules',\n",
440 " 'init_traceback_handlers',\n",
441 " 'init_user_ns',\n",
442 " 'init_virtualenv',\n",
443 " 'initialized',\n",
444 " 'input_splitter',\n",
445 " 'input_transformer_manager',\n",
446 " 'inspector',\n",
447 " 'instance',\n",
448 " 'ipython_dir',\n",
449 " 'keepkernel_on_exit',\n",
450 " 'kernel',\n",
451 " 'logappend',\n",
452 " 'logfile',\n",
453 " 'logger',\n",
454 " 'logstart',\n",
455 " 'magic',\n",
456 " 'magics_manager',\n",
457 " 'meta',\n",
458 " 'mktempfile',\n",
459 " 'more',\n",
460 " 'multiline_history',\n",
461 " 'new_main_mod',\n",
462 " 'ns_table',\n",
463 " 'object_info_string_level',\n",
464 " 'object_inspect',\n",
465 " 'on_trait_change',\n",
466 " 'parent',\n",
467 " 'parent_header',\n",
468 " 'payload_manager',\n",
469 " 'pdb',\n",
470 " 'pre_readline',\n",
471 " 'prefilter',\n",
472 " 'prefilter_manager',\n",
473 " 'prepare_user_module',\n",
474 " 'profile',\n",
475 " 'profile_dir',\n",
476 " 'prompt_in1',\n",
477 " 'prompt_in2',\n",
478 " 'prompt_manager',\n",
479 " 'prompt_out',\n",
480 " 'prompts_pad_left',\n",
481 " 'push',\n",
482 " 'pycolorize',\n",
483 " 'pylab_gui_select',\n",
484 " 'quiet',\n",
485 " 'raw_input_original',\n",
486 " 'readline',\n",
487 " 'readline_delims',\n",
488 " 'readline_no_record',\n",
489 " 'readline_parse_and_bind',\n",
490 " 'readline_remove_delims',\n",
491 " 'readline_use',\n",
492 " 'refill_readline_hist',\n",
493 " 'register_magic_function',\n",
494 " 'register_magics',\n",
495 " 'register_post_execute',\n",
496 " 'reset',\n",
497 " 'reset_selective',\n",
498 " 'restore_sys_module_state',\n",
499 " 'rl_do_indent',\n",
500 " 'rl_next_input',\n",
501 " 'run_ast_nodes',\n",
502 " 'run_cell',\n",
503 " 'run_cell_magic',\n",
504 " 'run_code',\n",
505 " 'run_line_magic',\n",
506 " 'runcode',\n",
507 " 'safe_execfile',\n",
508 " 'safe_execfile_ipy',\n",
509 " 'safe_run_module',\n",
510 " 'save_sys_module_state',\n",
511 " 'section_names',\n",
512 " 'separate_in',\n",
513 " 'separate_out',\n",
514 " 'separate_out2',\n",
515 " 'set_autoindent',\n",
516 " 'set_completer_frame',\n",
517 " 'set_custom_completer',\n",
518 " 'set_custom_exc',\n",
519 " 'set_hook',\n",
520 " 'set_next_input',\n",
521 " 'set_parent',\n",
522 " 'set_readline_completer',\n",
523 " 'show_rewritten_input',\n",
524 " 'show_usage',\n",
525 " 'show_usage_error',\n",
526 " 'showindentationerror',\n",
527 " 'showsyntaxerror',\n",
528 " 'showtraceback',\n",
529 " 'starting_dir',\n",
530 " 'stdin_encoding',\n",
531 " 'strdispatchers',\n",
532 " 'sys_excepthook',\n",
533 " 'system',\n",
534 " 'system_piped',\n",
535 " 'system_raw',\n",
536 " 'tempfiles',\n",
537 " 'trait_metadata',\n",
538 " 'trait_names',\n",
539 " 'traits',\n",
540 " 'transform_ast',\n",
541 " 'update_config',\n",
542 " 'user_expressions',\n",
543 " 'user_global_ns',\n",
544 " 'user_module',\n",
545 " 'user_ns',\n",
546 " 'user_ns_hidden',\n",
547 " 'user_variables',\n",
548 " 'var_expand',\n",
549 " 'wildcards_case_sensitive',\n",
550 " 'write',\n",
551 " 'write_err',\n",
552 " 'xmode']"
553 ]
554 }
555 ],
556 "prompt_number": 4
557 },
558 {
559 "cell_type": "code",
560 "collapsed": false,
561 "input": [],
562 "language": "python",
563 "metadata": {},
564 "outputs": []
197 "prompt_number": 8
565 198 }
566 199 ],
567 200 "metadata": {}
568 201 }
569 202 ]
570 203 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now