##// END OF EJS Templates
Updated examples 3-6
Jonathan Frederic -
Show More
@@ -1,336 +1,172 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "cell_tags": [
3 "cell_tags": [
4 [
4 [
5 "<None>",
5 "<None>",
6 null
6 null
7 ]
7 ]
8 ],
8 ],
9 "name": ""
9 "name": ""
10 },
10 },
11 "nbformat": 3,
11 "nbformat": 3,
12 "nbformat_minor": 0,
12 "nbformat_minor": 0,
13 "worksheets": [
13 "worksheets": [
14 {
14 {
15 "cells": [
15 "cells": [
16 {
16 {
17 "cell_type": "code",
17 "cell_type": "code",
18 "collapsed": false,
18 "collapsed": false,
19 "input": [
19 "input": [
20 "from IPython.html import widgets # Widget definitions\n",
20 "from IPython.html import widgets # Widget definitions\n",
21 "from IPython.display import display # Used to display widgets in the notebook"
21 "from IPython.display import display # Used to display widgets in the notebook"
22 ],
22 ],
23 "language": "python",
23 "language": "python",
24 "metadata": {},
24 "metadata": {},
25 "outputs": [],
25 "outputs": [],
26 "prompt_number": 1
26 "prompt_number": 1
27 },
27 },
28 {
28 {
29 "cell_type": "heading",
29 "cell_type": "heading",
30 "level": 1,
30 "level": 1,
31 "metadata": {},
31 "metadata": {},
32 "source": [
32 "source": [
33 "Parent/Child Relationships"
33 "Parent/Child Relationships"
34 ]
34 ]
35 },
35 },
36 {
36 {
37 "cell_type": "markdown",
37 "cell_type": "markdown",
38 "metadata": {},
38 "metadata": {},
39 "source": [
39 "source": [
40 "To display widget A inside widget B, widget A must be a child of widget B. With IPython widgets, the widgets are instances that live in the back-end (usally Python). There can be multiple views displayed in the front-end that represent one widget in the backend. Each view can be displayed at a different time. 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",
40 "To display widget A inside widget B, widget A must be a child of widget B. With IPython widgets, the widgets are instances that live in the back-end (usally Python). There can be multiple views displayed in the front-end that represent one widget in the backend. Each view can be displayed at a different time. 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",
41 "\n",
41 "\n",
42 "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)."
42 "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)."
43 ]
43 ]
44 },
44 },
45 {
45 {
46 "cell_type": "code",
46 "cell_type": "code",
47 "collapsed": false,
47 "collapsed": false,
48 "input": [
48 "input": [
49 "floatrange = widgets.FloatSliderWidget() # You can set the parent in the constructor,\n",
49 "floatrange = widgets.FloatSliderWidget()\n",
50 "\n",
50 "string = widgets.TextBoxWidget(value='hi')\n",
51 "string = widgets.TextBocWidget(value='hi')\n",
52 "container = widgets.ContainerWidget(children=[floatrange, string])\n",
51 "container = widgets.ContainerWidget(children=[floatrange, string])\n",
53 "\n",
52 "\n",
54 "display(container) # Displays the `container` and all of it's children."
53 "display(container) # Displays the `container` and all of it's children."
55 ],
54 ],
56 "language": "python",
55 "language": "python",
57 "metadata": {},
56 "metadata": {},
58 "outputs": [
59 {
60 "ename": "AttributeError",
61 "evalue": "'module' object has no attribute 'TextBocWidget'",
62 "output_type": "pyerr",
63 "traceback": [
64 "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
65 "\u001b[1;32m<ipython-input-4-085d43fdae3d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mfloatrange\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidgets\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mFloatSliderWidget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# You can set the parent in the constructor,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mstring\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidgets\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mTextBocWidget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'hi'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[0mcontainer\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mwidgets\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mContainerWidget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mchildren\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mfloatrange\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstring\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
66 "\u001b[1;31mAttributeError\u001b[0m: 'module' object has no attribute 'TextBocWidget'"
67 ]
68 }
69 ],
70 "prompt_number": 4
71 },
72 {
73 "cell_type": "markdown",
74 "metadata": {},
75 "source": [
76 "Children can also be added to parents after the parent has been displayed. If the children are added after the parent has already been displayed, the children must be displayed themselves.\n",
77 "\n",
78 "In the example below, the IntRangeWidget is never rendered since display was called on the parent before the parent/child relationship was established."
79 ]
80 },
81 {
82 "cell_type": "code",
83 "collapsed": false,
84 "input": [
85 "intrange = widgets.IntRangeWidget() # Never gets displayed.\n",
86 "container = widgets.MulticontainerWidget(children=[intrange])\n",
87 "\n",
88 "display(container)\n"
89 ],
90 "language": "python",
91 "metadata": {},
92 "outputs": [],
93 "prompt_number": 5
94 },
95 {
96 "cell_type": "markdown",
97 "metadata": {},
98 "source": [
99 "Calling display on the child fixes the problem."
100 ]
101 },
102 {
103 "cell_type": "code",
104 "collapsed": false,
105 "input": [
106 "container = widgets.MulticontainerWidget()\n",
107 "display(container)\n",
108 "\n",
109 "intrange = widgets.IntRangeWidget(parent=container)\n",
110 "display(intrange) # This line is needed since the `container` has already been displayed."
111 ],
112 "language": "python",
113 "metadata": {},
114 "outputs": [],
115 "prompt_number": 4
116 },
117 {
118 "cell_type": "heading",
119 "level": 1,
120 "metadata": {},
121 "source": [
122 "Changing Child Views"
123 ]
124 },
125 {
126 "cell_type": "markdown",
127 "metadata": {},
128 "source": [
129 "The view used to display a widget must defined by the time the widget is displayed. If children widgets are to be displayed along with the parent in one call, their `view_name`s can't be set since all of the widgets are sharing the same display call. Instead, their `default_view_name`s must be set (as seen below)."
130 ]
131 },
132 {
133 "cell_type": "code",
134 "collapsed": false,
135 "input": [
136 "\n",
137 "floatrange = widgets.FloatRangeWidget()\n",
138 "floatrange.default_view_name = \"FloatTextView\" # It can be set as a property.\n",
139 "\n",
140 "string = widgets.StringWidget(default_view_name = \"TextAreaView\") # It can also be set in the constructor.\n",
141 "container = widgets.MulticontainerWidget(children=[floatrange, string])\n",
142 "display(container)"
143 ],
144 "language": "python",
145 "metadata": {},
146 "outputs": [],
57 "outputs": [],
147 "prompt_number": 6
58 "prompt_number": 2
148 },
59 },
149 {
60 {
150 "cell_type": "markdown",
61 "cell_type": "markdown",
151 "metadata": {},
62 "metadata": {},
152 "source": [
63 "source": [
153 "However, if the children are displayed after the parent, their `view_name` can also be set like normal. Both methods will work. The code below produces the same output as the code above."
64 "Children can also be added to parents after the parent has been displayed. The parent is responsible for rendering its children."
154 ]
65 ]
155 },
66 },
156 {
67 {
157 "cell_type": "code",
68 "cell_type": "code",
158 "collapsed": false,
69 "collapsed": false,
159 "input": [
70 "input": [
160 "container = widgets.MulticontainerWidget()\n",
71 "container = widgets.ContainerWidget()\n",
161 "display(container)\n",
72 "display(container)\n",
162 "\n",
73 "\n",
163 "floatrange = widgets.FloatRangeWidget()\n",
74 "intrange = widgets.IntSliderWidget()\n",
164 "floatrange.parent=container\n",
75 "container.children=[intrange]\n"
165 "display(floatrange, view_name = \"FloatTextView\") # view_name can be set during display.\n",
166 "\n",
167 "string = widgets.StringWidget()\n",
168 "string.parent = container\n",
169 "string.default_view_name = \"TextAreaView\" # Setting default_view_name still works.\n",
170 "display(string)\n"
171 ],
76 ],
172 "language": "python",
77 "language": "python",
173 "metadata": {},
78 "metadata": {},
174 "outputs": [],
79 "outputs": [],
175 "prompt_number": 6
80 "prompt_number": 3
176 },
81 },
177 {
82 {
178 "cell_type": "heading",
83 "cell_type": "heading",
179 "level": 1,
84 "level": 1,
180 "metadata": {},
85 "metadata": {},
181 "source": [
86 "source": [
182 "Visibility"
87 "Visibility"
183 ]
88 ]
184 },
89 },
185 {
90 {
186 "cell_type": "markdown",
91 "cell_type": "markdown",
187 "metadata": {},
92 "metadata": {},
188 "source": [
93 "source": [
189 "Sometimes it's necessary to hide/show widget views in place, without ruining the order that they have been displayed on the page. Using the `display` method, the views are always added to the end of their respective containers. Instead the `visibility` property of widgets can be used to hide/show widgets that have already been displayed (as seen below)."
94 "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)."
190 ]
95 ]
191 },
96 },
192 {
97 {
193 "cell_type": "code",
98 "cell_type": "code",
194 "collapsed": false,
99 "collapsed": false,
195 "input": [
100 "input": [
196 "string = widgets.StringWidget(value=\"Hello World!\")\n",
101 "string = widgets.LatexWidget(value=\"Hello World!\")\n",
197 "display(string, view_name=\"HTMLView\") "
102 "display(string) "
198 ],
103 ],
199 "language": "python",
104 "language": "python",
200 "metadata": {},
105 "metadata": {},
201 "outputs": [],
106 "outputs": [],
202 "prompt_number": 7
107 "prompt_number": 4
203 },
108 },
204 {
109 {
205 "cell_type": "code",
110 "cell_type": "code",
206 "collapsed": false,
111 "collapsed": false,
207 "input": [
112 "input": [
208 "string.visible=False"
113 "string.visible=False"
209 ],
114 ],
210 "language": "python",
115 "language": "python",
211 "metadata": {},
116 "metadata": {},
212 "outputs": [],
117 "outputs": [],
213 "prompt_number": 8
118 "prompt_number": 5
214 },
119 },
215 {
120 {
216 "cell_type": "code",
121 "cell_type": "code",
217 "collapsed": false,
122 "collapsed": false,
218 "input": [
123 "input": [
219 "string.visible=True"
124 "string.visible=True"
220 ],
125 ],
221 "language": "python",
126 "language": "python",
222 "metadata": {},
127 "metadata": {},
223 "outputs": [],
128 "outputs": [],
224 "prompt_number": 9
129 "prompt_number": 6
225 },
130 },
226 {
131 {
227 "cell_type": "markdown",
132 "cell_type": "markdown",
228 "metadata": {},
133 "metadata": {},
229 "source": [
134 "source": [
230 "In the example below, a form is rendered which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
135 "In the example below, a form is rendered which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
231 ]
136 ]
232 },
137 },
233 {
138 {
234 "cell_type": "code",
139 "cell_type": "code",
235 "collapsed": false,
140 "collapsed": false,
236 "input": [
141 "input": [
237 "form = widgets.ContainerWidget()\n",
142 "form = widgets.ContainerWidget()\n",
238 "first = widgets.StringWidget(description=\"First Name:\")\n",
143 "first = widgets.TextBoxWidget(description=\"First Name:\")\n",
239 "last = widgets.StringWidget(description=\"Last Name:\")\n",
144 "last = widgets.TextBoxWidget(description=\"Last Name:\")\n",
240 "\n",
145 "\n",
241 "student = widgets.BoolWidget(description=\"Student:\", value=False)\n",
146 "student = widgets.CheckBoxWidget(description=\"Student:\", value=False)\n",
242 "form.children=[first, last, student]\n",
243 "display(form)"
244 ],
245 "language": "python",
246 "metadata": {},
247 "outputs": [],
248 "prompt_number": 2
249 },
250 {
251 "cell_type": "code",
252 "collapsed": false,
253 "input": [
254 "form = widgets.ContainerWidget()\n",
255 "first = widgets.StringWidget(description=\"First Name:\")\n",
256 "last = widgets.StringWidget(description=\"Last Name:\")\n",
257 "\n",
258 "student = widgets.BoolWidget(description=\"Student:\", value=False)\n",
259 "school_info = widgets.ContainerWidget(visible=False, children=[\n",
147 "school_info = widgets.ContainerWidget(visible=False, children=[\n",
260 " widgets.StringWidget(description=\"School:\"),\n",
148 " widgets.TextBoxWidget(description=\"School:\"),\n",
261 " widgets.IntRangeWidget(description=\"Grade:\", min=0, max=12, default_view_name='IntTextView')\n",
149 " widgets.IntTextWidget(description=\"Grade:\", min=0, max=12)\n",
262 " ])\n",
150 " ])\n",
263 "\n",
151 "\n",
264 "pet = widgets.StringWidget(description=\"Pet's Name:\")\n",
152 "pet = widgets.TextBoxWidget(description=\"Pet's Name:\")\n",
265 "form.children = [first, last, student, school_info, pet]\n",
153 "form.children = [first, last, student, school_info, pet]\n",
266 "display(form)\n",
154 "display(form)\n",
267 "\n",
155 "\n",
268 "def on_student_toggle(name, value):\n",
156 "def on_student_toggle(name, value):\n",
269 " print value\n",
270 " if value:\n",
157 " if value:\n",
271 " school_info.visible = True\n",
158 " school_info.visible = True\n",
272 " else:\n",
159 " else:\n",
273 " school_info.visible = False\n",
160 " school_info.visible = False\n",
274 "student.on_trait_change(on_student_toggle, 'value')\n"
161 "student.on_trait_change(on_student_toggle, 'value')\n"
275 ],
162 ],
276 "language": "python",
163 "language": "python",
277 "metadata": {},
164 "metadata": {},
278 "outputs": [
165 "outputs": [],
279 {
166 "prompt_number": 7
280 "output_type": "stream",
281 "stream": "stdout",
282 "text": [
283 "True\n"
284 ]
285 },
286 {
287 "output_type": "stream",
288 "stream": "stdout",
289 "text": [
290 "False\n"
291 ]
292 },
293 {
294 "output_type": "stream",
295 "stream": "stdout",
296 "text": [
297 "True\n"
298 ]
299 },
300 {
301 "output_type": "stream",
302 "stream": "stdout",
303 "text": [
304 "False\n"
305 ]
306 },
307 {
308 "output_type": "stream",
309 "stream": "stdout",
310 "text": [
311 "True\n"
312 ]
313 },
314 {
315 "output_type": "stream",
316 "stream": "stdout",
317 "text": [
318 "False\n"
319 ]
320 }
321 ],
322 "prompt_number": 2
323 },
324 {
325 "cell_type": "code",
326 "collapsed": false,
327 "input": [],
328 "language": "python",
329 "metadata": {},
330 "outputs": []
331 }
167 }
332 ],
168 ],
333 "metadata": {}
169 "metadata": {}
334 }
170 }
335 ]
171 ]
336 } No newline at end of file
172 }
@@ -1,344 +1,341 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "cell_tags": [
3 "cell_tags": [
4 [
4 [
5 "<None>",
5 "<None>",
6 null
6 null
7 ]
7 ]
8 ],
8 ],
9 "name": ""
9 "name": ""
10 },
10 },
11 "nbformat": 3,
11 "nbformat": 3,
12 "nbformat_minor": 0,
12 "nbformat_minor": 0,
13 "worksheets": [
13 "worksheets": [
14 {
14 {
15 "cells": [
15 "cells": [
16 {
16 {
17 "cell_type": "code",
17 "cell_type": "code",
18 "collapsed": false,
18 "collapsed": false,
19 "input": [
19 "input": [
20 "from IPython.html import widgets # Widget definitions\n",
20 "from IPython.html import widgets # Widget definitions\n",
21 "from IPython.display import display # Used to display widgets in the notebook"
21 "from IPython.display import display # Used to display widgets in the notebook"
22 ],
22 ],
23 "language": "python",
23 "language": "python",
24 "metadata": {},
24 "metadata": {},
25 "outputs": [],
25 "outputs": [],
26 "prompt_number": 1
26 "prompt_number": 1
27 },
27 },
28 {
28 {
29 "cell_type": "heading",
29 "cell_type": "heading",
30 "level": 1,
30 "level": 1,
31 "metadata": {},
31 "metadata": {},
32 "source": [
32 "source": [
33 "CSS"
33 "CSS"
34 ]
34 ]
35 },
35 },
36 {
36 {
37 "cell_type": "markdown",
37 "cell_type": "markdown",
38 "metadata": {},
38 "metadata": {},
39 "source": [
39 "source": [
40 "When trying to design an attractive widget GUI, styling becomes important. Widget 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. "
40 "When trying to design an attractive widget GUI, styling becomes important. Widget 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. "
41 ]
41 ]
42 },
42 },
43 {
43 {
44 "cell_type": "code",
44 "cell_type": "code",
45 "collapsed": false,
45 "collapsed": false,
46 "input": [
46 "input": [
47 "print(widgets.Widget.set_css.__doc__)"
47 "print(widgets.DOMWidget.set_css.__doc__)"
48 ],
48 ],
49 "language": "python",
49 "language": "python",
50 "metadata": {},
50 "metadata": {},
51 "outputs": [
51 "outputs": [
52 {
52 {
53 "output_type": "stream",
53 "output_type": "stream",
54 "stream": "stdout",
54 "stream": "stdout",
55 "text": [
55 "text": [
56 "Set one or more CSS properties of the widget (shared among all of the\n",
56 "Set one or more CSS properties of the widget.\n",
57 " views). This function has two signatures:\n",
57 "\n",
58 " - set_css(css_dict, [selector=''])\n",
58 " This function has two signatures:\n",
59 " - set_css(key, value, [selector=''])\n",
59 " - set_css(css_dict, selector='')\n",
60 " - set_css(key, value, selector='')\n",
60 "\n",
61 "\n",
61 " Parameters\n",
62 " Parameters\n",
62 " ----------\n",
63 " ----------\n",
63 " css_dict : dict\n",
64 " css_dict : dict\n",
64 " CSS key/value pairs to apply\n",
65 " CSS key/value pairs to apply\n",
65 " key: unicode\n",
66 " key: unicode\n",
66 " CSS key\n",
67 " CSS key\n",
67 " value\n",
68 " value\n",
68 " CSS value\n",
69 " CSS value\n",
69 " selector: unicode (optional)\n",
70 " selector: unicode (optional)\n",
70 " JQuery selector to use to apply the CSS key/value.\n",
71 " JQuery selector to use to apply the CSS key/value. If no selector \n",
72 " is provided, an empty selector is used. An empty selector makes the \n",
73 " front-end try to apply the css to a default element. The default\n",
74 " element is an attribute unique to each view, which is a DOM element\n",
75 " of the view that should be styled with common CSS (see \n",
76 " `$el_to_style` in the Javascript code).\n",
71 " \n"
77 " \n"
72 ]
78 ]
73 }
79 }
74 ],
80 ],
75 "prompt_number": 2
81 "prompt_number": 2
76 },
82 },
77 {
83 {
78 "cell_type": "markdown",
84 "cell_type": "markdown",
79 "metadata": {},
85 "metadata": {},
80 "source": [
86 "source": [
81 "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."
87 "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."
82 ]
88 ]
83 },
89 },
84 {
90 {
85 "cell_type": "code",
91 "cell_type": "code",
86 "collapsed": false,
92 "collapsed": false,
87 "input": [
93 "input": [
88 "print(widgets.Widget.get_css.__doc__)"
94 "print(widgets.DOMWidget.get_css.__doc__)"
89 ],
95 ],
90 "language": "python",
96 "language": "python",
91 "metadata": {},
97 "metadata": {},
92 "outputs": [
98 "outputs": [
93 {
99 {
94 "output_type": "stream",
100 "output_type": "stream",
95 "stream": "stdout",
101 "stream": "stdout",
96 "text": [
102 "text": [
97 "Get a CSS property of the widget. Note, this function does not\n",
103 "Get a CSS property of the widget.\n",
98 " actually request the CSS from the front-end; Only properties that have\n",
104 "\n",
99 " been set with set_css can be read.\n",
105 " Note: This function does not actually request the CSS from the \n",
106 " front-end; Only properties that have been set with set_css can be read.\n",
100 "\n",
107 "\n",
101 " Parameters\n",
108 " Parameters\n",
102 " ----------\n",
109 " ----------\n",
103 " key: unicode\n",
110 " key: unicode\n",
104 " CSS key\n",
111 " CSS key\n",
105 " selector: unicode (optional)\n",
112 " selector: unicode (optional)\n",
106 " JQuery selector used when the CSS key/value was set.\n",
113 " JQuery selector used when the CSS key/value was set.\n",
107 " \n"
114 " \n"
108 ]
115 ]
109 }
116 }
110 ],
117 ],
111 "prompt_number": 3
118 "prompt_number": 3
112 },
119 },
113 {
120 {
114 "cell_type": "markdown",
121 "cell_type": "markdown",
115 "metadata": {},
122 "metadata": {},
116 "source": [
123 "source": [
117 "Below is an example that applies CSS attributes to a container to emphasize text."
124 "Below is an example that applies CSS attributes to a container to emphasize text."
118 ]
125 ]
119 },
126 },
120 {
127 {
121 "cell_type": "code",
128 "cell_type": "code",
122 "collapsed": false,
129 "collapsed": false,
123 "input": [
130 "input": [
124 "label = widgets.StringWidget(default_view_name=\"HTMLView\")\n",
131 "label = widgets.LatexWidget()\n",
125 "label.value = \"<strong>ALERT: </strong> Hello World!\"\n",
132 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
126 "container = widgets.ContainerWidget(children=[label])\n",
133 "container = widgets.ContainerWidget(children=[label])\n",
127 "\n",
134 "\n",
128 "# set_css used to set a single CSS attribute.\n",
135 "# set_css used to set a single CSS attribute.\n",
129 "container.set_css('border', '3px solid black') # Border the container\n",
136 "container.set_css('border', '3px solid black') # Border the container\n",
130 "\n",
137 "\n",
131 "# set_css used to set multiple CSS attributes.\n",
138 "# set_css used to set multiple CSS attributes.\n",
132 "container.set_css({'padding': '6px', # Add padding to the container\n",
139 "container.set_css({'padding': '6px', # Add padding to the container\n",
133 " 'background': 'yellow'}) # Fill the container yellow\n",
140 " 'background': 'yellow'}) # Fill the container yellow\n",
134 "\n",
141 "\n",
135 "display(container)"
142 "display(container)"
136 ],
143 ],
137 "language": "python",
144 "language": "python",
138 "metadata": {},
145 "metadata": {},
139 "outputs": [],
146 "outputs": [],
140 "prompt_number": 4
147 "prompt_number": 4
141 },
148 },
142 {
149 {
143 "cell_type": "heading",
150 "cell_type": "heading",
144 "level": 1,
151 "level": 1,
145 "metadata": {},
152 "metadata": {},
146 "source": [
153 "source": [
147 "DOM Classes"
154 "DOM Classes"
148 ]
155 ]
149 },
156 },
150 {
157 {
151 "cell_type": "markdown",
158 "cell_type": "markdown",
152 "metadata": {},
159 "metadata": {},
153 "source": [
160 "source": [
154 "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."
161 "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."
155 ]
162 ]
156 },
163 },
157 {
164 {
158 "cell_type": "code",
165 "cell_type": "code",
159 "collapsed": false,
166 "collapsed": false,
160 "input": [
167 "input": [
161 "print(widgets.Widget.add_class.__doc__)"
168 "print(widgets.DOMWidget.add_class.__doc__)"
162 ],
169 ],
163 "language": "python",
170 "language": "python",
164 "metadata": {},
171 "metadata": {},
165 "outputs": [
172 "outputs": [
166 {
173 {
167 "output_type": "stream",
174 "output_type": "stream",
168 "stream": "stdout",
175 "stream": "stdout",
169 "text": [
176 "text": [
170 "Add class[es] to a DOM element\n",
177 "Add class[es] to a DOM element.\n",
171 "\n",
178 "\n",
172 " Parameters\n",
179 " Parameters\n",
173 " ----------\n",
180 " ----------\n",
174 " class_name: unicode\n",
181 " class_names: unicode or list\n",
175 " Class name(s) to add to the DOM element(s). Multiple class names\n",
182 " Class name(s) to add to the DOM element(s).\n",
176 " must be space separated.\n",
177 " selector: unicode (optional)\n",
183 " selector: unicode (optional)\n",
178 " JQuery selector to select the DOM element(s) that the class(es) will\n",
184 " JQuery selector to select the DOM element(s) that the class(es) will\n",
179 " be added to.\n",
185 " be added to.\n",
180 " \n"
186 " \n"
181 ]
187 ]
182 }
188 }
183 ],
189 ],
184 "prompt_number": 5
190 "prompt_number": 5
185 },
191 },
186 {
192 {
187 "cell_type": "markdown",
193 "cell_type": "markdown",
188 "metadata": {},
194 "metadata": {},
189 "source": [
195 "source": [
190 "Since `add_class` if a DOM operation, it will only affect widgets that have 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). "
196 "Since `add_class` if a DOM operation, it will only affect widgets that have 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). "
191 ]
197 ]
192 },
198 },
193 {
199 {
194 "cell_type": "code",
200 "cell_type": "code",
195 "collapsed": false,
201 "collapsed": false,
196 "input": [
202 "input": [
197 "container = widgets.ContainerWidget()\n",
203 "container = widgets.ContainerWidget()\n",
198 "container.set_css({'border': '3px solid black',\n",
204 "container.set_css({'border': '3px solid black',\n",
199 " 'padding': '6px', \n",
205 " 'padding': '6px', \n",
200 " 'background': 'yellow'}) \n",
206 " 'background': 'yellow'}) \n",
201 "\n",
207 "\n",
202 "label = widgets.StringWidget(default_view_name=\"HTMLView\")\n",
208 "label = widgets.LatexWidget()\n",
203 "label.value = \"<strong>ALERT: </strong> Hello World!\"\n",
209 "label.value = \"$\\\\textbf{ALERT:} Hello World!$\"\n",
204 "container.children = [label]\n",
210 "container.children = [label]\n",
205 "display(container)\n",
211 "display(container)\n",
206 "container.add_class('corner-all') # Must be called AFTER display"
212 "container.add_class('corner-all') # Must be called AFTER display"
207 ],
213 ],
208 "language": "python",
214 "language": "python",
209 "metadata": {},
215 "metadata": {},
210 "outputs": [],
216 "outputs": [],
211 "prompt_number": 7
217 "prompt_number": 6
212 },
218 },
213 {
219 {
214 "cell_type": "markdown",
220 "cell_type": "markdown",
215 "metadata": {},
221 "metadata": {},
216 "source": [
222 "source": [
217 "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 ."
223 "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 ."
218 ]
224 ]
219 },
225 },
220 {
226 {
221 "cell_type": "code",
227 "cell_type": "code",
222 "collapsed": false,
228 "collapsed": false,
223 "input": [
229 "input": [
224 "label = widgets.StringWidget(value = \"<strong>ALERT: </strong> Hello World!\")\n",
230 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
225 "display(label, view_name=\"HTMLView\")\n",
231 "display(label)\n",
226 "\n",
232 "\n",
227 "# Apply twitter bootstrap alert class to the label.\n",
233 "# Apply twitter bootstrap alert class to the label.\n",
228 "label.add_class(\"alert\")"
234 "label.add_class(\"alert\")"
229 ],
235 ],
230 "language": "python",
236 "language": "python",
231 "metadata": {},
237 "metadata": {},
232 "outputs": [],
238 "outputs": [],
233 "prompt_number": 11
239 "prompt_number": 7
234 },
240 },
235 {
241 {
236 "cell_type": "markdown",
242 "cell_type": "markdown",
237 "metadata": {},
243 "metadata": {},
238 "source": [
244 "source": [
239 "The example below shows how bootstrap classes can be used to change button apearance."
245 "The example below shows how bootstrap classes can be used to change button apearance."
240 ]
246 ]
241 },
247 },
242 {
248 {
243 "cell_type": "code",
249 "cell_type": "code",
244 "collapsed": false,
250 "collapsed": false,
245 "input": [
251 "input": [
246 "# List of the bootstrap button styles\n",
252 "# List of the bootstrap button styles\n",
247 "button_classes = ['Default', 'btn-primary', 'btn-info', 'btn-success', \n",
253 "button_classes = ['Default', 'btn-primary', 'btn-info', 'btn-success', \n",
248 " 'btn-warning', 'btn-danger', 'btn-inverse', 'btn-link']\n",
254 " 'btn-warning', 'btn-danger', 'btn-inverse', 'btn-link']\n",
249 "\n",
255 "\n",
250 "# Create each button and apply the style. Also add margin to the buttons so they space\n",
256 "# Create each button and apply the style. Also add margin to the buttons so they space\n",
251 "# themselves nicely.\n",
257 "# themselves nicely.\n",
252 "for i in range(8):\n",
258 "for i in range(8):\n",
253 " button = widgets.ButtonWidget(description=button_classes[i])\n",
259 " button = widgets.ButtonWidget(description=button_classes[i])\n",
254 " button.set_css(\"margin\", \"5px\")\n",
260 " button.set_css(\"margin\", \"5px\")\n",
255 " display(button)\n",
261 " display(button)\n",
256 " if i > 0: # Don't add a class the first button.\n",
262 " if i > 0: # Don't add a class the first button.\n",
257 " button.add_class(button_classes[i])\n",
263 " button.add_class(button_classes[i])\n",
258 " "
264 " "
259 ],
265 ],
260 "language": "python",
266 "language": "python",
261 "metadata": {},
267 "metadata": {},
262 "outputs": [],
268 "outputs": [],
263 "prompt_number": 12
269 "prompt_number": 8
264 },
270 },
265 {
271 {
266 "cell_type": "markdown",
272 "cell_type": "markdown",
267 "metadata": {},
273 "metadata": {},
268 "source": [
274 "source": [
269 "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_widget`, it must be called after the widget has been displayed. The doc string of `remove_class` can be seen below."
275 "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_widget`, it must be called after the widget has been displayed. The doc string of `remove_class` can be seen below."
270 ]
276 ]
271 },
277 },
272 {
278 {
273 "cell_type": "code",
279 "cell_type": "code",
274 "collapsed": false,
280 "collapsed": false,
275 "input": [
281 "input": [
276 "print(widgets.Widget.remove_class.__doc__)"
282 "print(widgets.DOMWidget.remove_class.__doc__)"
277 ],
283 ],
278 "language": "python",
284 "language": "python",
279 "metadata": {},
285 "metadata": {},
280 "outputs": [
286 "outputs": [
281 {
287 {
282 "output_type": "stream",
288 "output_type": "stream",
283 "stream": "stdout",
289 "stream": "stdout",
284 "text": [
290 "text": [
285 "Remove class[es] from a DOM element\n",
291 "Remove class[es] from a DOM element.\n",
286 "\n",
292 "\n",
287 " Parameters\n",
293 " Parameters\n",
288 " ----------\n",
294 " ----------\n",
289 " class_name: unicode\n",
295 " class_names: unicode or list\n",
290 " Class name(s) to remove from the DOM element(s). Multiple class\n",
296 " Class name(s) to remove from the DOM element(s).\n",
291 " names must be space separated.\n",
292 " selector: unicode (optional)\n",
297 " selector: unicode (optional)\n",
293 " JQuery selector to select the DOM element(s) that the class(es) will\n",
298 " JQuery selector to select the DOM element(s) that the class(es) will\n",
294 " be removed from.\n",
299 " be removed from.\n",
295 " \n"
300 " \n"
296 ]
301 ]
297 }
302 }
298 ],
303 ],
299 "prompt_number": 13
304 "prompt_number": 9
300 },
305 },
301 {
306 {
302 "cell_type": "markdown",
307 "cell_type": "markdown",
303 "metadata": {},
308 "metadata": {},
304 "source": [
309 "source": [
305 "The example below animates an alert using different bootstrap styles."
310 "The example below animates an alert using different bootstrap styles."
306 ]
311 ]
307 },
312 },
308 {
313 {
309 "cell_type": "code",
314 "cell_type": "code",
310 "collapsed": false,
315 "collapsed": false,
311 "input": [
316 "input": [
312 "import time\n",
317 "import time\n",
313 "label = widgets.StringWidget(value = \"<strong>ALERT: </strong> Hello World!\")\n",
318 "label = widgets.LatexWidget(value = \"$\\\\textbf{ALERT:} Hello World!$\")\n",
314 "display(label, view_name=\"HTMLView\")\n",
319 "display(label)\n",
315 "\n",
320 "\n",
316 "# Apply twitter bootstrap alert class to the label.\n",
321 "# Apply twitter bootstrap alert class to the label.\n",
317 "label.add_class(\"alert\")\n",
322 "label.add_class(\"alert\")\n",
318 "\n",
323 "\n",
319 "# Animate through additional bootstrap label styles 3 times\n",
324 "# Animate through additional bootstrap label styles 3 times\n",
320 "additional_alert_styles = ['alert-error', 'alert-info', 'alert-success']\n",
325 "additional_alert_styles = ['alert-error', 'alert-info', 'alert-success']\n",
321 "for i in range(3 * len(additional_alert_styles)):\n",
326 "for i in range(3 * len(additional_alert_styles)):\n",
322 " label.add_class(additional_alert_styles[i % 3])\n",
327 " label.add_class(additional_alert_styles[i % 3])\n",
323 " label.remove_class(additional_alert_styles[(i-1) % 3])\n",
328 " label.remove_class(additional_alert_styles[(i-1) % 3])\n",
324 " time.sleep(1)\n",
329 " time.sleep(1)\n",
325 " "
330 " "
326 ],
331 ],
327 "language": "python",
332 "language": "python",
328 "metadata": {},
333 "metadata": {},
329 "outputs": [],
334 "outputs": [],
330 "prompt_number": 14
335 "prompt_number": 10
331 },
332 {
333 "cell_type": "code",
334 "collapsed": false,
335 "input": [],
336 "language": "python",
337 "metadata": {},
338 "outputs": []
339 }
336 }
340 ],
337 ],
341 "metadata": {}
338 "metadata": {}
342 }
339 }
343 ]
340 ]
344 } No newline at end of file
341 }
@@ -1,312 +1,313 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "cell_tags": [
3 "cell_tags": [
4 [
4 [
5 "<None>",
5 "<None>",
6 null
6 null
7 ]
7 ]
8 ],
8 ],
9 "name": ""
9 "name": ""
10 },
10 },
11 "nbformat": 3,
11 "nbformat": 3,
12 "nbformat_minor": 0,
12 "nbformat_minor": 0,
13 "worksheets": [
13 "worksheets": [
14 {
14 {
15 "cells": [
15 "cells": [
16 {
16 {
17 "cell_type": "code",
17 "cell_type": "code",
18 "collapsed": false,
18 "collapsed": false,
19 "input": [
19 "input": [
20 "from IPython.html import widgets # Widget definitions\n",
20 "from IPython.html import widgets # Widget definitions\n",
21 "from IPython.display import display # Used to display widgets in the notebook"
21 "from IPython.display import display # Used to display widgets in the notebook"
22 ],
22 ],
23 "language": "python",
23 "language": "python",
24 "metadata": {},
24 "metadata": {},
25 "outputs": [],
25 "outputs": [],
26 "prompt_number": 1
26 "prompt_number": 1
27 },
27 },
28 {
28 {
29 "cell_type": "heading",
29 "cell_type": "heading",
30 "level": 1,
30 "level": 1,
31 "metadata": {},
31 "metadata": {},
32 "source": [
32 "source": [
33 "Alignment"
33 "Alignment"
34 ]
34 ]
35 },
35 },
36 {
36 {
37 "cell_type": "markdown",
37 "cell_type": "markdown",
38 "metadata": {},
38 "metadata": {},
39 "source": [
39 "source": [
40 "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) "
40 "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) "
41 ]
41 ]
42 },
42 },
43 {
43 {
44 "cell_type": "code",
44 "cell_type": "code",
45 "collapsed": false,
45 "collapsed": false,
46 "input": [
46 "input": [
47 "display(widgets.StringWidget(description=\"a:\"))\n",
47 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
48 "display(widgets.StringWidget(description=\"aa:\"))\n",
48 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
49 "display(widgets.StringWidget(description=\"aaa:\"))"
49 "display(widgets.TextBoxWidget(description=\"aaa:\"))"
50 ],
50 ],
51 "language": "python",
51 "language": "python",
52 "metadata": {},
52 "metadata": {},
53 "outputs": [],
53 "outputs": [],
54 "prompt_number": 2
54 "prompt_number": 2
55 },
55 },
56 {
56 {
57 "cell_type": "markdown",
57 "cell_type": "markdown",
58 "metadata": {},
58 "metadata": {},
59 "source": [
59 "source": [
60 "If a label is longer than the minimum width, the widget is shifted to the right (as seen below)."
60 "If a label is longer than the minimum width, the widget is shifted to the right (as seen below)."
61 ]
61 ]
62 },
62 },
63 {
63 {
64 "cell_type": "code",
64 "cell_type": "code",
65 "collapsed": false,
65 "collapsed": false,
66 "input": [
66 "input": [
67 "display(widgets.StringWidget(description=\"a:\"))\n",
67 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
68 "display(widgets.StringWidget(description=\"aa:\"))\n",
68 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
69 "display(widgets.StringWidget(description=\"aaa:\"))\n",
69 "display(widgets.TextBoxWidget(description=\"aaa:\"))\n",
70 "display(widgets.StringWidget(description=\"aaaaaaaaaaaaaaaaaa:\"))"
70 "display(widgets.TextBoxWidget(description=\"aaaaaaaaaaaaaaaaaa:\"))"
71 ],
71 ],
72 "language": "python",
72 "language": "python",
73 "metadata": {},
73 "metadata": {},
74 "outputs": [],
74 "outputs": [],
75 "prompt_number": 3
75 "prompt_number": 3
76 },
76 },
77 {
77 {
78 "cell_type": "markdown",
78 "cell_type": "markdown",
79 "metadata": {},
79 "metadata": {},
80 "source": [
80 "source": [
81 "If a `description` is not set for the widget, the label is not displayed (as seen below)."
81 "If a `description` is not set for the widget, the label is not displayed (as seen below)."
82 ]
82 ]
83 },
83 },
84 {
84 {
85 "cell_type": "code",
85 "cell_type": "code",
86 "collapsed": false,
86 "collapsed": false,
87 "input": [
87 "input": [
88 "display(widgets.StringWidget(description=\"a:\"))\n",
88 "display(widgets.TextBoxWidget(description=\"a:\"))\n",
89 "display(widgets.StringWidget(description=\"aa:\"))\n",
89 "display(widgets.TextBoxWidget(description=\"aa:\"))\n",
90 "display(widgets.StringWidget(description=\"aaa:\"))\n",
90 "display(widgets.TextBoxWidget(description=\"aaa:\"))\n",
91 "display(widgets.StringWidget())"
91 "display(widgets.TextBoxWidget())"
92 ],
92 ],
93 "language": "python",
93 "language": "python",
94 "metadata": {},
94 "metadata": {},
95 "outputs": [],
95 "outputs": [],
96 "prompt_number": 4
96 "prompt_number": 4
97 },
97 },
98 {
98 {
99 "cell_type": "heading",
99 "cell_type": "heading",
100 "level": 1,
100 "level": 1,
101 "metadata": {},
101 "metadata": {},
102 "source": [
102 "source": [
103 "Custom Alignment"
103 "Custom Alignment"
104 ]
104 ]
105 },
105 },
106 {
106 {
107 "cell_type": "markdown",
107 "cell_type": "markdown",
108 "metadata": {},
108 "metadata": {},
109 "source": [
109 "source": [
110 "`ContainerWidget`s allow for custom alignment of widgets. The `hbox` and `vbox` methods (parameterless) cause the `ContainerWidget` to both horizontally and vertically align its children. The following example compares `vbox` to `hbox`."
110 "`ContainerWidget`s allow for custom alignment of widgets. The `hbox` and `vbox` methods (parameterless) cause the `ContainerWidget` to both horizontally and vertically align its children. The following example compares `vbox` to `hbox`."
111 ]
111 ]
112 },
112 },
113 {
113 {
114 "cell_type": "code",
114 "cell_type": "code",
115 "collapsed": false,
115 "collapsed": false,
116 "input": [
116 "input": [
117 "child_style = {\n",
117 "child_style = {\n",
118 " 'background': '#77CC77',\n",
118 " 'background': '#77CC77',\n",
119 " 'padding': '25px',\n",
119 " 'padding': '25px',\n",
120 " 'margin': '5px',\n",
120 " 'margin': '5px',\n",
121 " 'font-size': 'xx-large',\n",
121 " 'font-size': 'xx-large',\n",
122 " 'color': 'white',\n",
122 " 'color': 'white',\n",
123 "}\n",
123 "}\n",
124 "\n",
124 "\n",
125 "def make_container(title):\n",
125 "def make_container(title):\n",
126 " display(widgets.StringWidget(default_view_name='HTMLView', value='<h2><br>' + title + '</h2>'))\n",
126 " header = widgets.LatexWidget(value=title) \n",
127 " display(header)\n",
128 " header.set_css({\n",
129 " 'font-size': '30pt',\n",
130 " 'margin-top': '40pt',\n",
131 " 'margin-bottom': '20pt',\n",
132 " })\n",
133 " \n",
127 " container = widgets.ContainerWidget()\n",
134 " container = widgets.ContainerWidget()\n",
128 " container.set_css('background', '#999999')\n",
135 " container.set_css('background', '#999999')\n",
129 " display(container)\n",
136 " display(container)\n",
130 " return container\n",
137 " return container\n",
131 "\n",
138 "\n",
132 "def fill_container(container):\n",
139 "def fill_container(container):\n",
133 " components = []\n",
140 " components = []\n",
134 " for i in range(3):\n",
141 " for i in range(3):\n",
135 " components.append(widgets.StringWidget(default_view_name='HTMLView', value=\"ABC\"[i]))\n",
142 " components.append(widgets.LatexWidget(value=\"ABC\"[i]))\n",
136 " components[i].set_css(child_style)\n",
143 " components[i].set_css(child_style)\n",
137 " container.children = components\n",
144 " container.children = components\n",
138 " \n",
145 " \n",
139 "container = make_container('VBox')\n",
146 "container = make_container('VBox')\n",
140 "container.vbox()\n",
147 "container.add_class('vbox')\n",
141 "fill_container(container)\n",
148 "fill_container(container)\n",
142 "\n",
149 "\n",
143 "container = make_container('HBox')\n",
150 "container = make_container('HBox')\n",
144 "container.hbox()\n",
151 "container.add_class('hbox')\n",
145 "fill_container(container)\n"
152 "fill_container(container)\n"
146 ],
153 ],
147 "language": "python",
154 "language": "python",
148 "metadata": {},
155 "metadata": {},
149 "outputs": [],
156 "outputs": [],
150 "prompt_number": 5
157 "prompt_number": 5
151 },
158 },
152 {
159 {
153 "cell_type": "markdown",
160 "cell_type": "markdown",
154 "metadata": {},
161 "metadata": {},
155 "source": [
162 "source": [
156 "The `ContainerWidget` `pack_start`, `pack_center`, and `pack_end` methods (parameterless) adjust the alignment of the widgets on the axis that they are being rendered on. Below is an example of the different alignments."
163 "The `ContainerWidget` `pack_start`, `pack_center`, and `pack_end` methods (parameterless) adjust the alignment of the widgets on the axis that they are being rendered on. Below is an example of the different alignments."
157 ]
164 ]
158 },
165 },
159 {
166 {
160 "cell_type": "code",
167 "cell_type": "code",
161 "collapsed": false,
168 "collapsed": false,
162 "input": [
169 "input": [
163 "container = make_container('HBox Pack Start')\n",
170 "container = make_container('HBox Pack Start')\n",
164 "container.hbox()\n",
171 "container.add_class('hbox')\n",
165 "container.pack_start()\n",
172 "container.add_class('start')\n",
166 "fill_container(container)\n",
173 "fill_container(container)\n",
167 " \n",
174 " \n",
168 "container = make_container('HBox Pack Center')\n",
175 "container = make_container('HBox Pack Center')\n",
169 "container.hbox()\n",
176 "container.add_class('hbox')\n",
170 "container.pack_center()\n",
177 "container.add_class('center')\n",
171 "fill_container(container)\n",
178 "fill_container(container)\n",
172 " \n",
179 " \n",
173 "container = make_container('HBox Pack End')\n",
180 "container = make_container('HBox Pack End')\n",
174 "container.hbox()\n",
181 "container.add_class('hbox')\n",
175 "container.pack_end()\n",
182 "container.add_class('end')\n",
176 "fill_container(container)"
183 "fill_container(container)"
177 ],
184 ],
178 "language": "python",
185 "language": "python",
179 "metadata": {},
186 "metadata": {},
180 "outputs": [],
187 "outputs": [],
181 "prompt_number": 6
188 "prompt_number": 6
182 },
189 },
183 {
190 {
184 "cell_type": "markdown",
191 "cell_type": "markdown",
185 "metadata": {},
192 "metadata": {},
186 "source": [
193 "source": [
187 "The `ContainerWidget` `flex0`, `flex1`, and `flex2` methods (parameterless) modify the containers flexibility. Changing a container flexibility affects how and if the container will occupy the remaining space. Setting `flex0` has the same result as setting no flex. Below is an example of different flex configurations. The number on the boxes correspond to the applied flex."
194 "The `ContainerWidget` `flex0`, `flex1`, and `flex2` methods (parameterless) modify the containers flexibility. Changing a container flexibility affects how and if the container will occupy the remaining space. Setting `flex0` has the same result as setting no flex. Below is an example of different flex configurations. The number on the boxes correspond to the applied flex."
188 ]
195 ]
189 },
196 },
190 {
197 {
191 "cell_type": "code",
198 "cell_type": "code",
192 "collapsed": false,
199 "collapsed": false,
193 "input": [
200 "input": [
194 "def fill_container(container, flexes):\n",
201 "def fill_container(container, flexes):\n",
195 " components = []\n",
202 " components = []\n",
196 " for i in range(len(flexes)):\n",
203 " for i in range(len(flexes)):\n",
197 " components.append(widgets.ContainerWidget(parent=container))\n",
204 " components.append(widgets.ContainerWidget())\n",
198 " components[i].set_css(child_style)\n",
205 " components[i].set_css(child_style)\n",
199 " \n",
206 " \n",
200 " label = widgets.StringWidget(parent=components[i], default_view_name='HTMLView', value=str(flexes[i]))\n",
207 " label = widgets.LatexWidget(value=str(flexes[i]))\n",
208 " components[i].children = [label]\n",
209 " container.children = components\n",
201 " \n",
210 " \n",
211 " for i in range(len(flexes)):\n",
202 " if flexes[i] == 0:\n",
212 " if flexes[i] == 0:\n",
203 " components[i].flex0()\n",
213 " components[i].add_class('box-flex0')\n",
204 " elif flexes[i] == 1:\n",
214 " elif flexes[i] == 1:\n",
205 " components[i].flex1()\n",
215 " components[i].add_class('box-flex1')\n",
206 " elif flexes[i] == 2:\n",
216 " elif flexes[i] == 2:\n",
207 " components[i].flex2()\n",
217 " components[i].add_class('box-flex2')\n",
208 " container.children = components\n",
209 " \n",
218 " \n",
210 "container = make_container('Different Flex Configurations')\n",
219 "container = make_container('Different Flex Configurations')\n",
211 "container.hbox()\n",
220 "container.add_class('hbox')\n",
212 "fill_container(container, [0, 0, 0])\n",
221 "fill_container(container, [0, 0, 0])\n",
213 " \n",
222 " \n",
214 "container = make_container('')\n",
223 "container = make_container('')\n",
215 "container.hbox()\n",
224 "container.add_class('hbox')\n",
216 "fill_container(container, [0, 0, 1])\n",
225 "fill_container(container, [0, 0, 1])\n",
217 " \n",
226 " \n",
218 "container = make_container('')\n",
227 "container = make_container('')\n",
219 "container.hbox()\n",
228 "container.add_class('hbox')\n",
220 "fill_container(container, [0, 1, 1])\n",
229 "fill_container(container, [0, 1, 1])\n",
221 " \n",
230 " \n",
222 "container = make_container('')\n",
231 "container = make_container('')\n",
223 "container.hbox()\n",
232 "container.add_class('hbox')\n",
224 "fill_container(container, [0, 2, 2])\n",
233 "fill_container(container, [0, 2, 2])\n",
225 " \n",
234 " \n",
226 "container = make_container('')\n",
235 "container = make_container('')\n",
227 "container.hbox()\n",
236 "container.add_class('hbox')\n",
228 "fill_container(container, [0, 1, 2])\n",
237 "fill_container(container, [0, 1, 2])\n",
229 " \n",
238 " \n",
230 "container = make_container('')\n",
239 "container = make_container('')\n",
231 "container.hbox()\n",
240 "container.add_class('hbox')\n",
232 "fill_container(container, [1, 1, 2])"
241 "fill_container(container, [1, 1, 2])"
233 ],
242 ],
234 "language": "python",
243 "language": "python",
235 "metadata": {},
244 "metadata": {},
236 "outputs": [],
245 "outputs": [],
237 "prompt_number": 8
246 "prompt_number": 7
238 },
247 },
239 {
248 {
240 "cell_type": "markdown",
249 "cell_type": "markdown",
241 "metadata": {},
250 "metadata": {},
242 "source": [
251 "source": [
243 "The `ContainerWidget` `align_start`, `align_center`, and `align_end` methods (parameterless) 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."
252 "The `ContainerWidget` `align_start`, `align_center`, and `align_end` methods (parameterless) 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."
244 ]
253 ]
245 },
254 },
246 {
255 {
247 "cell_type": "code",
256 "cell_type": "code",
248 "collapsed": false,
257 "collapsed": false,
249 "input": [
258 "input": [
250 "def fill_container(container):\n",
259 "def fill_container(container):\n",
251 " components = []\n",
260 " components = []\n",
252 " for i in range(3):\n",
261 " for i in range(3):\n",
253 " components.append(widgets.StringWidget(parent=container, default_view_name='HTMLView', value=\"ABC\"[i]))\n",
262 " components.append(widgets.LatexWidget(parent=container, value=\"ABC\"[i]))\n",
254 " components[i].set_css(child_style)\n",
263 " components[i].set_css(child_style)\n",
255 " components[i].set_css('height', str((i+1) * 50) + 'px')\n",
264 " components[i].set_css('height', str((i+1) * 50) + 'px')\n",
256 " container.children = components\n",
265 " container.children = components\n",
257 "\n",
266 "\n",
258 "container = make_container('HBox Align Start')\n",
267 "container = make_container('HBox Align Start')\n",
259 "container.hbox()\n",
268 "container.add_class(\"hbox\")\n",
260 "container.align_start()\n",
269 "container.add_class(\"align-start\")\n",
261 "fill_container(container)\n",
270 "fill_container(container)\n",
262 " \n",
271 " \n",
263 "container = make_container('HBox Align Center')\n",
272 "container = make_container('HBox Align Center')\n",
264 "container.hbox()\n",
273 "container.add_class(\"hbox\")\n",
265 "container.align_center()\n",
274 "container.add_class(\"align-center\")\n",
266 "fill_container(container)\n",
275 "fill_container(container)\n",
267 " \n",
276 " \n",
268 "container = make_container('HBox Align End')\n",
277 "container = make_container('HBox Align End')\n",
269 "container.hbox()\n",
278 "container.add_class(\"hbox\")\n",
270 "container.align_end()\n",
279 "container.add_class(\"align-end\")\n",
271 "fill_container(container)"
280 "fill_container(container)"
272 ],
281 ],
273 "language": "python",
282 "language": "python",
274 "metadata": {},
283 "metadata": {},
275 "outputs": [],
284 "outputs": [],
276 "prompt_number": 9
285 "prompt_number": 8
277 },
286 },
278 {
287 {
279 "cell_type": "markdown",
288 "cell_type": "markdown",
280 "metadata": {},
289 "metadata": {},
281 "source": [
290 "source": [
282 "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."
291 "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."
283 ]
292 ]
284 },
293 },
285 {
294 {
286 "cell_type": "code",
295 "cell_type": "code",
287 "collapsed": false,
296 "collapsed": false,
288 "input": [
297 "input": [
289 "container = widgets.ContainerWidget()\n",
298 "container = widgets.ContainerWidget()\n",
290 "container.hbox()\n",
299 "container.children=[widgets.FloatSliderWidget(orientation='vertical', description=str(i+1), value=50.0) \n",
291 "container.children=[widgets.FloatRangeWidget(orientation='vertical', description=str(i+1), value=50.0) \n",
292 " for i in range(15)]\n",
300 " for i in range(15)]\n",
293 "display(container)"
301 "display(container)\n",
302 "container.add_class('hbox')"
294 ],
303 ],
295 "language": "python",
304 "language": "python",
296 "metadata": {},
305 "metadata": {},
297 "outputs": [],
306 "outputs": [],
298 "prompt_number": 11
307 "prompt_number": 9
299 },
300 {
301 "cell_type": "code",
302 "collapsed": false,
303 "input": [],
304 "language": "python",
305 "metadata": {},
306 "outputs": []
307 }
308 }
308 ],
309 ],
309 "metadata": {}
310 "metadata": {}
310 }
311 }
311 ]
312 ]
312 } No newline at end of file
313 }
@@ -1,1246 +1,1065 b''
1 {
1 {
2 "metadata": {
2 "metadata": {
3 "cell_tags": [
3 "cell_tags": [
4 [
4 [
5 "<None>",
5 "<None>",
6 null
6 null
7 ]
7 ]
8 ],
8 ],
9 "name": ""
9 "name": ""
10 },
10 },
11 "nbformat": 3,
11 "nbformat": 3,
12 "nbformat_minor": 0,
12 "nbformat_minor": 0,
13 "worksheets": [
13 "worksheets": [
14 {
14 {
15 "cells": [
15 "cells": [
16 {
16 {
17 "cell_type": "markdown",
17 "cell_type": "markdown",
18 "metadata": {},
18 "metadata": {},
19 "source": [
19 "source": [
20 "Before reading, the author recommends the reader to review\n",
20 "Before reading, the author recommends the reader to review\n",
21 "\n",
21 "\n",
22 "- [MVC prgramming](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)\n",
22 "- [MVC prgramming](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)\n",
23 "- [Backbone.js](https://www.codeschool.com/courses/anatomy-of-backbonejs)\n",
23 "- [Backbone.js](https://www.codeschool.com/courses/anatomy-of-backbonejs)\n",
24 "- [The widget IPEP](https://github.com/ipython/ipython/wiki/IPEP-23%3A-Backbone.js-Widgets)\n",
24 "- [The widget IPEP](https://github.com/ipython/ipython/wiki/IPEP-23%3A-Backbone.js-Widgets)\n",
25 "- [The original widget PR discussion](https://github.com/ipython/ipython/pull/4374)"
25 "- [The original widget PR discussion](https://github.com/ipython/ipython/pull/4374)"
26 ]
26 ]
27 },
27 },
28 {
28 {
29 "cell_type": "code",
29 "cell_type": "code",
30 "collapsed": false,
30 "collapsed": false,
31 "input": [
31 "input": [
32 "from __future__ import print_function # For py 2.7 compat\n",
32 "from __future__ import print_function # For py 2.7 compat\n",
33 "\n",
33 "\n",
34 "from IPython.html import widgets # Widget definitions\n",
34 "from IPython.html import widgets # Widget definitions\n",
35 "from IPython.display import display # Used to display widgets in the notebook"
35 "from IPython.display import display # Used to display widgets in the notebook"
36 ],
36 ],
37 "language": "python",
37 "language": "python",
38 "metadata": {},
38 "metadata": {},
39 "outputs": [],
39 "outputs": [],
40 "prompt_number": 15
40 "prompt_number": 19
41 },
41 },
42 {
42 {
43 "cell_type": "markdown",
43 "cell_type": "markdown",
44 "metadata": {},
44 "metadata": {},
45 "source": [
45 "source": [
46 "The 3 part of this tutorial requires the 3rd party `dateutil` library. https://pypi.python.org/pypi/python-dateutil"
46 "The 3 part of this tutorial requires the 3rd party `dateutil` library. https://pypi.python.org/pypi/python-dateutil"
47 ]
47 ]
48 },
48 },
49 {
49 {
50 "cell_type": "code",
50 "cell_type": "code",
51 "collapsed": false,
51 "collapsed": false,
52 "input": [
52 "input": [
53 "# Import the dateutil library to parse date strings.\n",
53 "# Import the dateutil library to parse date strings.\n",
54 "from dateutil import parser"
54 "from dateutil import parser"
55 ],
55 ],
56 "language": "python",
56 "language": "python",
57 "metadata": {},
57 "metadata": {},
58 "outputs": [],
58 "outputs": [],
59 "prompt_number": 16
59 "prompt_number": 20
60 },
60 },
61 {
61 {
62 "cell_type": "heading",
62 "cell_type": "heading",
63 "level": 1,
63 "level": 1,
64 "metadata": {},
64 "metadata": {},
65 "source": [
65 "source": [
66 "Abstract"
66 "Abstract"
67 ]
67 ]
68 },
68 },
69 {
69 {
70 "cell_type": "markdown",
70 "cell_type": "markdown",
71 "metadata": {},
71 "metadata": {},
72 "source": [
72 "source": [
73 "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."
73 "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."
74 ]
74 ]
75 },
75 },
76 {
76 {
77 "cell_type": "heading",
77 "cell_type": "heading",
78 "level": 1,
78 "level": 1,
79 "metadata": {},
79 "metadata": {},
80 "source": [
80 "source": [
81 "Section 1 - Basics"
81 "Section 1 - Basics"
82 ]
82 ]
83 },
83 },
84 {
84 {
85 "cell_type": "heading",
85 "cell_type": "heading",
86 "level": 2,
86 "level": 2,
87 "metadata": {},
87 "metadata": {},
88 "source": [
88 "source": [
89 "Python"
89 "Python"
90 ]
90 ]
91 },
91 },
92 {
92 {
93 "cell_type": "markdown",
93 "cell_type": "markdown",
94 "metadata": {},
94 "metadata": {},
95 "source": [
95 "source": [
96 "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."
96 "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."
97 ]
97 ]
98 },
98 },
99 {
99 {
100 "cell_type": "code",
100 "cell_type": "code",
101 "collapsed": false,
101 "collapsed": false,
102 "input": [
102 "input": [
103 "# Import the base Widget class and the traitlets Unicode class.\n",
103 "# Import the base Widget class and the traitlets Unicode class.\n",
104 "from IPython.html.widgets import Widget\n",
104 "from IPython.html.widgets import DOMWidget\n",
105 "from IPython.utils.traitlets import Unicode\n",
105 "from IPython.utils.traitlets import Unicode\n",
106 "\n",
106 "\n",
107 "# Define our DateWidget and its target model and default view.\n",
107 "# Define our DateWidget and its target model and default view.\n",
108 "class DateWidget(Widget):\n",
108 "class DateWidget(DOMWidget):\n",
109 " target_name = Unicode('DateWidgetModel')\n",
109 " _view_name = Unicode('DatePickerView', sync=True)"
110 " default_view_name = Unicode('DatePickerView')"
111 ],
110 ],
112 "language": "python",
111 "language": "python",
113 "metadata": {},
112 "metadata": {},
114 "outputs": [],
113 "outputs": [],
115 "prompt_number": 17
114 "prompt_number": 21
116 },
115 },
117 {
116 {
118 "cell_type": "markdown",
117 "cell_type": "markdown",
119 "metadata": {},
118 "metadata": {},
120 "source": [
119 "source": [
121 "- **target_name** is a special `Widget` property that tells the widget framework which Backbone model in the front-end corresponds to this widget.\n",
120 "- **_view_name** is the default Backbone view to display when the user calls `display` to display an instance of this widget.\n"
122 "- **default_view_name** is the default Backbone view to display when the user calls `display` to display an instance of this widget.\n"
123 ]
121 ]
124 },
122 },
125 {
123 {
126 "cell_type": "heading",
124 "cell_type": "heading",
127 "level": 2,
125 "level": 2,
128 "metadata": {},
126 "metadata": {},
129 "source": [
127 "source": [
130 "JavaScript"
128 "JavaScript"
131 ]
129 ]
132 },
130 },
133 {
131 {
134 "cell_type": "markdown",
132 "cell_type": "markdown",
135 "metadata": {},
133 "metadata": {},
136 "source": [
134 "source": [
137 "In the IPython notebook [require.js](http://requirejs.org/) is used to load JavaScript dependencies. All IPython widget code depends on `notebook/js/widget.js`. In it the base widget model, base view, and widget manager are defined. We need to use require.js to include this file:"
135 "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:"
138 ]
136 ]
139 },
137 },
140 {
138 {
141 "cell_type": "code",
139 "cell_type": "code",
142 "collapsed": false,
140 "collapsed": false,
143 "input": [
141 "input": [
144 "%%javascript\n",
142 "%%javascript\n",
145 "\n",
143 "\n",
146 "require([\"notebook/js/widgets/\"], function(){\n",
144 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
147 "\n",
145 "\n",
148 "});"
146 "});"
149 ],
147 ],
150 "language": "python",
148 "language": "python",
151 "metadata": {},
149 "metadata": {},
152 "outputs": [
150 "outputs": [
153 {
151 {
154 "javascript": [
152 "javascript": [
155 "\n",
153 "\n",
156 "require([\"notebook/js/widget\"], function(){\n",
154 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
157 "\n",
155 "\n",
158 "});"
156 "});"
159 ],
157 ],
160 "metadata": {},
158 "metadata": {},
161 "output_type": "display_data",
159 "output_type": "display_data",
162 "text": [
160 "text": [
163 "<IPython.core.display.Javascript at 0x10a26a850>"
161 "<IPython.core.display.Javascript at 0x1e409d0>"
164 ]
162 ]
165 }
163 }
166 ],
164 ],
167 "prompt_number": 18
165 "prompt_number": 22
168 },
169 {
170 "cell_type": "markdown",
171 "metadata": {},
172 "source": [
173 "The next step is to add a definition for the widget's model. It's important to extend the `IPython.WidgetModel` which extends the Backbone.js base model instead of trying to extend the Backbone.js base model directly. After defining the model, it needs to be registed with the widget manager using the `target_name` used in the Python code."
174 ]
175 },
176 {
177 "cell_type": "code",
178 "collapsed": false,
179 "input": [
180 "%%javascript\n",
181 "\n",
182 "require([\"notebook/js/widgets/base\"], function(){\n",
183 " \n",
184 " // Define the DateModel and register it with the widget manager.\n",
185 " var DateModel = IPython.WidgetModel.extend({});\n",
186 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
187 "});"
188 ],
189 "language": "python",
190 "metadata": {},
191 "outputs": [
192 {
193 "javascript": [
194 "\n",
195 "require([\"notebook/js/widgets/base\"], function(){\n",
196 " \n",
197 " // Define the DateModel and register it with the widget manager.\n",
198 " var DateModel = IPython.WidgetModel.extend({});\n",
199 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
200 "});"
201 ],
202 "metadata": {},
203 "output_type": "display_data",
204 "text": [
205 "<IPython.core.display.Javascript at 0x10a26aa10>"
206 ]
207 }
208 ],
209 "prompt_number": 20
210 },
166 },
211 {
167 {
212 "cell_type": "markdown",
168 "cell_type": "markdown",
213 "metadata": {},
169 "metadata": {},
214 "source": [
170 "source": [
215 "Now that the model is defined, we need to define a view that can be used to represent the model. To do this, the `IPython.WidgetView` 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 like the model was.\n",
171 "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 like the model was.\n",
216 "\n",
172 "\n",
217 "**Final JavaScript code below:**"
173 "**Final JavaScript code below:**"
218 ]
174 ]
219 },
175 },
220 {
176 {
221 "cell_type": "code",
177 "cell_type": "code",
222 "collapsed": false,
178 "collapsed": false,
223 "input": [
179 "input": [
224 "%%javascript\n",
180 "%%javascript\n",
225 "\n",
181 "\n",
226 "require([\"notebook/js/widgets/base\"], function(){\n",
182 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
227 " \n",
228 " // Define the DateModel and register it with the widget manager.\n",
229 " var DateModel = IPython.WidgetModel.extend({});\n",
230 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
231 " \n",
183 " \n",
232 " // Define the DatePickerView\n",
184 " // Define the DatePickerView\n",
233 " var DatePickerView = IPython.WidgetView.extend({\n",
185 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
234 " \n",
186 " render: function(){ this.$el.text('Hello World!'); },\n",
235 " render: function(){\n",
236 " this.$el = $('<div />')\n",
237 " .html('Hello World!');\n",
238 " },\n",
239 " });\n",
187 " });\n",
240 " \n",
188 " \n",
241 " // Register the DatePickerView with the widget manager.\n",
189 " // Register the DatePickerView with the widget manager.\n",
242 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
190 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
243 "});"
191 "});"
244 ],
192 ],
245 "language": "python",
193 "language": "python",
246 "metadata": {},
194 "metadata": {},
247 "outputs": [
195 "outputs": [
248 {
196 {
249 "javascript": [
197 "javascript": [
250 "\n",
198 "\n",
251 "require([\"notebook/js/widgets/base\"], function(){\n",
199 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
252 " \n",
253 " // Define the DateModel and register it with the widget manager.\n",
254 " var DateModel = IPython.WidgetModel.extend({});\n",
255 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
256 " \n",
200 " \n",
257 " // Define the DatePickerView\n",
201 " // Define the DatePickerView\n",
258 " var DatePickerView = IPython.WidgetView.extend({\n",
202 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
259 " \n",
203 " render: function(){ this.$el.text('Hello World!'); },\n",
260 " render: function(){\n",
261 " this.$el = $('<div />')\n",
262 " .html('Hello World!');\n",
263 " },\n",
264 " });\n",
204 " });\n",
265 " \n",
205 " \n",
266 " // Register the DatePickerView with the widget manager.\n",
206 " // Register the DatePickerView with the widget manager.\n",
267 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
207 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
268 "});"
208 "});"
269 ],
209 ],
270 "metadata": {},
210 "metadata": {},
271 "output_type": "display_data",
211 "output_type": "display_data",
272 "text": [
212 "text": [
273 "<IPython.core.display.Javascript at 0x10a26ae90>"
213 "<IPython.core.display.Javascript at 0x1e40a50>"
274 ]
214 ]
275 }
215 }
276 ],
216 ],
277 "prompt_number": 23
217 "prompt_number": 23
278 },
218 },
279 {
219 {
280 "cell_type": "heading",
220 "cell_type": "heading",
281 "level": 2,
221 "level": 2,
282 "metadata": {},
222 "metadata": {},
283 "source": [
223 "source": [
284 "Test"
224 "Test"
285 ]
225 ]
286 },
226 },
287 {
227 {
288 "cell_type": "markdown",
228 "cell_type": "markdown",
289 "metadata": {},
229 "metadata": {},
290 "source": [
230 "source": [
291 "To test, create the widget the same way that the other widgets are created."
231 "To test, create the widget the same way that the other widgets are created."
292 ]
232 ]
293 },
233 },
294 {
234 {
295 "cell_type": "code",
235 "cell_type": "code",
296 "collapsed": false,
236 "collapsed": false,
297 "input": [
237 "input": [
298 "my_widget = DateWidget()\n",
238 "DateWidget()"
299 "display(my_widget)"
300 ],
239 ],
301 "language": "python",
240 "language": "python",
302 "metadata": {},
241 "metadata": {},
303 "outputs": [],
242 "outputs": [],
304 "prompt_number": 24
243 "prompt_number": 24
305 },
244 },
306 {
245 {
307 "cell_type": "heading",
246 "cell_type": "heading",
308 "level": 1,
247 "level": 1,
309 "metadata": {},
248 "metadata": {},
310 "source": [
249 "source": [
311 "Section 2 - Something useful"
250 "Section 2 - Something useful"
312 ]
251 ]
313 },
252 },
314 {
253 {
315 "cell_type": "heading",
254 "cell_type": "heading",
316 "level": 2,
255 "level": 2,
317 "metadata": {},
256 "metadata": {},
318 "source": [
257 "source": [
319 "Python"
258 "Python"
320 ]
259 ]
321 },
260 },
322 {
261 {
323 "cell_type": "markdown",
262 "cell_type": "markdown",
324 "metadata": {},
263 "metadata": {},
325 "source": [
264 "source": [
326 "In the last section we created a simple widget that displayed *Hello World!* There was no custom state information associated with the widget. 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 added to the the `_keys` list. The `_keys` list tells the widget machinery what traitlets should be synced with the front-end. Adding this to the code from the last section:"
265 "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 fron-end. Adding this to the code from the last section:"
327 ]
266 ]
328 },
267 },
329 {
268 {
330 "cell_type": "code",
269 "cell_type": "code",
331 "collapsed": false,
270 "collapsed": false,
332 "input": [
271 "input": [
333 "# Import the base Widget class and the traitlets Unicode class.\n",
272 "# Import the base Widget class and the traitlets Unicode class.\n",
334 "from IPython.html.widgets import Widget\n",
273 "from IPython.html.widgets import DOMWidget\n",
335 "from IPython.utils.traitlets import Unicode\n",
274 "from IPython.utils.traitlets import Unicode\n",
336 "\n",
275 "\n",
337 "# Define our DateWidget and its target model and default view.\n",
276 "# Define our DateWidget and its target model and default view.\n",
338 "class DateWidget(Widget):\n",
277 "class DateWidget(DOMWidget):\n",
339 " target_name = Unicode('DateWidgetModel')\n",
278 " _view_name = Unicode('DatePickerView', sync=True)\n",
340 " default_view_name = Unicode('DatePickerView')\n",
279 " value = Unicode(sync=True)"
341 " \n",
342 " # Define the custom state properties to sync with the front-end\n",
343 " _keys = ['value']\n",
344 " value = Unicode()"
345 ],
280 ],
346 "language": "python",
281 "language": "python",
347 "metadata": {},
282 "metadata": {},
348 "outputs": [],
283 "outputs": [],
349 "prompt_number": 25
284 "prompt_number": 25
350 },
285 },
351 {
286 {
352 "cell_type": "heading",
287 "cell_type": "heading",
353 "level": 2,
288 "level": 2,
354 "metadata": {},
289 "metadata": {},
355 "source": [
290 "source": [
356 "JavaScript"
291 "JavaScript"
357 ]
292 ]
358 },
293 },
359 {
294 {
360 "cell_type": "markdown",
295 "cell_type": "markdown",
361 "metadata": {},
296 "metadata": {},
362 "source": [
297 "source": [
363 "In the JavaScript there is no need to define the same properties in the JavaScript model. When the JavaScript model is created for the first time, it copies all of the attributes from the Python model. We need to replace *Hello World!* with an actual HTML date picker widget."
298 "In the JavaScript there is no need to define the same properties in the JavaScript model. When the JavaScript model is created for the first time, it copies all of the attributes from the Python model. We need to replace *Hello World!* with an actual HTML date picker widget."
364 ]
299 ]
365 },
300 },
366 {
301 {
367 "cell_type": "code",
302 "cell_type": "code",
368 "collapsed": false,
303 "collapsed": false,
369 "input": [
304 "input": [
370 "%%javascript\n",
305 "%%javascript\n",
371 "\n",
306 "\n",
372 "require([\"notebook/js/widgets/base\"], function(){\n",
307 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
373 " \n",
374 " // Define the DateModel and register it with the widget manager.\n",
375 " var DateModel = IPython.WidgetModel.extend({});\n",
376 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
377 " \n",
308 " \n",
378 " // Define the DatePickerView\n",
309 " // Define the DatePickerView\n",
379 " var DatePickerView = IPython.WidgetView.extend({\n",
310 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
380 " \n",
381 " render: function(){\n",
311 " render: function(){\n",
382 " \n",
312 " \n",
383 " // Create a div to hold our widget.\n",
384 " this.$el = $('<div />');\n",
385 " \n",
386 " // Create the date picker control.\n",
313 " // Create the date picker control.\n",
387 " this.$date = $('<input />')\n",
314 " this.$date = $('<input />')\n",
388 " .attr('type', 'date');\n",
315 " .attr('type', 'date')\n",
316 " .appendTo(this.$el);\n",
389 " },\n",
317 " },\n",
390 " });\n",
318 " });\n",
391 " \n",
319 " \n",
392 " // Register the DatePickerView with the widget manager.\n",
320 " // Register the DatePickerView with the widget manager.\n",
393 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
321 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
394 "});"
322 "});"
395 ],
323 ],
396 "language": "python",
324 "language": "python",
397 "metadata": {},
325 "metadata": {},
398 "outputs": [
326 "outputs": [
399 {
327 {
400 "javascript": [
328 "javascript": [
401 "\n",
329 "\n",
402 "require([\"notebook/js/widgets/base\"], function(){\n",
330 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
403 " \n",
404 " // Define the DateModel and register it with the widget manager.\n",
405 " var DateModel = IPython.WidgetModel.extend({});\n",
406 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
407 " \n",
331 " \n",
408 " // Define the DatePickerView\n",
332 " // Define the DatePickerView\n",
409 " var DatePickerView = IPython.WidgetView.extend({\n",
333 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
410 " \n",
411 " render: function(){\n",
334 " render: function(){\n",
412 " \n",
335 " \n",
413 " // Create a div to hold our widget.\n",
414 " this.$el = $('<div />');\n",
415 " \n",
416 " // Create the date picker control.\n",
336 " // Create the date picker control.\n",
417 " this.$date = $('<input />')\n",
337 " this.$date = $('<input />')\n",
418 " .attr('type', 'date');\n",
338 " .attr('type', 'date')\n",
339 " .appendTo(this.$el);\n",
419 " },\n",
340 " },\n",
420 " });\n",
341 " });\n",
421 " \n",
342 " \n",
422 " // Register the DatePickerView with the widget manager.\n",
343 " // Register the DatePickerView with the widget manager.\n",
423 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
344 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
424 "});"
345 "});"
425 ],
346 ],
426 "metadata": {},
347 "metadata": {},
427 "output_type": "display_data",
348 "output_type": "display_data",
428 "text": [
349 "text": [
429 "<IPython.core.display.Javascript at 0x10a274050>"
350 "<IPython.core.display.Javascript at 0x1e40810>"
430 ]
351 ]
431 }
352 }
432 ],
353 ],
433 "prompt_number": 26
354 "prompt_number": 26
434 },
355 },
435 {
356 {
436 "cell_type": "markdown",
357 "cell_type": "markdown",
437 "metadata": {},
358 "metadata": {},
438 "source": [
359 "source": [
439 "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."
360 "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."
440 ]
361 ]
441 },
362 },
442 {
363 {
443 "cell_type": "code",
364 "cell_type": "code",
444 "collapsed": false,
365 "collapsed": false,
445 "input": [
366 "input": [
446 "%%javascript\n",
367 "%%javascript\n",
447 "\n",
368 "\n",
448 "require([\"notebook/js/widgets/base\"], function(){\n",
369 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
449 " \n",
450 " // Define the DateModel and register it with the widget manager.\n",
451 " var DateModel = IPython.WidgetModel.extend({});\n",
452 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
453 " \n",
370 " \n",
454 " // Define the DatePickerView\n",
371 " // Define the DatePickerView\n",
455 " var DatePickerView = IPython.WidgetView.extend({\n",
372 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
456 " \n",
457 " render: function(){\n",
373 " render: function(){\n",
458 " \n",
374 " \n",
459 " // Create a div to hold our widget.\n",
460 " this.$el = $('<div />');\n",
461 " \n",
462 " // Create the date picker control.\n",
375 " // Create the date picker control.\n",
463 " this.$date = $('<input />')\n",
376 " this.$date = $('<input />')\n",
464 " .attr('type', 'date');\n",
377 " .attr('type', 'date')\n",
378 " .appendTo(this.$el);\n",
465 " },\n",
379 " },\n",
466 " \n",
380 " \n",
467 " update: function() {\n",
381 " update: function() {\n",
468 " \n",
382 " \n",
469 " // Set the value of the date control and then call base.\n",
383 " // Set the value of the date control and then call base.\n",
470 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
384 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
471 " return IPython.WidgetView.prototype.update.call(this);\n",
385 " return DatePickerView.__super__.update.apply(this);\n",
472 " },\n",
386 " },\n",
473 " });\n",
387 " });\n",
474 " \n",
388 " \n",
475 " // Register the DatePickerView with the widget manager.\n",
389 " // Register the DatePickerView with the widget manager.\n",
476 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
390 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
477 "});"
391 "});"
478 ],
392 ],
479 "language": "python",
393 "language": "python",
480 "metadata": {},
394 "metadata": {},
481 "outputs": [
395 "outputs": [
482 {
396 {
483 "javascript": [
397 "javascript": [
484 "\n",
398 "\n",
485 "require([\"notebook/js/widgets/base\"], function(){\n",
399 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
486 " \n",
487 " // Define the DateModel and register it with the widget manager.\n",
488 " var DateModel = IPython.WidgetModel.extend({});\n",
489 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
490 " \n",
400 " \n",
491 " // Define the DatePickerView\n",
401 " // Define the DatePickerView\n",
492 " var DatePickerView = IPython.WidgetView.extend({\n",
402 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
493 " \n",
494 " render: function(){\n",
403 " render: function(){\n",
495 " \n",
404 " \n",
496 " // Create a div to hold our widget.\n",
497 " this.$el = $('<div />');\n",
498 " \n",
499 " // Create the date picker control.\n",
405 " // Create the date picker control.\n",
500 " this.$date = $('<input />')\n",
406 " this.$date = $('<input />')\n",
501 " .attr('type', 'date');\n",
407 " .attr('type', 'date')\n",
408 " .appendTo(this.$el);\n",
502 " },\n",
409 " },\n",
503 " \n",
410 " \n",
504 " update: function() {\n",
411 " update: function() {\n",
505 " \n",
412 " \n",
506 " // Set the value of the date control and then call base.\n",
413 " // Set the value of the date control and then call base.\n",
507 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
414 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
508 " return IPython.WidgetView.prototype.update.call(this);\n",
415 " return DatePickerView.__super__.update.apply(this);\n",
509 " },\n",
416 " },\n",
510 " });\n",
417 " });\n",
511 " \n",
418 " \n",
512 " // Register the DatePickerView with the widget manager.\n",
419 " // Register the DatePickerView with the widget manager.\n",
513 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
420 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
514 "});"
421 "});"
515 ],
422 ],
516 "metadata": {},
423 "metadata": {},
517 "output_type": "display_data",
424 "output_type": "display_data",
518 "text": [
425 "text": [
519 "<IPython.core.display.Javascript at 0x10a2740d0>"
426 "<IPython.core.display.Javascript at 0x1e40390>"
520 ]
427 ]
521 }
428 }
522 ],
429 ],
523 "prompt_number": 27
430 "prompt_number": 27
524 },
431 },
525 {
432 {
526 "cell_type": "markdown",
433 "cell_type": "markdown",
527 "metadata": {},
434 "metadata": {},
528 "source": [
435 "source": [
529 "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. By setting the `this.$el` property of the view, we break the Backbone powered event handling. To fix this, a call to `this.delegateEvents()` must be added after `this.$el` is set. \n",
436 "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",
530 "\n",
531 "After the date change event fires and the new value is set in the model, it's very important that we call `update_other_views(this)` to make the other views on the page update and 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",
532 "\n",
437 "\n",
533 "**Final JavaScript code below:**"
438 "**Final JavaScript code below:**"
534 ]
439 ]
535 },
440 },
536 {
441 {
537 "cell_type": "code",
442 "cell_type": "code",
538 "collapsed": false,
443 "collapsed": false,
539 "input": [
444 "input": [
540 "%%javascript\n",
445 "%%javascript\n",
541 "\n",
446 "\n",
542 "require([\"notebook/js/widgets/base\"], function(){\n",
447 "\n",
543 " \n",
448 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
544 " // Define the DateModel and register it with the widget manager.\n",
545 " var DateModel = IPython.WidgetModel.extend({});\n",
546 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
547 " \n",
449 " \n",
548 " // Define the DatePickerView\n",
450 " // Define the DatePickerView\n",
549 " var DatePickerView = IPython.WidgetView.extend({\n",
451 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
550 " \n",
551 " render: function(){\n",
452 " render: function(){\n",
552 " \n",
453 " \n",
553 " // Create a div to hold our widget.\n",
554 " this.$el = $('<div />');\n",
555 " this.delegateEvents();\n",
556 " \n",
557 " // Create the date picker control.\n",
454 " // Create the date picker control.\n",
558 " this.$date = $('<input />')\n",
455 " this.$date = $('<input />')\n",
559 " .attr('type', 'date')\n",
456 " .attr('type', 'date')\n",
560 " .appendTo(this.$el);\n",
457 " .appendTo(this.$el);\n",
561 " },\n",
458 " },\n",
562 " \n",
459 " \n",
563 " update: function() {\n",
460 " update: function() {\n",
564 " \n",
461 " \n",
565 " // Set the value of the date control and then call base.\n",
462 " // Set the value of the date control and then call base.\n",
566 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
463 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
567 " return IPython.WidgetView.prototype.update.call(this);\n",
464 " return DatePickerView.__super__.update.apply(this);\n",
568 " },\n",
465 " },\n",
569 " \n",
466 " \n",
570 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
467 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
571 " events: {\"change\": \"handle_date_change\"},\n",
468 " events: {\"change\": \"handle_date_change\"},\n",
572 " \n",
469 " \n",
573 " // Callback for when the date is changed.\n",
470 " // Callback for when the date is changed.\n",
574 " handle_date_change: function(event) {\n",
471 " handle_date_change: function(event) {\n",
575 " this.model.set('value', this.$date.val());\n",
472 " this.model.set('value', this.$date.val());\n",
473 " this.touch();\n",
576 " },\n",
474 " },\n",
577 " \n",
578 " });\n",
475 " });\n",
579 " \n",
476 " \n",
580 " // Register the DatePickerView with the widget manager.\n",
477 " // Register the DatePickerView with the widget manager.\n",
581 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
478 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
582 "});"
479 "});"
583 ],
480 ],
584 "language": "python",
481 "language": "python",
585 "metadata": {},
482 "metadata": {},
586 "outputs": [
483 "outputs": [
587 {
484 {
588 "javascript": [
485 "javascript": [
589 "\n",
486 "\n",
590 "require([\"notebook/js/widgets/base\"], function(){\n",
487 "\n",
591 " \n",
488 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
592 " // Define the DateModel and register it with the widget manager.\n",
593 " var DateModel = IPython.WidgetModel.extend({});\n",
594 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
595 " \n",
489 " \n",
596 " // Define the DatePickerView\n",
490 " // Define the DatePickerView\n",
597 " var DatePickerView = IPython.WidgetView.extend({\n",
491 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
598 " \n",
599 " render: function(){\n",
492 " render: function(){\n",
600 " \n",
493 " \n",
601 " // Create a div to hold our widget.\n",
602 " this.$el = $('<div />');\n",
603 " this.delegateEvents();\n",
604 " \n",
605 " // Create the date picker control.\n",
494 " // Create the date picker control.\n",
606 " this.$date = $('<input />')\n",
495 " this.$date = $('<input />')\n",
607 " .attr('type', 'date')\n",
496 " .attr('type', 'date')\n",
608 " .appendTo(this.$el);\n",
497 " .appendTo(this.$el);\n",
609 " },\n",
498 " },\n",
610 " \n",
499 " \n",
611 " update: function() {\n",
500 " update: function() {\n",
612 " \n",
501 " \n",
613 " // Set the value of the date control and then call base.\n",
502 " // Set the value of the date control and then call base.\n",
614 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
503 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
615 " return IPython.WidgetView.prototype.update.call(this);\n",
504 " return DatePickerView.__super__.update.apply(this);\n",
616 " },\n",
505 " },\n",
617 " \n",
506 " \n",
618 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
507 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
619 " events: {\"change\": \"handle_date_change\"},\n",
508 " events: {\"change\": \"handle_date_change\"},\n",
620 " \n",
509 " \n",
621 " // Callback for when the date is changed.\n",
510 " // Callback for when the date is changed.\n",
622 " handle_date_change: function(event) {\n",
511 " handle_date_change: function(event) {\n",
623 " this.model.set('value', this.$date.val());\n",
512 " this.model.set('value', this.$date.val());\n",
513 " this.touch();\n",
624 " },\n",
514 " },\n",
625 " \n",
626 " });\n",
515 " });\n",
627 " \n",
516 " \n",
628 " // Register the DatePickerView with the widget manager.\n",
517 " // Register the DatePickerView with the widget manager.\n",
629 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
518 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
630 "});"
519 "});"
631 ],
520 ],
632 "metadata": {},
521 "metadata": {},
633 "output_type": "display_data",
522 "output_type": "display_data",
634 "text": [
523 "text": [
635 "<IPython.core.display.Javascript at 0x10a274a10>"
524 "<IPython.core.display.Javascript at 0x1e40890>"
636 ]
525 ]
637 }
526 }
638 ],
527 ],
639 "prompt_number": 52
528 "prompt_number": 28
640 },
529 },
641 {
530 {
642 "cell_type": "heading",
531 "cell_type": "heading",
643 "level": 2,
532 "level": 2,
644 "metadata": {},
533 "metadata": {},
645 "source": [
534 "source": [
646 "Test"
535 "Test"
647 ]
536 ]
648 },
537 },
649 {
538 {
650 "cell_type": "markdown",
539 "cell_type": "markdown",
651 "metadata": {},
540 "metadata": {},
652 "source": [
541 "source": [
653 "To test, create the widget the same way that the other widgets are created."
542 "To test, create the widget the same way that the other widgets are created."
654 ]
543 ]
655 },
544 },
656 {
545 {
657 "cell_type": "code",
546 "cell_type": "code",
658 "collapsed": false,
547 "collapsed": false,
659 "input": [
548 "input": [
660 "my_widget = DateWidget()\n",
549 "my_widget = DateWidget()\n",
661 "display(my_widget)"
550 "display(my_widget)"
662 ],
551 ],
663 "language": "python",
552 "language": "python",
664 "metadata": {},
553 "metadata": {},
665 "outputs": [],
554 "outputs": [],
666 "prompt_number": 29
555 "prompt_number": 29
667 },
556 },
668 {
557 {
669 "cell_type": "markdown",
558 "cell_type": "markdown",
670 "metadata": {},
559 "metadata": {},
671 "source": [
560 "source": [
672 "Display the widget again to make sure that both views remain in sync."
561 "Display the widget again to make sure that both views remain in sync."
673 ]
562 ]
674 },
563 },
675 {
564 {
676 "cell_type": "code",
565 "cell_type": "code",
677 "collapsed": false,
566 "collapsed": false,
678 "input": [
567 "input": [
679 "display(my_widget)"
568 "my_widget"
680 ],
569 ],
681 "language": "python",
570 "language": "python",
682 "metadata": {},
571 "metadata": {},
683 "outputs": [],
572 "outputs": [],
684 "prompt_number": 30
573 "prompt_number": 30
685 },
574 },
686 {
575 {
687 "cell_type": "markdown",
576 "cell_type": "markdown",
688 "metadata": {},
577 "metadata": {},
689 "source": [
578 "source": [
690 "Read the date from Python"
579 "Read the date from Python"
691 ]
580 ]
692 },
581 },
693 {
582 {
694 "cell_type": "code",
583 "cell_type": "code",
695 "collapsed": false,
584 "collapsed": false,
696 "input": [
585 "input": [
697 "my_widget.value"
586 "my_widget.value"
698 ],
587 ],
699 "language": "python",
588 "language": "python",
700 "metadata": {},
589 "metadata": {},
701 "outputs": [
590 "outputs": [
702 {
591 {
703 "metadata": {},
592 "metadata": {},
704 "output_type": "pyout",
593 "output_type": "pyout",
705 "prompt_number": 37,
594 "prompt_number": 31,
706 "text": [
595 "text": [
707 "u'1998-12-01'"
596 "u''"
708 ]
597 ]
709 }
598 }
710 ],
599 ],
711 "prompt_number": 37
600 "prompt_number": 31
712 },
601 },
713 {
602 {
714 "cell_type": "markdown",
603 "cell_type": "markdown",
715 "metadata": {},
604 "metadata": {},
716 "source": [
605 "source": [
717 "Set the date from Python"
606 "Set the date from Python"
718 ]
607 ]
719 },
608 },
720 {
609 {
721 "cell_type": "code",
610 "cell_type": "code",
722 "collapsed": false,
611 "collapsed": false,
723 "input": [
612 "input": [
724 "my_widget.value = \"1998-12-01\" # December 1st, 1999"
613 "my_widget.value = \"1998-12-01\" # December 1st, 1999"
725 ],
614 ],
726 "language": "python",
615 "language": "python",
727 "metadata": {},
616 "metadata": {},
728 "outputs": [],
617 "outputs": [],
729 "prompt_number": 34
618 "prompt_number": 32
730 },
619 },
731 {
620 {
732 "cell_type": "heading",
621 "cell_type": "heading",
733 "level": 1,
622 "level": 1,
734 "metadata": {},
623 "metadata": {},
735 "source": [
624 "source": [
736 "Section 3 - Extra credit"
625 "Section 3 - Extra credit"
737 ]
626 ]
738 },
627 },
739 {
628 {
740 "cell_type": "markdown",
629 "cell_type": "markdown",
741 "metadata": {},
630 "metadata": {},
742 "source": [
631 "source": [
743 "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."
632 "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."
744 ]
633 ]
745 },
634 },
746 {
635 {
747 "cell_type": "heading",
636 "cell_type": "heading",
748 "level": 2,
637 "level": 2,
749 "metadata": {},
638 "metadata": {},
750 "source": [
639 "source": [
751 "Python"
640 "Python"
752 ]
641 ]
753 },
642 },
754 {
643 {
755 "cell_type": "markdown",
644 "cell_type": "markdown",
756 "metadata": {},
645 "metadata": {},
757 "source": [
646 "source": [
758 "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."
647 "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."
759 ]
648 ]
760 },
649 },
761 {
650 {
762 "cell_type": "code",
651 "cell_type": "code",
763 "collapsed": false,
652 "collapsed": false,
764 "input": [
653 "input": [
765 "# Import the base Widget class and the traitlets Unicode class.\n",
654 "# Import the base Widget class and the traitlets Unicode class.\n",
766 "from IPython.html.widgets import Widget\n",
655 "from IPython.html.widgets import DOMWidget\n",
767 "from IPython.utils.traitlets import Unicode\n",
656 "from IPython.utils.traitlets import Unicode\n",
768 "\n",
657 "\n",
769 "# Define our DateWidget and its target model and default view.\n",
658 "# Define our DateWidget and its target model and default view.\n",
770 "class DateWidget(Widget):\n",
659 "class DateWidget(DOMWidget):\n",
771 " target_name = Unicode('DateWidgetModel')\n",
660 " _view_name = Unicode('DatePickerView', sync=True)\n",
772 " default_view_name = Unicode('DatePickerView')\n",
661 " value = Unicode(sync=True)\n",
773 " \n",
774 " # Define the custom state properties to sync with the front-end\n",
775 " _keys = ['value']\n",
776 " value = Unicode()\n",
777 " \n",
662 " \n",
778 " # This function automatically gets called by the traitlet machinery when\n",
663 " # This function automatically gets called by the traitlet machinery when\n",
779 " # value is modified because of this function's name.\n",
664 " # value is modified because of this function's name.\n",
780 " def _value_changed(self, name, old_value, new_value):\n",
665 " def _value_changed(self, name, old_value, new_value):\n",
781 " pass\n",
666 " pass\n",
782 " "
667 " "
783 ],
668 ],
784 "language": "python",
669 "language": "python",
785 "metadata": {},
670 "metadata": {},
786 "outputs": [],
671 "outputs": [],
787 "prompt_number": 38
672 "prompt_number": 33
788 },
673 },
789 {
674 {
790 "cell_type": "markdown",
675 "cell_type": "markdown",
791 "metadata": {},
676 "metadata": {},
792 "source": [
677 "source": [
793 "Now the function that parses the date string and only sets it in the correct format can be added."
678 "Now the function that parses the date string and only sets it in the correct format can be added."
794 ]
679 ]
795 },
680 },
796 {
681 {
797 "cell_type": "code",
682 "cell_type": "code",
798 "collapsed": false,
683 "collapsed": false,
799 "input": [
684 "input": [
685 "# Import the dateutil library to parse date strings.\n",
686 "from dateutil import parser\n",
800 "\n",
687 "\n",
801 "# Import the base Widget class and the traitlets Unicode class.\n",
688 "# Import the base Widget class and the traitlets Unicode class.\n",
802 "from IPython.html.widgets import Widget\n",
689 "from IPython.html.widgets import DOMWidget\n",
803 "from IPython.utils.traitlets import Unicode\n",
690 "from IPython.utils.traitlets import Unicode\n",
804 "\n",
691 "\n",
805 "# Define our DateWidget and its target model and default view.\n",
692 "# Define our DateWidget and its target model and default view.\n",
806 "class DateWidget(Widget):\n",
693 "class DateWidget(DOMWidget):\n",
807 " target_name = Unicode('DateWidgetModel')\n",
694 " _view_name = Unicode('DatePickerView', sync=True)\n",
808 " default_view_name = Unicode('DatePickerView')\n",
695 " value = Unicode(sync=True)\n",
809 " \n",
810 " # Define the custom state properties to sync with the front-end\n",
811 " _keys = ['value']\n",
812 " value = Unicode()\n",
813 " \n",
696 " \n",
814 " # This function automatically gets called by the traitlet machinery when\n",
697 " # This function automatically gets called by the traitlet machinery when\n",
815 " # value is modified because of this function's name.\n",
698 " # value is modified because of this function's name.\n",
816 " def _value_changed(self, name, old_value, new_value):\n",
699 " def _value_changed(self, name, old_value, new_value):\n",
817 " \n",
700 " \n",
818 " # Parse the date time value.\n",
701 " # Parse the date time value.\n",
819 " try:\n",
702 " try:\n",
820 " parsed_date = parser.parse(new_value)\n",
703 " parsed_date = parser.parse(new_value)\n",
821 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
704 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
822 " except:\n",
705 " except:\n",
823 " parsed_date_string = ''\n",
706 " parsed_date_string = ''\n",
824 " \n",
707 " \n",
825 " # Set the parsed date string if the current date string is different.\n",
708 " # Set the parsed date string if the current date string is different.\n",
826 " if self.value != parsed_date_string:\n",
709 " if self.value != parsed_date_string:\n",
827 " self.value = parsed_date_string"
710 " self.value = parsed_date_string"
828 ],
711 ],
829 "language": "python",
712 "language": "python",
830 "metadata": {},
713 "metadata": {},
831 "outputs": [],
714 "outputs": [],
832 "prompt_number": 39
715 "prompt_number": 34
833 },
716 },
834 {
717 {
835 "cell_type": "markdown",
718 "cell_type": "markdown",
836 "metadata": {},
719 "metadata": {},
837 "source": [
720 "source": [
838 "The standard property name used for widget labels is `description`. In the code block below, `description` has been added to the Python widget."
721 "The standard property name used for widget labels is `description`. In the code block below, `description` has been added to the Python widget."
839 ]
722 ]
840 },
723 },
841 {
724 {
842 "cell_type": "code",
725 "cell_type": "code",
843 "collapsed": false,
726 "collapsed": false,
844 "input": [
727 "input": [
845 "# Import the dateutil library to parse date strings.\n",
728 "# Import the dateutil library to parse date strings.\n",
846 "from dateutil import parser\n",
729 "from dateutil import parser\n",
847 "\n",
730 "\n",
848 "# Import the base Widget class and the traitlets Unicode class.\n",
731 "# Import the base Widget class and the traitlets Unicode class.\n",
849 "from IPython.html.widgets import Widget\n",
732 "from IPython.html.widgets import DOMWidget\n",
850 "from IPython.utils.traitlets import Unicode\n",
733 "from IPython.utils.traitlets import Unicode\n",
851 "\n",
734 "\n",
852 "# Define our DateWidget and its target model and default view.\n",
735 "# Define our DateWidget and its target model and default view.\n",
853 "class DateWidget(Widget):\n",
736 "class DateWidget(DOMWidget):\n",
854 " target_name = Unicode('DateWidgetModel')\n",
737 " _view_name = Unicode('DatePickerView', sync=True)\n",
855 " default_view_name = Unicode('DatePickerView')\n",
738 " value = Unicode(sync=True)\n",
856 " \n",
739 " description = Unicode(sync=True)\n",
857 " # Define the custom state properties to sync with the front-end\n",
858 " _keys = ['value', 'description']\n",
859 " value = Unicode()\n",
860 " description = Unicode()\n",
861 " \n",
740 " \n",
862 " # This function automatically gets called by the traitlet machinery when\n",
741 " # This function automatically gets called by the traitlet machinery when\n",
863 " # value is modified because of this function's name.\n",
742 " # value is modified because of this function's name.\n",
864 " def _value_changed(self, name, old_value, new_value):\n",
743 " def _value_changed(self, name, old_value, new_value):\n",
865 " \n",
744 " \n",
866 " # Parse the date time value.\n",
745 " # Parse the date time value.\n",
867 " try:\n",
746 " try:\n",
868 " parsed_date = parser.parse(new_value)\n",
747 " parsed_date = parser.parse(new_value)\n",
869 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
748 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
870 " except:\n",
749 " except:\n",
871 " parsed_date_string = ''\n",
750 " parsed_date_string = ''\n",
872 " \n",
751 " \n",
873 " # Set the parsed date string if the current date string is different.\n",
752 " # Set the parsed date string if the current date string is different.\n",
874 " if self.value != parsed_date_string:\n",
753 " if self.value != parsed_date_string:\n",
875 " self.value = parsed_date_string"
754 " self.value = parsed_date_string"
876 ],
755 ],
877 "language": "python",
756 "language": "python",
878 "metadata": {},
757 "metadata": {},
879 "outputs": [],
758 "outputs": [],
880 "prompt_number": 40
759 "prompt_number": 35
881 },
760 },
882 {
761 {
883 "cell_type": "markdown",
762 "cell_type": "markdown",
884 "metadata": {},
763 "metadata": {},
885 "source": [
764 "source": [
886 "Finally, a callback list is added so the user can perform custom validation. If any one of the callbacks returns False, the new date time is not set.\n",
765 "Finally, a callback list is added so the user can perform custom validation. If any one of the callbacks returns False, the new date time is not set.\n",
887 "\n",
766 "\n",
888 "**Final Python code below:**"
767 "**Final Python code below:**"
889 ]
768 ]
890 },
769 },
891 {
770 {
892 "cell_type": "code",
771 "cell_type": "code",
893 "collapsed": false,
772 "collapsed": false,
894 "input": [
773 "input": [
895 "# Import the dateutil library to parse date strings.\n",
774 "# Import the dateutil library to parse date strings.\n",
896 "from dateutil import parser\n",
775 "from dateutil import parser\n",
897 "\n",
776 "\n",
898 "# Import the base Widget class and the traitlets Unicode class.\n",
777 "# Import the base Widget class and the traitlets Unicode class.\n",
899 "from IPython.html.widgets import Widget\n",
778 "from IPython.html.widgets import DOMWidget, CallbackDispatcher\n",
900 "from IPython.utils.traitlets import Unicode\n",
779 "from IPython.utils.traitlets import Unicode\n",
901 "\n",
780 "\n",
902 "# Define our DateWidget and its target model and default view.\n",
781 "# Define our DateWidget and its target model and default view.\n",
903 "class DateWidget(Widget):\n",
782 "class DateWidget(DOMWidget):\n",
904 " target_name = Unicode('DateWidgetModel')\n",
783 " _view_name = Unicode('DatePickerView', sync=True)\n",
905 " default_view_name = Unicode('DatePickerView')\n",
784 " value = Unicode(sync=True)\n",
906 " \n",
785 " description = Unicode(sync=True)\n",
907 " # Define the custom state properties to sync with the front-end\n",
908 " _keys = ['value', 'description']\n",
909 " value = Unicode()\n",
910 " description = Unicode()\n",
911 " \n",
786 " \n",
912 " def __init__(self, **kwargs):\n",
787 " def __init__(self, **kwargs):\n",
913 " super(DateWidget, self).__init__(**kwargs)\n",
788 " super(DateWidget, self).__init__(**kwargs)\n",
914 " self._validation_callbacks = []\n",
789 " \n",
790 " # Specify the number of positional arguments supported. For \n",
791 " # validation we only are worried about one parameter, the\n",
792 " # new value that should be validated.\n",
793 " self.validation = CallbackDispatcher(acceptable_nargs=[1])\n",
915 " \n",
794 " \n",
916 " # This function automatically gets called by the traitlet machinery when\n",
795 " # This function automatically gets called by the traitlet machinery when\n",
917 " # value is modified because of this function's name.\n",
796 " # value is modified because of this function's name.\n",
918 " def _value_changed(self, name, old_value, new_value):\n",
797 " def _value_changed(self, name, old_value, new_value):\n",
919 " \n",
798 " \n",
920 " # Parse the date time value.\n",
799 " # Parse the date time value.\n",
921 " try:\n",
800 " try:\n",
922 " parsed_date = parser.parse(new_value)\n",
801 " parsed_date = parser.parse(new_value)\n",
923 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
802 " parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
924 " except:\n",
803 " except:\n",
925 " parsed_date = None\n",
926 " parsed_date_string = ''\n",
804 " parsed_date_string = ''\n",
927 " \n",
805 " \n",
928 " # Set the parsed date string if the current date string is different.\n",
806 " # Set the parsed date string if the current date string is different.\n",
929 " if old_value != new_value:\n",
807 " if old_value != new_value:\n",
930 " if self.handle_validate(parsed_date):\n",
808 " validation = self.validation(parsed_date)\n",
809 " if validation is None or validation == True:\n",
931 " self.value = parsed_date_string\n",
810 " self.value = parsed_date_string\n",
932 " else:\n",
811 " else:\n",
933 " self.value = old_value\n",
812 " self.value = old_value\n",
934 " self.send_state() # The traitlet event won't fire since the value isn't changing.\n",
813 " self.send_state() # The traitlet event won't fire since the value isn't changing.\n",
935 " # We need to force the back-end to send the front-end the state\n",
814 " # We need to force the back-end to send the front-end the state\n",
936 " # to make sure that the date control date doesn't change.\n",
815 " # to make sure that the date control date doesn't change."
937 " \n",
938 " \n",
939 " # Allow the user to register custom validation callbacks.\n",
940 " # callback(new value as a datetime object)\n",
941 " def on_validate(self, callback, remove=False):\n",
942 " if remove and callback in self._validation_callbacks:\n",
943 " self._validation_callbacks.remove(callback)\n",
944 " elif (not remove) and (not callback in self._validation_callbacks):\n",
945 " self._validation_callbacks.append(callback)\n",
946 " \n",
947 " # Call user validation callbacks. Return True if valid.\n",
948 " def handle_validate(self, new_value):\n",
949 " for callback in self._validation_callbacks:\n",
950 " if not callback(new_value):\n",
951 " return False\n",
952 " return True\n",
953 " "
954 ],
816 ],
955 "language": "python",
817 "language": "python",
956 "metadata": {},
818 "metadata": {},
957 "outputs": [],
819 "outputs": [],
958 "prompt_number": 41
820 "prompt_number": 45
959 },
821 },
960 {
822 {
961 "cell_type": "heading",
823 "cell_type": "heading",
962 "level": 2,
824 "level": 2,
963 "metadata": {},
825 "metadata": {},
964 "source": [
826 "source": [
965 "JavaScript"
827 "JavaScript"
966 ]
828 ]
967 },
829 },
968 {
830 {
969 "cell_type": "markdown",
831 "cell_type": "markdown",
970 "metadata": {},
832 "metadata": {},
971 "source": [
833 "source": [
972 "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",
834 "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",
973 "\n",
835 "\n",
974 "We hide the label if the description value is blank."
836 "We hide the label if the description value is blank."
975 ]
837 ]
976 },
838 },
977 {
839 {
978 "cell_type": "code",
840 "cell_type": "code",
979 "collapsed": false,
841 "collapsed": false,
980 "input": [
842 "input": [
981 "%%javascript\n",
843 "%%javascript\n",
982 "\n",
844 "\n",
983 "require([\"notebook/js/widgets/base\"], function(){\n",
845 "\n",
984 " \n",
846 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
985 " // Define the DateModel and register it with the widget manager.\n",
986 " var DateModel = IPython.WidgetModel.extend({});\n",
987 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
988 " \n",
847 " \n",
989 " // Define the DatePickerView\n",
848 " // Define the DatePickerView\n",
990 " var DatePickerView = IPython.WidgetView.extend({\n",
849 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
991 " \n",
992 " render: function(){\n",
850 " render: function(){\n",
993 " \n",
851 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
994 " // Create a div to hold our widget.\n",
852 " it fit with the other built in widgets.*/\n",
995 " this.$el = $('<div />')\n",
996 " .addClass('widget-hbox-single'); // Apply this class to the widget container to make\n",
997 " // it fit with the other built in widgets.\n",
998 " this.delegateEvents();\n",
999 " \n",
1000 " // Create a label.\n",
853 " // Create a label.\n",
1001 " this.$label = $('<div />')\n",
854 " this.$label = $('<div />')\n",
1002 " .addClass('widget-hlabel')\n",
855 " .addClass('widget-hlabel')\n",
1003 " .appendTo(this.$el)\n",
856 " .appendTo(this.$el)\n",
1004 " .hide(); // Hide the label by default.\n",
857 " .hide(); // Hide the label by default.\n",
1005 " \n",
858 " \n",
1006 " // Create the date picker control.\n",
859 " // Create the date picker control.\n",
1007 " this.$date = $('<input />')\n",
860 " this.$date = $('<input />')\n",
1008 " .attr('type', 'date')\n",
861 " .attr('type', 'date')\n",
1009 " .appendTo(this.$el);\n",
862 " .appendTo(this.$el);\n",
1010 " },\n",
863 " },\n",
1011 " \n",
864 " \n",
1012 " update: function() {\n",
865 " update: function() {\n",
1013 " \n",
866 " \n",
1014 " // Set the value of the date control and then call base.\n",
867 " // Set the value of the date control and then call base.\n",
1015 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
868 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
1016 " \n",
869 " \n",
1017 " // Hide or show the label depending on the existance of a description.\n",
870 " // Hide or show the label depending on the existance of a description.\n",
1018 " var description = this.model.get('description');\n",
871 " var description = this.model.get('description');\n",
1019 " if (description == undefined || description == '') {\n",
872 " if (description == undefined || description == '') {\n",
1020 " this.$label.hide();\n",
873 " this.$label.hide();\n",
1021 " } else {\n",
874 " } else {\n",
1022 " this.$label.show();\n",
875 " this.$label.show();\n",
1023 " this.$label.html(description);\n",
876 " this.$label.text(description);\n",
1024 " }\n",
877 " }\n",
1025 " \n",
878 " \n",
1026 " return IPython.WidgetView.prototype.update.call(this);\n",
879 " return DatePickerView.__super__.update.apply(this);\n",
1027 " },\n",
880 " },\n",
1028 " \n",
881 " \n",
1029 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
882 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
1030 " events: {\"change\": \"handle_date_change\"},\n",
883 " events: {\"change\": \"handle_date_change\"},\n",
1031 " \n",
884 " \n",
1032 " // Callback for when the date is changed.\n",
885 " // Callback for when the date is changed.\n",
1033 " handle_date_change: function(event) {\n",
886 " handle_date_change: function(event) {\n",
1034 " this.model.set('value', this.$date.val());\n",
887 " this.model.set('value', this.$date.val());\n",
888 " this.touch();\n",
1035 " },\n",
889 " },\n",
1036 " \n",
1037 " });\n",
890 " });\n",
1038 " \n",
891 " \n",
1039 " // Register the DatePickerView with the widget manager.\n",
892 " // Register the DatePickerView with the widget manager.\n",
1040 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
893 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
1041 "});"
894 "});"
1042 ],
895 ],
1043 "language": "python",
896 "language": "python",
1044 "metadata": {},
897 "metadata": {},
1045 "outputs": [
898 "outputs": [
1046 {
899 {
1047 "javascript": [
900 "javascript": [
1048 "\n",
901 "\n",
1049 "require([\"notebook/js/widgets/base\"], function(){\n",
902 "\n",
1050 " \n",
903 "require([\"notebook/js/widgets/widget\"], function(WidgetManager){\n",
1051 " // Define the DateModel and register it with the widget manager.\n",
1052 " var DateModel = IPython.WidgetModel.extend({});\n",
1053 " IPython.widget_manager.register_widget_model('DateWidgetModel', DateModel);\n",
1054 " \n",
904 " \n",
1055 " // Define the DatePickerView\n",
905 " // Define the DatePickerView\n",
1056 " var DatePickerView = IPython.WidgetView.extend({\n",
906 " var DatePickerView = IPython.DOMWidgetView.extend({\n",
1057 " \n",
1058 " render: function(){\n",
907 " render: function(){\n",
1059 " \n",
908 " this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
1060 " // Create a div to hold our widget.\n",
909 " it fit with the other built in widgets.*/\n",
1061 " this.$el = $('<div />')\n",
1062 " .addClass('widget-hbox-single'); // Apply this class to the widget container to make\n",
1063 " // it fit with the other built in widgets.\n",
1064 " this.delegateEvents();\n",
1065 " \n",
1066 " // Create a label.\n",
910 " // Create a label.\n",
1067 " this.$label = $('<div />')\n",
911 " this.$label = $('<div />')\n",
1068 " .addClass('widget-hlabel')\n",
912 " .addClass('widget-hlabel')\n",
1069 " .appendTo(this.$el)\n",
913 " .appendTo(this.$el)\n",
1070 " .hide(); // Hide the label by default.\n",
914 " .hide(); // Hide the label by default.\n",
1071 " \n",
915 " \n",
1072 " // Create the date picker control.\n",
916 " // Create the date picker control.\n",
1073 " this.$date = $('<input />')\n",
917 " this.$date = $('<input />')\n",
1074 " .attr('type', 'date')\n",
918 " .attr('type', 'date')\n",
1075 " .appendTo(this.$el);\n",
919 " .appendTo(this.$el);\n",
1076 " },\n",
920 " },\n",
1077 " \n",
921 " \n",
1078 " update: function() {\n",
922 " update: function() {\n",
1079 " \n",
923 " \n",
1080 " // Set the value of the date control and then call base.\n",
924 " // Set the value of the date control and then call base.\n",
1081 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
925 " this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
1082 " \n",
926 " \n",
1083 " // Hide or show the label depending on the existance of a description.\n",
927 " // Hide or show the label depending on the existance of a description.\n",
1084 " var description = this.model.get('description');\n",
928 " var description = this.model.get('description');\n",
1085 " if (description == undefined || description == '') {\n",
929 " if (description == undefined || description == '') {\n",
1086 " this.$label.hide();\n",
930 " this.$label.hide();\n",
1087 " } else {\n",
931 " } else {\n",
1088 " this.$label.show();\n",
932 " this.$label.show();\n",
1089 " this.$label.html(description);\n",
933 " this.$label.text(description);\n",
1090 " }\n",
934 " }\n",
1091 " \n",
935 " \n",
1092 " return IPython.WidgetView.prototype.update.call(this);\n",
936 " return DatePickerView.__super__.update.apply(this);\n",
1093 " },\n",
937 " },\n",
1094 " \n",
938 " \n",
1095 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
939 " // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
1096 " events: {\"change\": \"handle_date_change\"},\n",
940 " events: {\"change\": \"handle_date_change\"},\n",
1097 " \n",
941 " \n",
1098 " // Callback for when the date is changed.\n",
942 " // Callback for when the date is changed.\n",
1099 " handle_date_change: function(event) {\n",
943 " handle_date_change: function(event) {\n",
1100 " this.model.set('value', this.$date.val());\n",
944 " this.model.set('value', this.$date.val());\n",
945 " this.touch();\n",
1101 " },\n",
946 " },\n",
1102 " \n",
1103 " });\n",
947 " });\n",
1104 " \n",
948 " \n",
1105 " // Register the DatePickerView with the widget manager.\n",
949 " // Register the DatePickerView with the widget manager.\n",
1106 " IPython.widget_manager.register_widget_view('DatePickerView', DatePickerView);\n",
950 " WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
1107 "});"
951 "});"
1108 ],
952 ],
1109 "metadata": {},
953 "metadata": {},
1110 "output_type": "display_data",
954 "output_type": "display_data",
1111 "text": [
955 "text": [
1112 "<IPython.core.display.Javascript at 0x10a274a10>"
956 "<IPython.core.display.Javascript at 0x1efe210>"
1113 ]
957 ]
1114 }
958 }
1115 ],
959 ],
1116 "prompt_number": 53
960 "prompt_number": 40
1117 },
961 },
1118 {
962 {
1119 "cell_type": "heading",
963 "cell_type": "heading",
1120 "level": 2,
964 "level": 2,
1121 "metadata": {},
965 "metadata": {},
1122 "source": [
966 "source": [
1123 "Test"
967 "Test"
1124 ]
968 ]
1125 },
969 },
1126 {
970 {
1127 "cell_type": "markdown",
971 "cell_type": "markdown",
1128 "metadata": {},
972 "metadata": {},
1129 "source": [
973 "source": [
1130 "To test the drawing of the label we create the widget like normal but supply the additional description property a value."
974 "To test the drawing of the label we create the widget like normal but supply the additional description property a value."
1131 ]
975 ]
1132 },
976 },
1133 {
977 {
1134 "cell_type": "code",
978 "cell_type": "code",
1135 "collapsed": false,
979 "collapsed": false,
1136 "input": [
980 "input": [
1137 "# Add some additional widgets for aesthetic purpose\n",
981 "# Add some additional widgets for aesthetic purpose\n",
1138 "display(widgets.StringWidget(description=\"First:\"))\n",
982 "display(widgets.TextBoxWidget(description=\"First:\"))\n",
1139 "display(widgets.StringWidget(description=\"Last:\"))\n",
983 "display(widgets.TextBoxWidget(description=\"Last:\"))\n",
1140 "\n",
984 "\n",
1141 "my_widget = DateWidget(description=\"DOB:\")\n",
985 "my_widget = DateWidget()\n",
1142 "display(my_widget)"
986 "display(my_widget)\n",
1143 ],
987 "my_widget.description=\"DOB:\""
1144 "language": "python",
1145 "metadata": {},
1146 "outputs": [],
1147 "prompt_number": 43
1148 },
1149 {
1150 "cell_type": "markdown",
1151 "metadata": {},
1152 "source": [
1153 "Since the date widget uses `value` and `description`, we can also display its value using a `TextBoxView`. The allows us to look at the raw date value being passed to and from the back-end and front-end."
1154 ]
1155 },
1156 {
1157 "cell_type": "code",
1158 "collapsed": false,
1159 "input": [
1160 "display(my_widget, view_name=\"TextBoxView\")"
1161 ],
988 ],
1162 "language": "python",
989 "language": "python",
1163 "metadata": {},
990 "metadata": {},
1164 "outputs": [],
991 "outputs": [],
1165 "prompt_number": 44
992 "prompt_number": 46
1166 },
993 },
1167 {
994 {
1168 "cell_type": "markdown",
995 "cell_type": "markdown",
1169 "metadata": {},
996 "metadata": {},
1170 "source": [
997 "source": [
1171 "Now we will try to create a widget that only accepts dates in the year 2013. We render the widget without a description to verify that it can still render without a label."
998 "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."
1172 ]
999 ]
1173 },
1000 },
1174 {
1001 {
1175 "cell_type": "code",
1002 "cell_type": "code",
1176 "collapsed": false,
1003 "collapsed": false,
1177 "input": [
1004 "input": [
1178 "my_widget = DateWidget()\n",
1005 "my_widget = DateWidget()\n",
1179 "display(my_widget)\n",
1006 "display(my_widget)\n",
1180 "\n",
1007 "\n",
1181 "def validate_date(date):\n",
1008 "def validate_date(date):\n",
1182 " return not date is None and date.year == 2013\n",
1009 " return not date is None and date.year == 2014\n",
1183 "my_widget.on_validate(validate_date)"
1010 "my_widget.validation.register_callback(validate_date)"
1184 ],
1011 ],
1185 "language": "python",
1012 "language": "python",
1186 "metadata": {},
1013 "metadata": {},
1187 "outputs": [],
1014 "outputs": [],
1188 "prompt_number": 57
1015 "prompt_number": 47
1189 },
1016 },
1190 {
1017 {
1191 "cell_type": "code",
1018 "cell_type": "code",
1192 "collapsed": false,
1019 "collapsed": false,
1193 "input": [
1020 "input": [
1194 "# Try setting a valid date\n",
1021 "# Try setting a valid date\n",
1195 "my_widget.value = \"December 2, 2013\""
1022 "my_widget.value = \"December 2, 2014\""
1196 ],
1023 ],
1197 "language": "python",
1024 "language": "python",
1198 "metadata": {},
1025 "metadata": {},
1199 "outputs": [],
1026 "outputs": [],
1200 "prompt_number": 46
1027 "prompt_number": 48
1201 },
1028 },
1202 {
1029 {
1203 "cell_type": "code",
1030 "cell_type": "code",
1204 "collapsed": false,
1031 "collapsed": false,
1205 "input": [
1032 "input": [
1206 "# Try setting an invalid date\n",
1033 "# Try setting an invalid date\n",
1207 "my_widget.value = \"June 12, 1999\""
1034 "my_widget.value = \"June 12, 1999\""
1208 ],
1035 ],
1209 "language": "python",
1036 "language": "python",
1210 "metadata": {},
1037 "metadata": {},
1211 "outputs": [],
1038 "outputs": [],
1212 "prompt_number": 48
1039 "prompt_number": 49
1213 },
1040 },
1214 {
1041 {
1215 "cell_type": "code",
1042 "cell_type": "code",
1216 "collapsed": false,
1043 "collapsed": false,
1217 "input": [
1044 "input": [
1218 "my_widget.value"
1045 "my_widget.value"
1219 ],
1046 ],
1220 "language": "python",
1047 "language": "python",
1221 "metadata": {},
1048 "metadata": {},
1222 "outputs": [
1049 "outputs": [
1223 {
1050 {
1224 "metadata": {},
1051 "metadata": {},
1225 "output_type": "pyout",
1052 "output_type": "pyout",
1226 "prompt_number": 58,
1053 "prompt_number": 50,
1227 "text": [
1054 "text": [
1228 "u''"
1055 "u'2014-12-02'"
1229 ]
1056 ]
1230 }
1057 }
1231 ],
1058 ],
1232 "prompt_number": 58
1059 "prompt_number": 50
1233 },
1234 {
1235 "cell_type": "code",
1236 "collapsed": false,
1237 "input": [],
1238 "language": "python",
1239 "metadata": {},
1240 "outputs": []
1241 }
1060 }
1242 ],
1061 ],
1243 "metadata": {}
1062 "metadata": {}
1244 }
1063 }
1245 ]
1064 ]
1246 } No newline at end of file
1065 }
General Comments 0
You need to be logged in to leave comments. Login now