-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathindex.html
258 lines (213 loc) · 22.8 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="IPython Cookbook, ">
<!-- FAVICON -->
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
<link rel="alternate" href="https://ipython-books.github.io/feeds/all.atom.xml" type="application/atom+xml" title="IPython Cookbook Full Atom Feed"/>
<title>IPython Cookbook - 3.4. Creating custom Jupyter Notebook widgets in Python, HTML, and JavaScript</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/0.3.0/pure-min.css">
<!--[if lte IE 8]>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/0.5.0/pure-min.css">
<![endif]-->
<!--[if gt IE 8]><!-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/0.5.0/pure-min.css">
<!--<![endif]-->
<link rel="stylesheet" href="https://ipython-books.github.io/theme/css/styles.css">
<link rel="stylesheet" href="https://ipython-books.github.io/theme/css/pygments.css">
<!-- <link href='https://fonts.googleapis.com/css?family=Lato:300,400,700' rel='stylesheet' type='text/css'> -->
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,500" rel="stylesheet" type="text/css">
<link href='https://fonts.googleapis.com/css?family=Ubuntu+Mono' rel='stylesheet' type='text/css'>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
</head>
<body>
<header id="header" class="pure-g">
<div class="pure-u-1 pure-u-md-3-4">
<div id="menu">
<div class="pure-menu pure-menu-open pure-menu-horizontal">
<ul>
<li><a href="/">home</a></li>
<li><a href="https://github.com/ipython-books/cookbook-2nd-code">Jupyter notebooks</a></li>
<li><a href="https://github.com/ipython-books/minibook-2nd-code">minibook</a></li>
<li><a href="https://cyrille.rossant.net">author</a></li>
</ul> </div>
</div>
</div>
<div class="pure-u-1 pure-u-md-1-4">
<div id="social">
<div class="pure-menu pure-menu-open pure-menu-horizontal">
<ul>
<li><a href="https://twitter.com/cyrillerossant"><i class="fa fa-twitter"></i></a></li>
<li><a href="https://github.com/ipython-books/cookbook-2nd"><i class="fa fa-github"></i></a></li>
</ul> </div>
</div>
</div>
</header>
<div id="layout" class="pure-g">
<section id="content" class="pure-u-1 pure-u-md-4-4">
<div class="l-box">
<header id="page-header">
<h1>3.4. Creating custom Jupyter Notebook widgets in Python, HTML, and JavaScript</h1>
</header>
<section id="page">
<p><a href="/"><img src="https://raw.githubusercontent.com/ipython-books/cookbook-2nd/master/cover-cookbook-2nd.png" align="left" alt="IPython Cookbook, Second Edition" height="130" style="margin-right: 20px; margin-bottom: 10px;" /></a> <em>This is one of the 100+ free recipes of the <a href="/">IPython Cookbook, Second Edition</a>, by <a href="http://cyrille.rossant.net">Cyrille Rossant</a>, a guide to numerical computing and data science in the Jupyter Notebook. The ebook and printed book are available for purchase at <a href="https://www.packtpub.com/big-data-and-business-intelligence/ipython-interactive-computing-and-visualization-cookbook-second-e">Packt Publishing</a>.</em></p>
<p>▶ <em><a href="https://github.com/ipython-books/cookbook-2nd">Text on GitHub</a> with a <a href="https://creativecommons.org/licenses/by-nc-nd/3.0/us/legalcode">CC-BY-NC-ND license</a></em><br />
▶ <em><a href="https://github.com/ipython-books/cookbook-2nd-code">Code on GitHub</a> with a <a href="https://opensource.org/licenses/MIT">MIT license</a></em></p>
<p>▶ <a href="https://ipython-books.github.io/chapter-3-mastering-the-jupyter-notebook/"><strong><em>Go to</em></strong> <em>Chapter 3 : Mastering the Jupyter Notebook</em></a><br />
▶ <a href="https://github.com/ipython-books/cookbook-2nd-code/blob/master/chapter03_notebook/04_custom_widgets.ipynb"><em><strong>Get</strong> the Jupyter notebook</em></a> </p>
<p>The ipywidgets packages provides many built-in control widgets to interact with code and data in the Jupyter Notebook. In this recipe, we show how to build a custom interactive widget from scratch, using Python on the kernel side, and HTML/JavaScript on the client side (frontend). The widget just displays two buttons to increase and decrease a number. The number can be accessed and updated either from the kernel (Python code) or the client (browser).</p>
<h2>How to do it...</h2>
<p><strong>1. </strong> Let's import the packages:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">ipywidgets</span> <span class="kn">as</span> <span class="nn">widgets</span>
<span class="kn">from</span> <span class="nn">traitlets</span> <span class="kn">import</span> <span class="n">Unicode</span><span class="p">,</span> <span class="n">Int</span><span class="p">,</span> <span class="n">validate</span>
</pre></div>
<p><strong>2. </strong> We create a <code>CounterWidget</code> class deriving from <code>DOMWidget</code>:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CounterWidget</span><span class="p">(</span><span class="n">widgets</span><span class="o">.</span><span class="n">DOMWidget</span><span class="p">):</span>
<span class="n">_view_name</span> <span class="o">=</span> <span class="n">Unicode</span><span class="p">(</span><span class="s1">'CounterView'</span><span class="p">)</span><span class="o">.</span><span class="n">tag</span><span class="p">(</span><span class="n">sync</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">_view_module</span> <span class="o">=</span> <span class="n">Unicode</span><span class="p">(</span><span class="s1">'counter'</span><span class="p">)</span><span class="o">.</span><span class="n">tag</span><span class="p">(</span><span class="n">sync</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">Int</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">tag</span><span class="p">(</span><span class="n">sync</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
<p>This class represents the Python part of the widget. The <code>_view_name</code> and <code>_view_module</code> attributes refer to the name and module of the JavaScript part. We use the traitlets package to specify the type of the variables. The <code>value</code> attribute is the counter value, an integer initialized at 0. All of these attributes' values are synchronized between Python and JavaScript, hence the <code>sync=True</code> option.</p>
<p><strong>3. </strong> We now turn to the JavaScript side of the widget. We can write the code directly in the notebook using the <code>%%javascript</code> cell magic. The widgets framework relies on several JavaScript libraries: jQuery (represented as the <code>$</code> variable), require.js (modules and dependencies), and Backbone.js (a model view controller framework):</p>
<div class="highlight"><pre><span></span><span class="o">%%</span><span class="n">javascript</span>
<span class="o">//</span> <span class="n">We</span> <span class="n">make</span> <span class="n">sure</span> <span class="n">the</span> <span class="sb">`counter`</span> <span class="n">module</span> <span class="ow">is</span> <span class="n">defined</span>
<span class="o">//</span> <span class="n">only</span> <span class="n">once</span><span class="o">.</span>
<span class="n">require</span><span class="o">.</span><span class="n">undef</span><span class="p">(</span><span class="s1">'counter'</span><span class="p">);</span>
<span class="o">//</span> <span class="n">We</span> <span class="n">define</span> <span class="n">the</span> <span class="sb">`counter`</span> <span class="n">module</span> <span class="n">depending</span> <span class="n">on</span> <span class="n">the</span>
<span class="o">//</span> <span class="n">Jupyter</span> <span class="n">widgets</span> <span class="n">framework</span><span class="o">.</span>
<span class="n">define</span><span class="p">(</span><span class="s1">'counter'</span><span class="p">,</span> <span class="p">[</span><span class="s2">"@jupyter-widgets/base"</span><span class="p">],</span>
<span class="n">function</span><span class="p">(</span><span class="n">widgets</span><span class="p">)</span> <span class="p">{</span>
<span class="o">//</span> <span class="n">We</span> <span class="n">create</span> <span class="n">the</span> <span class="n">CounterView</span> <span class="n">frontend</span> <span class="n">class</span><span class="p">,</span>
<span class="o">//</span> <span class="n">deriving</span> <span class="kn">from</span> <span class="nn">DOMWidgetView.</span>
<span class="n">var</span> <span class="n">CounterView</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">DOMWidgetView</span><span class="o">.</span><span class="n">extend</span><span class="p">({</span>
<span class="o">//</span> <span class="n">This</span> <span class="n">method</span> <span class="n">creates</span> <span class="n">the</span> <span class="n">HTML</span> <span class="n">widget</span><span class="o">.</span>
<span class="n">render</span><span class="p">:</span> <span class="n">function</span><span class="p">()</span> <span class="p">{</span>
<span class="o">//</span> <span class="n">The</span> <span class="n">value_changed</span><span class="p">()</span> <span class="n">method</span> <span class="n">should</span> <span class="n">be</span>
<span class="o">//</span> <span class="n">called</span> <span class="n">when</span> <span class="n">the</span> <span class="n">model</span><span class="s1">'s value changes</span>
<span class="o">//</span> <span class="n">on</span> <span class="n">the</span> <span class="n">kernel</span> <span class="n">side</span><span class="o">.</span>
<span class="n">this</span><span class="o">.</span><span class="n">value_changed</span><span class="p">();</span>
<span class="n">this</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">on</span><span class="p">(</span><span class="s1">'change:value'</span><span class="p">,</span>
<span class="n">this</span><span class="o">.</span><span class="n">value_changed</span><span class="p">,</span> <span class="n">this</span><span class="p">);</span>
<span class="n">var</span> <span class="n">model</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">model</span><span class="p">;</span>
<span class="n">var</span> <span class="n">that</span> <span class="o">=</span> <span class="n">this</span><span class="p">;</span>
<span class="o">//</span> <span class="n">We</span> <span class="n">create</span> <span class="n">the</span> <span class="n">plus</span> <span class="ow">and</span> <span class="n">minus</span> <span class="n">buttons</span><span class="o">.</span>
<span class="n">this</span><span class="o">.</span><span class="n">bm</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="s1">'<button/>'</span><span class="p">)</span>
<span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)</span>
<span class="o">.</span><span class="n">click</span><span class="p">(</span><span class="n">function</span><span class="p">()</span> <span class="p">{</span>
<span class="o">//</span> <span class="n">When</span> <span class="n">the</span> <span class="n">button</span> <span class="ow">is</span> <span class="n">clicked</span><span class="p">,</span>
<span class="o">//</span> <span class="n">the</span> <span class="n">model</span><span class="s1">'s value is updated.</span>
<span class="n">var</span> <span class="n">x</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'value'</span><span class="p">);</span>
<span class="n">model</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'value'</span><span class="p">,</span> <span class="n">x</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">that</span><span class="o">.</span><span class="n">touch</span><span class="p">();</span>
<span class="p">});</span>
<span class="n">this</span><span class="o">.</span><span class="n">bp</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="s1">'<button/>'</span><span class="p">)</span>
<span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s1">'+'</span><span class="p">)</span>
<span class="o">.</span><span class="n">click</span><span class="p">(</span><span class="n">function</span><span class="p">()</span> <span class="p">{</span>
<span class="n">var</span> <span class="n">x</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'value'</span><span class="p">);</span>
<span class="n">model</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'value'</span><span class="p">,</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">that</span><span class="o">.</span><span class="n">touch</span><span class="p">();</span>
<span class="p">});</span>
<span class="o">//</span> <span class="n">This</span> <span class="n">element</span> <span class="n">displays</span> <span class="n">the</span> <span class="n">current</span>
<span class="o">//</span> <span class="n">value</span> <span class="n">of</span> <span class="n">the</span> <span class="n">counter</span><span class="o">.</span>
<span class="n">this</span><span class="o">.</span><span class="n">span</span> <span class="o">=</span> <span class="err">$</span><span class="p">(</span><span class="s1">'<span />'</span><span class="p">)</span>
<span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s1">'0'</span><span class="p">)</span>
<span class="o">.</span><span class="n">css</span><span class="p">({</span><span class="n">marginLeft</span><span class="p">:</span> <span class="s1">'10px'</span><span class="p">,</span>
<span class="n">marginRight</span><span class="p">:</span> <span class="s1">'10px'</span><span class="p">});</span>
<span class="o">//</span> <span class="n">this</span><span class="o">.</span><span class="n">el</span> <span class="n">represents</span> <span class="n">the</span> <span class="n">widget</span><span class="s1">'s DOM</span>
<span class="o">//</span> <span class="n">element</span><span class="o">.</span> <span class="n">We</span> <span class="n">add</span> <span class="n">the</span> <span class="n">minus</span> <span class="n">button</span><span class="p">,</span>
<span class="o">//</span> <span class="n">the</span> <span class="n">span</span> <span class="n">element</span><span class="p">,</span> <span class="ow">and</span> <span class="n">the</span> <span class="n">plus</span> <span class="n">button</span><span class="o">.</span>
<span class="err">$</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">el</span><span class="p">)</span>
<span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">bm</span><span class="p">)</span>
<span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">span</span><span class="p">)</span>
<span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">bp</span><span class="p">);</span>
<span class="p">},</span>
<span class="n">value_changed</span><span class="p">:</span> <span class="n">function</span><span class="p">()</span> <span class="p">{</span>
<span class="o">//</span> <span class="n">Update</span> <span class="n">the</span> <span class="n">displayed</span> <span class="n">number</span> <span class="n">when</span> <span class="n">the</span>
<span class="o">//</span> <span class="n">counter</span><span class="s1">'s value changes.</span>
<span class="n">var</span> <span class="n">x</span> <span class="o">=</span> <span class="n">this</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'value'</span><span class="p">);</span>
<span class="err">$</span><span class="p">(</span><span class="err">$</span><span class="p">(</span><span class="n">this</span><span class="o">.</span><span class="n">el</span><span class="p">)</span><span class="o">.</span><span class="n">children</span><span class="p">()[</span><span class="mi">1</span><span class="p">])</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
<span class="p">},</span>
<span class="p">});</span>
<span class="k">return</span> <span class="p">{</span>
<span class="n">CounterView</span> <span class="p">:</span> <span class="n">CounterView</span>
<span class="p">};</span>
<span class="p">});</span>
</pre></div>
<p><strong>4. </strong> Let's display the widget:</p>
<div class="highlight"><pre><span></span><span class="n">w</span> <span class="o">=</span> <span class="n">CounterWidget</span><span class="p">()</span>
<span class="n">w</span>
</pre></div>
<p><img alt="Custom widget" src="https://ipython-books.github.io/pages/chapter03_notebook/04_custom_widgets_files/04_custom_widgets_11_0.png" /></p>
<p><strong>5. </strong> Pressing the buttons updates the value immediately.</p>
<p><img alt="Custom widget" src="https://ipython-books.github.io/pages/chapter03_notebook/04_custom_widgets_files/widget2.png" /></p>
<p><strong>6. </strong> The counter's value is automatically updated on the kernel side:</p>
<div class="highlight"><pre><span></span><span class="k">print</span><span class="p">(</span><span class="n">w</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
</pre></div>
<div class="highlight"><pre><span></span>4
</pre></div>
<p><strong>7. </strong> Conversely, we can update the value from Python, and it is updated in the frontend:</p>
<div class="highlight"><pre><span></span><span class="n">w</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">5</span>
</pre></div>
<p><img alt="Custom widget" src="https://ipython-books.github.io/pages/chapter03_notebook/04_custom_widgets_files/04_custom_widgets_17_0.png" /></p>
<h2>There's more...</h2>
<p>Here are a few references:</p>
<ul>
<li>Custom widget tutorial at <a href="https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html">https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html</a></li>
<li>Require.js library at <a href="http://requirejs.org/">http://requirejs.org/</a></li>
<li>Backbone.js library at <a href="http://backbonejs.org/">http://backbonejs.org/</a></li>
</ul>
<h2>See also</h2>
<ul>
<li>Mastering widgets in the Jupyter Notebook</li>
</ul>
</section>
</div>
</section>
<footer id="footer" class="pure-u-1 pure-u-md-4-4">
<div class="l-box">
<div>
<p>© <a href="https://cyrille.rossant.net">Cyrille Rossant</a> –
Built with <a href="https://github.com/PurePelicanTheme/pure-single">Pure Theme</a>
for <a href="https://blog.getpelican.com/">Pelican</a>
</p>
</div>
</div>
</footer>
</div>
<!-- Start of StatCounter Code for Default Guide -->
<script type="text/javascript">
var sc_project=9752080;
var sc_invisible=1;
var sc_security="c177b501";
var scJsHost = (("https:" == document.location.protocol) ?
"https://secure." : "http://www.");
</script>
<script type="text/javascript"
src="https://www.statcounter.com/counter/counter.js"
async></script>
<noscript><div class="statcounter"><a title="Web Analytics"
href="https://statcounter.com/" target="_blank"><img
class="statcounter"
src="//c.statcounter.com/9752080/0/c177b501/1/" alt="Web
Analytics"></a></div></noscript>
<!-- End of StatCounter Code for Default Guide -->
</body>
</html>