Last active
January 19, 2016 17:28
-
-
Save lbustelo/30fd748973be52edcb71 to your computer and use it in GitHub Desktop.
Tutorial Notebook for jupyter-declarativewidgets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Simple Todo App\n", | |
"This tutorial takes you through a step by step look at how to put together a very basic Todo application using [`jupyter-declarativewidgets`](https://github.com/jupyter-incubator/declarativewidgets)." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Step 1. The Python Code\n", | |
"\n", | |
"We are going to keep it simple to start with. The Todo `list` will be a Python variable. We will define some simple functions to `add` and `remove` Todo items." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"todo = []\n", | |
"\n", | |
"def addTodo(title, date=None):\n", | |
" global todo\n", | |
" todo = todo + [(title, date)]\n", | |
"\n", | |
"def removeTodo(idx:int):\n", | |
" global todo\n", | |
" del todo[idx]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Lets test the code" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"addTodo('a')\n", | |
"addTodo('b')\n", | |
"addTodo('c')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"todo" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"removeTodo(1)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"todo" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Step 2. Lets visualize the list\n", | |
"This is where we can use a simple `HTML` template using [Polymer](https://www.polymer-project.org/1.0/) to render the list. The `template` is an `urth-core-bind`, one of the `declarativewidgets` elements, which enhances data binding capabilities in several ways that you will see through the course of this tutorial. In the template we are using the binding variable `{{todo}}` to build the UI and a [dom-repeat](https://www.polymer-project.org/1.0/docs/devguide/templates.html#dom-repeat) `template` to build a simple `HTML` list." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%%HTML\n", | |
"<template is=\"urth-core-bind\">\n", | |
" <ul>\n", | |
" <template is=\"dom-repeat\" items=\"{{todo}}\">\n", | |
" <li>{{item.0}}</li>\n", | |
" </template>\n", | |
" </ul>\n", | |
"</template>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"But where are the Todo items?| Well... we need to send the data to the browser so it can be rendered. This is done with the following `Python` code." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from urth.widgets.widget_channels import channel\n", | |
"channel().set(\"todo\", todo)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Noticed what happened? Now the items of the list are rendered in our HTML template above." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Step 3. Lets add to the list\n", | |
"Now we need some UI to add new items to the list. A simple `HTML` form will do, but we need to connect the form submition to a call to the `addTodo` `Python` function. This is done by using the `urth-core-function` element. This element bridges the `HTML` markup and the `Python` code. \n", | |
"\n", | |
"The basics are:\n", | |
"* 'ref' is the name of the function in `Python` (i.e. `addTodo`)\n", | |
"* Parameters for the function are expose in the form `arg-param_name`.\n", | |
"* use `{{}}` template variables to bind values from the form to the function.\n", | |
"* calling the `invoke` method of the `urth-core-function` invokes the `Python` function.\n", | |
"\n", | |
"More details on all elements [here](http://jupyter-incubator.github.io/declarativewidgets/docs.html)." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%%HTML\n", | |
"<template is=\"urth-core-bind\">\n", | |
" <urth-core-function id=\"addTodo\" ref=\"addTodo\" arg-title=\"{{title}}\" arg-date=\"{{dueDate}}\"></urth-core-function> \n", | |
" <br/>Add New Item\n", | |
" <form onSubmit=\"return false;\">\n", | |
" title: <input id=\"titleIn\" type=\"text\" value=\"{{title::change}}\"></input> \n", | |
" date: <input id=\"dateIn\" type=\"text\" value=\"{{dueDate::change}}\"></input> \n", | |
" <button onClick=\"addTodo.invoke(); titleIn.value=''; dateIn.value=''\">Add</button>\n", | |
" </form>\n", | |
"</template>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Try adding something to the Todo list. The cell below show that the items are added." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"todo" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Notice that the UI we created above it is not getting updated. The browser has no clue that the `Python` `todo` variable has changes. We need to modify the `addTodo` function to notify that the `todo` list has changed. That is done using `channel().set(\"todo\", todo)`." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"def addTodo(title, date=None):\n", | |
" global todo\n", | |
" todo = todo + [(title, date)]\n", | |
" channel().set(\"todo\", todo)\n", | |
" \n", | |
"# Only here to refresh the UI \n", | |
"channel().set(\"todo\", todo)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Now try adding another item and notice that the list ui is not getting updated." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Step 3. Lets remove from the list\n", | |
"We will modify our original UI to add a remove button. Now each item will show its content and a button used to remove the item from the Todo list." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%%HTML\n", | |
"<template is=\"urth-core-bind\">\n", | |
" <ul>\n", | |
" <template is=\"dom-repeat\" items=\"{{todo}}\">\n", | |
" <li>\n", | |
" <urth-core-function ref=\"removeTodo\" arg-idx=\"[[index]]\" onClick=\"this.invoke()\">\n", | |
" <button>x</button> \n", | |
" </urth-core-function> \n", | |
" {{item.0}}\n", | |
" </li>\n", | |
" </template>\n", | |
" </ul>\n", | |
"</template>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Notice that clicking on the button is not removing the items from the UI. As with `addTod`, we need to modify `removeTodo` to notify that the `todo` list has chanted." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def removeTodo(idx:int):\n", | |
" global todo\n", | |
" del todo[idx]\n", | |
" channel().set(\"todo\", todo)\n", | |
" \n", | |
"# Only here to refresh the UI \n", | |
"channel().set(\"todo\", todo)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Try removing an item now. You should see the list getting updated." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true | |
}, | |
"source": [ | |
"#### Step 4. Lets make it look a bit nicer\n", | |
"With `declarativewidgets` we can import new elements to make our UI look nicer. " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%%HTML\n", | |
"<link rel='import' href='urth_components/paper-card/paper-card.html' \n", | |
" is='urth-core-import' package='PolymerElements/paper-card'>\n", | |
"<link rel='import' href='urth_components/paper-item/paper-item.html' \n", | |
" is='urth-core-import' package='PolymerElements/paper-item'>\n", | |
"<link rel='import' href='urth_components/paper-input/paper-input.html' \n", | |
" is='urth-core-import' package='PolymerElements/paper-input'>\n", | |
"<link rel='import' href='urth_components/paper-icon-button/paper-icon-button.html' \n", | |
" is='urth-core-import' package='PolymerElements/paper-icon-button'>\n", | |
"<link rel='import' href='urth_components/paper-button/paper-button.html' \n", | |
" is='urth-core-import' package='PolymerElements/paper-button'>\n", | |
"<link rel='import' href='urth_components/iron-icons/iron-icons.html' \n", | |
" is='urth-core-import' package='PolymerElements/iron-icons'>\n", | |
"<link rel='import' href='urth_components/iron-collapse/iron-collapse.html' \n", | |
" is='urth-core-import' package='PolymerElements/iron-collapse'>\n", | |
"<link rel='import' href='urth_components/paper-date-picker/paper-date-picker.html' \n", | |
" is='urth-core-import' package='bendavis78/paper-date-picker'>\n", | |
"\n", | |
"<style is=\"custom-style\">\n", | |
" paper-card {\n", | |
" width: 100%;\n", | |
" }\n", | |
" \n", | |
" .list {\n", | |
" @apply(--layout-vertical);\n", | |
" }\n", | |
"</style> \n", | |
"\n", | |
"<template is=\"urth-core-bind\"> \n", | |
" <paper-card heading=\"Todo List\" style=\"width: 100%\">\n", | |
" <div class=\"card-content\">\n", | |
" <div class=\"list\" role=\"listbox\">\n", | |
" <template is=\"dom-repeat\" items=\"{{todo}}\">\n", | |
" <paper-item>\n", | |
" <urth-core-function ref=\"removeTodo\" arg-idx=\"[[index]]\" onClick=\"this.invoke()\" delay=\"250\">\n", | |
" <paper-icon-button icon=\"clear\">X</paper-icon-button>\n", | |
" </urth-core-function> \n", | |
" <span>{{item.0}}</span>\n", | |
" <template is=\"dom-if\" if=\"{{item.1}}\">\n", | |
" <span style=\"color: gray\"> ({{item.1}})</span>\n", | |
" </template>\n", | |
" </paper-item>\n", | |
" </template>\n", | |
" <div>\n", | |
" </div>\n", | |
" <div class=\"card-actions\">\n", | |
" <paper-button onClick=\"newItemForm.toggle(); titleIn2.value=''\">New Item</paper-button>\n", | |
" <iron-collapse id=\"newItemForm\">\n", | |
" <div class=\"content\">\n", | |
" <paper-input id=\"titleIn2\" label=\"Title\" value=\"{{title}}\"></paper-input>\n", | |
" <label style=\"display: block; color: gray; border-bottom: 1px solid #737373; margin-top: 2px;\">Due Date</label>\n", | |
" <div>\n", | |
" <paper-date-picker date=\"{{dueDate}}\"></paper-date-picker>\n", | |
" </div>\n", | |
" <div style=\"text-align: right; margin: 10px 20px\">\n", | |
" <urth-core-function ref=\"addTodo\" arg-title=\"{{title}}\" arg-date=\"{{dueDate}}\" onClick=\"this.invoke();\" delay=\"250\">\n", | |
" <paper-button raised onClick=\"newItemForm.toggle()\"><iron-icon icon=\"add\"></iron-icon>Add Item</paper-button>\n", | |
" </urth-core-function>\n", | |
" </div>\n", | |
" </div>\n", | |
" </iron-collapse>\n", | |
" </div>\n", | |
" </paper-card> \n", | |
"</template>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.4.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment