remove widgets

Min RK 11 years ago
parent cc5751d5ac
commit c5536cb113

@ -1,120 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exploring Beat Frequencies using the `Audio` Object"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This example uses the `Audio` object and Matplotlib to explore the phenomenon of beat frequencies."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interactive\n",
"from IPython.display import Audio, display\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def beat_freq(f1=220.0, f2=224.0):\n",
" max_time = 3\n",
" rate = 8000\n",
" times = np.linspace(0,max_time,rate*max_time)\n",
" signal = np.sin(2*np.pi*f1*times) + np.sin(2*np.pi*f2*times)\n",
" print(f1, f2, abs(f1-f2))\n",
" display(Audio(data=signal, rate=rate))\n",
" return signal"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"v = interactive(beat_freq, f1=(200.0,300.0), f2=(200.0,300.0))\n",
"display(v)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"v.kwargs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"f1, f2 = v.children\n",
"f1.value = 255\n",
"f2.value = 260\n",
"plt.plot(v.result[0:6000])"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,793 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Styling.ipynb)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from __future__ import print_function # For py 2.7 compat"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Building a Custom Widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The widget framework is built **on top of the Comm framework** (short for communication). The Comm framework is a framework that **allows you send/receive JSON messages** to/from the front-end (as seen below).\n",
"\n",
"![Widget layer](images/WidgetArch.png)\n",
"\n",
"To create a custom widget, you need to **define the widget both in the back-end and in the front-end**. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Building a Custom Widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get started, you'll create a **simple hello world widget**. Later you'll build on this foundation to make more complex widgets."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Back-end (Python)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### DOMWidget and Widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To define a widget, you must inherit from the **Widget or DOMWidget** base class. If you intend for your widget to be **displayed in the IPython notebook**, you'll need to **inherit from the DOMWidget**. The DOMWidget class itself inherits from the Widget class. The Widget class is useful for cases in which the **Widget is not meant to be displayed directly in the notebook**, but **instead as a child of another rendering environment**. For example, if you wanted to create a three.js widget (a popular WebGL library), you would implement the rendering window as a DOMWidget and any 3D objects or lights meant to be rendered in that window as Widgets."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### _view_name"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Inheriting from the DOMWidget does not tell the widget framework what front-end widget to associate with your back-end widget. Instead, you must tell it yourself by defining a **specially named Traitlet, `_view_name`** (as seen below)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook import widgets\n",
"from traitlets import Unicode\n",
"\n",
"class HelloWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('HelloView', sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### sync=True traitlets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Traitlets is** an IPython library for defining **type-safe properties** on configurable objects. For this tutorial you do not need to worry about the *configurable* piece of the traitlets machinery. The **`sync=True` keyword argument** tells the widget framework to **handle synchronizing that value to the front-end**. Without `sync=True`, the front-end would have no knowledge of `_view_name`."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Other traitlet types"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unicode, used for _view_name, is not the only Traitlet type, there are many more some of which are listed below: \n",
"\n",
"- Any\n",
"- Bool\n",
"- Bytes\n",
"- CBool\n",
"- CBytes\n",
"- CComplex\n",
"- CFloat\n",
"- CInt\n",
"- CLong\n",
"- CRegExp\n",
"- CUnicode\n",
"- CaselessStrEnum\n",
"- Complex\n",
"- Dict\n",
"- DottedObjectName\n",
"- Enum\n",
"- Float\n",
"- FunctionType\n",
"- Instance\n",
"- InstanceType\n",
"- Int\n",
"- List\n",
"- Long\n",
"- Set\n",
"- TCPAddress\n",
"- Tuple\n",
"- Type\n",
"- Unicode\n",
"- Union\n",
"\n",
"\n",
"**Not all of these traitlets can be synchronized** across the network, **only the JSON-able** traits and **Widget instances** will be synchronized."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Front-end (JavaScript)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Models and views"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The IPython widget framework front-end relies heavily on [Backbone.js](http://backbonejs.org/). **Backbone.js is an MVC (model view controller) framework**. Widgets defined in the back-end are automatically **synchronized with generic Backbone.js models** in the front-end. The traitlets are added to the front-end instance **automatically on first state push**. The **`_view_name` trait** that you defined earlier is used by the widget framework to create the corresponding Backbone.js view and **link that view to the model**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Import the WidgetManager"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You first need to **import the `widget` and `manager` modules**. You will use the manager later to register your view by name (the same name you used in the back-end). To import the modules, use the `require` method of [require.js](http://requirejs.org/) (as seen below).\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Define the view"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next define your widget view class. **Inherit from the `DOMWidgetView`** by using the `.extend` method. Register the view class with the widget manager by calling **`.register_widget_view`**. The **first parameter is the widget view name** (`_view_name` that you defined earlier in Python) and the **second is a handle to the class type**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the HelloView\n",
" var HelloView = widget.DOMWidgetView.extend({\n",
" \n",
" });\n",
" \n",
" // Register the HelloView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('HelloView', HelloView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Render method"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lastly, **override the base `render` method** of the view to define custom rendering logic. A handle to the widget's default div element can be acquired via **`this.$el`**. The `$el` property is a **[jQuery](http://jquery.com/) object handle** (which can be thought of as a supercharged version of the normal DOM element's handle)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" var HelloView = widget.DOMWidgetView.extend({\n",
" \n",
" // Render the view.\n",
" render: function(){ \n",
" this.$el.text('Hello World!'); \n",
" },\n",
" });\n",
" \n",
" manager.WidgetManager.register_widget_view('HelloView', HelloView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You should be able to display your widget just like any other widget now."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"HelloWidget()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Making the widget stateful"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is not much that you can do with the above example that you can't do with the IPython display framework. To change this, you will make the widget stateful. Instead of displaying a static \"hello world\" message, it will **display a string set by the back-end**. First you need to **add a traitlet in the back-end**. Use the name of **`value` to stay consistent** with the rest of the widget framework and to **allow your widget to be used with interact**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class HelloWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('HelloView', sync=True)\n",
" value = Unicode('Hello World!', sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Accessing the model from the view"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To access the model associate with a view instance, use the **`model` property** of the view. **`get` and `set`** methods are used to interact with the Backbone model. **`get` is trivial**, however you have to **be careful when using `set`**. **After calling the model `set`** you need call the **view's `touch` method**. This associates the `set` operation with a particular view so **output will be routed to the correct cell**. The model also has a **`on` method** which allows you to listen to events triggered by the model (like value changes)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Rendering model contents"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By **replacing the string literal with a call to `model.get`**, the view will now display the **value of the back-end upon display**. However, it will not update itself to a new value when the value changes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" var HelloView = widget.DOMWidgetView.extend({\n",
" \n",
" render: function(){ \n",
" this.$el.text(this.model.get('value')); \n",
" },\n",
" });\n",
" \n",
" manager.WidgetManager.register_widget_view('HelloView', HelloView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Dynamic updates"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get the view to **update itself dynamically**, register a function to update the view's value when the model's `value` property changes. This can be done using the **`model.on` method**. The `on` method takes three parameters, an event name, callback handle, and callback context. The Backbone **event named `change`** will fire whenever the model changes. By **appending `:value`** to it, you tell Backbone to only listen to the change event of the `value` property (as seen below)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" var HelloView = widget.DOMWidgetView.extend({\n",
" \n",
" render: function(){ \n",
" this.value_changed();\n",
" this.model.on('change:value', this.value_changed, this);\n",
" },\n",
" \n",
" value_changed: function() {\n",
" this.$el.text(this.model.get('value')); \n",
" },\n",
" });\n",
" \n",
" manager.WidgetManager.register_widget_view('HelloView', HelloView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Test"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = HelloWidget()\n",
"w"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value = 'test'"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Finishing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bidirectional communication"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The examples above dump the value directly into the DOM. There is no way for you to interact with this dumped data in the front-end. To create an example that **accepts input**, you will have to do something more than blindly dumping the contents of value into the DOM. In this part of the tutorial, you will **use a jQuery spinner** to display and accept input in the front-end. IPython currently lacks a spinner implementation so this widget will be unique."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Update the Python code"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You will need to change the type of the **value traitlet to `Int`**. It also makes sense to **change the name of the widget** to something more appropriate, like `SpinnerWidget`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from traitlets import CInt\n",
"class SpinnerWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('SpinnerView', sync=True)\n",
" value = CInt(0, sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Updating the Javascript code"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The [jQuery docs for the spinner control](https://jqueryui.com/spinner/) say to use **`.spinner` to create a spinner** in an element. Calling **`.spinner` on `$el` will create a spinner inside `$el`**. Make sure to **update the widget name here too** so it's the same as `_view_name` in the back-end."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" var SpinnerView = widget.DOMWidgetView.extend({\n",
" \n",
" render: function(){ \n",
" \n",
" // jQuery code to create a spinner and append it to $el\n",
" this.$input = $('<input />');\n",
" this.$el.append(this.$input);\n",
" this.$spinner = this.$input.spinner({\n",
" change: function( event, ui ) {}\n",
" });\n",
" \n",
" this.value_changed();\n",
" this.model.on('change:value', this.value_changed, this);\n",
" },\n",
" \n",
" value_changed: function() {\n",
" \n",
" },\n",
" });\n",
" \n",
" manager.WidgetManager.register_widget_view('SpinnerView', SpinnerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Getting and setting the value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To **set the value of the spinner on update from the back-end**, you need to use **jQuery's `spinner` API**. `spinner.spinner('value', new)` will set the value of the spinner. Add that code to the **`value_changed` method** to make the spinner **update with the value stored in the back-end((. Using jQuery's spinner API, you can add a function to handle the **spinner `change` event** by passing it in when constructing the spinner. Inside the `change` event, call **`model.set`** to set the value and then **`touch`** to inform the framework that this view was the view that caused the change to the model. **Note: The `var that = this;` is a JavaScript trick to pass the current context into closures.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" var SpinnerView = widget.DOMWidgetView.extend({\n",
" \n",
" render: function(){ \n",
"\n",
" var that = this;\n",
" this.$input = $('<input />');\n",
" this.$el.append(this.$input);\n",
" this.$spinner = this.$input.spinner({\n",
" change: function( event, ui ) {\n",
" that.handle_spin();\n",
" },\n",
" spin: function( event, ui ) {\n",
" that.handle_spin();\n",
" }\n",
" });\n",
" \n",
" this.value_changed();\n",
" this.model.on('change:value', this.value_changed, this);\n",
" },\n",
" \n",
" value_changed: function() {\n",
" this.$spinner.spinner('value', this.model.get('value'));\n",
" },\n",
" \n",
" handle_spin: function() {\n",
" this.model.set('value', this.$spinner.spinner('value'));\n",
" this.touch();\n",
" },\n",
" });\n",
" \n",
" manager.WidgetManager.register_widget_view('SpinnerView', SpinnerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Test"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = SpinnerWidget(value=5)\n",
"w"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value = 20"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Trying to **use the spinner with another widget**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"w1 = SpinnerWidget(value=0)\n",
"w2 = widgets.IntSlider()\n",
"display(w1,w2)\n",
"\n",
"from traitlets import link\n",
"mylink = link((w1, 'value'), (w2, 'value'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Styling.ipynb)"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,838 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Before reading, make sure to review\n",
"\n",
"- [MVC prgramming](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)\n",
"- [Backbone.js](https://www.codeschool.com/courses/anatomy-of-backbonejs)\n",
"- [The widget IPEP](https://github.com/ipython/ipython/wiki/IPEP-23%3A-Backbone.js-Widgets)\n",
"- [The original widget PR discussion](https://github.com/ipython/ipython/pull/4374)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from __future__ import print_function # For py 2.7 compat\n",
"\n",
"from jupyter_notebook import widgets # Widget definitions\n",
"from IPython.display import display # Used to display widgets in the notebook\n",
"from traitlets import Unicode # Used to declare attributes of our widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Abstract"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook implements a custom date picker widget,\n",
"in order to demonstrate the widget creation process.\n",
"\n",
"To create a custom widget, both Python and JavaScript code is required."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Section 1 - Basics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When starting a project like this, it is often easiest to make a simple base implementation,\n",
"to verify that the underlying framework is working as expected.\n",
"To start, we will create an empty widget and make sure that it can be rendered.\n",
"The first step is to define the widget in Python."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our widget inherits from `widgets.DOMWidget` since it is intended that it will be displayed in the notebook directly.\n",
"The `_view_name` trait is special; the widget framework will read the `_view_name` trait to determine what Backbone view the widget is associated with.\n",
"**Using `sync=True` is very important** because it tells the widget framework that that specific traitlet should be synced between the front- and back-ends."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## JavaScript"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the IPython notebook [require.js](http://requirejs.org/) is used to load JavaScript dependencies.\n",
"All IPython widget code depends on `widgets/js/widget.js`,\n",
"where the base widget model and base view are defined.\n",
"We use require.js to load this file:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\"], function(WidgetManager){\n",
"\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we need to define a view that can be used to represent the model.\n",
"To do this, the `IPython.DOMWidgetView` is extended.\n",
"**A render function must be defined**.\n",
"The render function is used to render a widget view instance to the DOM.\n",
"For now, the render function renders a div that contains the text *Hello World!*\n",
"Lastly, the view needs to be registered with the widget manager, for which we need to load another module.\n",
"\n",
"**Final JavaScript code below:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the DatePickerView\n",
" var DatePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){ this.$el.text('Hello World!'); },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To test what we have so far, create the widget, just like you would the builtin widgets:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"DateWidget()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Section 2 - Something useful"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the last section we created a simple widget that displayed *Hello World!*\n",
"To make an actual date widget, we need to add a property that will be synced between the Python model and the JavaScript model.\n",
"The new attribute must be a traitlet, so the widget machinery can handle it.\n",
"The traitlet must be constructed with a `sync=True` keyword argument, to tell the widget machinery knows to synchronize it with the front-end.\n",
"Adding this to the code from the last section:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)\n",
" value = Unicode(sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## JavaScript"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the JavaScript, there is no need to define counterparts to the traitlets.\n",
"When the JavaScript model is created for the first time,\n",
"it copies all of the traitlet `sync=True` attributes from the Python model.\n",
"We need to replace *Hello World!* with an actual HTML date picker widget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the DatePickerView\n",
" var DatePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" \n",
" // Create the date picker control.\n",
" this.$date = $('<input />')\n",
" .attr('type', 'date')\n",
" .appendTo(this.$el);\n",
" },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the DatePickerView\n",
" var DatePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" \n",
" // Create the date picker control.\n",
" this.$date = $('<input />')\n",
" .attr('type', 'date')\n",
" .appendTo(this.$el);\n",
" },\n",
" \n",
" update: function() {\n",
" \n",
" // Set the value of the date control and then call base.\n",
" this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
" return DatePickerView.__super__.update.apply(this);\n",
" },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To get the changed value from the frontend to publish itself to the backend,\n",
"we need to listen to the change event triggered by the HTM date control and set the value in the model.\n",
"After the date change event fires and the new value is set in the model,\n",
"it is very important that we call `this.touch()` to let the widget machinery know which view changed the model.\n",
"This is important because the widget machinery needs to know which cell to route the message callbacks to.\n",
"\n",
"**Final JavaScript code below:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the DatePickerView\n",
" var DatePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" \n",
" // Create the date picker control.\n",
" this.$date = $('<input />')\n",
" .attr('type', 'date')\n",
" .appendTo(this.$el);\n",
" },\n",
" \n",
" update: function() {\n",
" \n",
" // Set the value of the date control and then call base.\n",
" this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
" return DatePickerView.__super__.update.apply(this);\n",
" },\n",
" \n",
" // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
" events: {\"change\": \"handle_date_change\"},\n",
" \n",
" // Callback for when the date is changed.\n",
" handle_date_change: function(event) {\n",
" this.model.set('value', this.$date.val());\n",
" this.touch();\n",
" },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To test, create the widget the same way that the other widgets are created."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget = DateWidget()\n",
"display(my_widget)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Display the widget again to make sure that both views remain in sync."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Read the date from Python"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget.value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Set the date from Python"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget.value = \"1998-12-01\" # December 1st, 1998"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Section 3 - Extra credit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The 3rd party `dateutil` library is required to continue. https://pypi.python.org/pypi/python-dateutil"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Import the dateutil library to parse date strings.\n",
"from dateutil import parser"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the last section we created a fully working date picker widget.\n",
"Now we will add custom validation and support for labels.\n",
"So far, only the ISO date format \"YYYY-MM-DD\" is supported.\n",
"Now, we will add support for all of the date formats recognized by the Python dateutil library."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The standard property name used for widget labels is `description`.\n",
"In the code block below, `description` has been added to the Python widget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)\n",
" value = Unicode(sync=True)\n",
" description = Unicode(sync=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The traitlet machinery searches the class that the trait is defined in for methods with \"`_changed`\" suffixed onto their names. Any method with the format \"`_X_changed`\" will be called when \"`X`\" is modified.\n",
"We can take advantage of this to perform validation and parsing of different date string formats.\n",
"Below, a method that listens to value has been added to the DateWidget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)\n",
" value = Unicode(sync=True)\n",
" description = Unicode(sync=True)\n",
"\n",
" # This function automatically gets called by the traitlet machinery when\n",
" # value is modified because of this function's name.\n",
" def _value_changed(self, name, old_value, new_value):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now the function parses the date string,\n",
"and only sets the value in the correct format."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)\n",
" value = Unicode(sync=True)\n",
" description = Unicode(sync=True)\n",
" \n",
" # This function automatically gets called by the traitlet machinery when\n",
" # value is modified because of this function's name.\n",
" def _value_changed(self, name, old_value, new_value):\n",
" \n",
" # Parse the date time value.\n",
" try:\n",
" parsed_date = parser.parse(new_value)\n",
" parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
" except:\n",
" parsed_date_string = ''\n",
" \n",
" # Set the parsed date string if the current date string is different.\n",
" if self.value != parsed_date_string:\n",
" self.value = parsed_date_string"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, a `CallbackDispatcher` is added so the user can perform custom validation.\n",
"If any one of the callbacks registered with the dispatcher returns False,\n",
"the new date is not set.\n",
"\n",
"**Final Python code below:**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class DateWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('DatePickerView', sync=True)\n",
" value = Unicode(sync=True)\n",
" description = Unicode(sync=True)\n",
" \n",
" def __init__(self, **kwargs):\n",
" super(DateWidget, self).__init__(**kwargs)\n",
" \n",
" self.validate = widgets.CallbackDispatcher()\n",
" \n",
" # This function automatically gets called by the traitlet machinery when\n",
" # value is modified because of this function's name.\n",
" def _value_changed(self, name, old_value, new_value):\n",
" \n",
" # Parse the date time value.\n",
" try:\n",
" parsed_date = parser.parse(new_value)\n",
" parsed_date_string = parsed_date.strftime(\"%Y-%m-%d\")\n",
" except:\n",
" parsed_date_string = ''\n",
" \n",
" # Set the parsed date string if the current date string is different.\n",
" if old_value != new_value:\n",
" valid = self.validate(parsed_date)\n",
" if valid in (None, True):\n",
" self.value = parsed_date_string\n",
" else:\n",
" self.value = old_value\n",
" self.send_state() # The traitlet event won't fire since the value isn't changing.\n",
" # We need to force the back-end to send the front-end the state\n",
" # to make sure that the date control date doesn't change."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## JavaScript"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using the Javascript code from the last section,\n",
"we add a label to the date time object.\n",
"The label is a div with the `widget-hlabel` class applied to it.\n",
"`widget-hlabel` is a class provided by the widget framework that applies special styling to a div to make it look like the rest of the horizontal labels used with the built-in widgets.\n",
"Similar to the `widget-hlabel` class is the `widget-hbox-single` class.\n",
"The `widget-hbox-single` class applies special styling to widget containers that store a single line horizontal widget.\n",
"\n",
"We hide the label if the description value is blank."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
" \n",
" // Define the DatePickerView\n",
" var DatePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" this.$el.addClass('widget-hbox-single'); /* Apply this class to the widget container to make\n",
" it fit with the other built in widgets.*/\n",
" // Create a label.\n",
" this.$label = $('<div />')\n",
" .addClass('widget-hlabel')\n",
" .appendTo(this.$el)\n",
" .hide(); // Hide the label by default.\n",
" \n",
" // Create the date picker control.\n",
" this.$date = $('<input />')\n",
" .attr('type', 'date')\n",
" .appendTo(this.$el);\n",
" },\n",
" \n",
" update: function() {\n",
" \n",
" // Set the value of the date control and then call base.\n",
" this.$date.val(this.model.get('value')); // ISO format \"YYYY-MM-DDTHH:mm:ss.sssZ\" is required\n",
" \n",
" // Hide or show the label depending on the existance of a description.\n",
" var description = this.model.get('description');\n",
" if (description == undefined || description == '') {\n",
" this.$label.hide();\n",
" } else {\n",
" this.$label.show();\n",
" this.$label.text(description);\n",
" }\n",
" \n",
" return DatePickerView.__super__.update.apply(this);\n",
" },\n",
" \n",
" // Tell Backbone to listen to the change event of input controls (which the HTML date picker is)\n",
" events: {\"change\": \"handle_date_change\"},\n",
" \n",
" // Callback for when the date is changed.\n",
" handle_date_change: function(event) {\n",
" this.model.set('value', this.$date.val());\n",
" this.touch();\n",
" },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('DatePickerView', DatePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To test the drawing of the label we create the widget like normal but supply the additional description property a value."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Add some additional widgets for aesthetic purpose\n",
"display(widgets.Text(description=\"First:\"))\n",
"display(widgets.Text(description=\"Last:\"))\n",
"\n",
"my_widget = DateWidget()\n",
"display(my_widget)\n",
"my_widget.description=\"DOB:\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget = DateWidget()\n",
"display(my_widget)\n",
"\n",
"def require_2014(date):\n",
" return not date is None and date.year == 2014\n",
"my_widget.validate.register_callback(require_2014)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Try setting a valid date\n",
"my_widget.value = \"December 2, 2014\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Try setting an invalid date\n",
"my_widget.value = \"June 12, 1999\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"my_widget.value"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"cell_tags": [
[
"<None>",
null
]
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,117 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Explore Random Graphs Using NetworkX"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, we build a simple UI for exploring random graphs with [NetworkX](http://networkx.github.io/)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interact"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import networkx as nx"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# wrap a few graph generation functions so they have the same signature\n",
"\n",
"def random_lobster(n, m, k, p):\n",
" return nx.random_lobster(n, p, p / m)\n",
"\n",
"def powerlaw_cluster(n, m, k, p):\n",
" return nx.powerlaw_cluster_graph(n, m, p)\n",
"\n",
"def erdos_renyi(n, m, k, p):\n",
" return nx.erdos_renyi_graph(n, p)\n",
"\n",
"def newman_watts_strogatz(n, m, k, p):\n",
" return nx.newman_watts_strogatz_graph(n, k, p)\n",
"\n",
"def plot_random_graph(n, m, k, p, generator):\n",
" g = generator(n, m, k, p)\n",
" nx.draw(g)\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(plot_random_graph, n=(2,30), m=(1,10), k=(1,10), p=(0.0, 1.0, 0.001),\n",
" generator={'lobster': random_lobster,\n",
" 'power law': powerlaw_cluster,\n",
" 'Newman-Watts-Strogatz': newman_watts_strogatz,\n",
" u'Erdős-Rényi': erdos_renyi,\n",
" });"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

File diff suppressed because it is too large Load Diff

@ -1,186 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Widget related imports\n",
"from jupyter_notebook import widgets\n",
"from IPython.display import display, clear_output, Javascript\n",
"from traitlets import Unicode\n",
"\n",
"# nbconvert related imports\n",
"from IPython.nbconvert import get_export_names, export_by_name\n",
"from IPython.nbconvert.writers import FilesWriter\n",
"from IPython.nbformat import read, NO_CONVERT\n",
"from IPython.nbconvert.utils.exceptions import ConversionException"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook shows a really roundabout way to get the name of the notebook file using widgets. The true purpose of this demo is to demonstrate how Javascript and Python widget models are related by `id`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create a text Widget without displaying it. The widget will be used to store the notebook's name which is otherwise only available in the front-end."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"notebook_name = widgets.Text()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Get the current notebook's name by pushing JavaScript to the browser that sets the notebook name in a string widget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"js = \"\"\"IPython.notebook.kernel.widget_manager.get_model('%s').then(function(model) {\n",
" model.set('value', IPython.notebook.notebook_name);\n",
" model.save();\n",
"});\n",
"\"\"\" % notebook_name.model_id\n",
"display(Javascript(data=js))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"filename = notebook_name.value\n",
"filename"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the widget that will allow the user to Export the current notebook."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"exporter_names = widgets.Dropdown(options=get_export_names(), value='html')\n",
"export_button = widgets.Button(description=\"Export\")\n",
"download_link = widgets.HTML(visible=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Export the notebook when the export button is clicked."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"file_writer = FilesWriter()\n",
"\n",
"def export(name, nb):\n",
" \n",
" # Get a unique key for the notebook and set it in the resources object.\n",
" notebook_name = name[:name.rfind('.')]\n",
" resources = {}\n",
" resources['unique_key'] = notebook_name\n",
" resources['output_files_dir'] = '%s_files' % notebook_name\n",
"\n",
" # Try to export\n",
" try:\n",
" output, resources = export_by_name(exporter_names.value, nb)\n",
" except ConversionException as e:\n",
" download_link.value = \"<br>Could not export notebook!\"\n",
" else:\n",
" write_results = file_writer.write(output, resources, notebook_name=notebook_name)\n",
" \n",
" download_link.value = \"<br>Results: <a href='files/{filename}'><i>\\\"{filename}\\\"</i></a>\".format(filename=write_results)\n",
" download_link.visible = True\n",
" \n",
"def handle_export(widget):\n",
" with open(filename, 'r') as f:\n",
" export(filename, read(f, NO_CONVERT))\n",
" \n",
"export_button.on_click(handle_export)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Display the controls."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(exporter_names, export_button, download_link)"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,115 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Factoring Polynomials with SymPy"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is an example that uses [SymPy](http://sympy.org/en/index.html) to factor polynomials."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interact\n",
"from IPython.display import display"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from sympy import Symbol, Eq, factor, init_printing\n",
"init_printing(use_latex='mathjax')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"x = Symbol('x')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def factorit(n):\n",
" display(Eq(x**n-1, factor(x**n-1)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice how the output of the `factorit` function is properly formatted LaTeX."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"factorit(12)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(factorit, n=(2,40));"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,187 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import base64\n",
"from __future__ import print_function # py 2.7 compat.\n",
"from jupyter_notebook import widgets # Widget definitions.\n",
"from traitlets import Unicode # Traitlet needed to add synced attributes to the widget."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is a custom widget that allows the user to upload file data to the notebook server. The file data is sent via a statefull `value` attribute of the widget. The widget has an upload failed event that fires in the front-end and is echoed to the back-end using a custom msg."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class FileWidget(widgets.DOMWidget):\n",
" _view_name = Unicode('FilePickerView', sync=True)\n",
" value = Unicode(sync=True)\n",
" filename = Unicode(sync=True)\n",
" \n",
" def __init__(self, **kwargs):\n",
" \"\"\"Constructor\"\"\"\n",
" widgets.DOMWidget.__init__(self, **kwargs) # Call the base.\n",
" \n",
" # Allow the user to register error callbacks with the following signatures:\n",
" # callback()\n",
" # callback(sender)\n",
" self.errors = widgets.CallbackDispatcher(accepted_nargs=[0, 1])\n",
" \n",
" # Listen for custom msgs\n",
" self.on_msg(self._handle_custom_msg)\n",
"\n",
" def _handle_custom_msg(self, content):\n",
" \"\"\"Handle a msg from the front-end.\n",
"\n",
" Parameters\n",
" ----------\n",
" content: dict\n",
" Content of the msg.\"\"\"\n",
" if 'event' in content and content['event'] == 'error':\n",
" self.errors()\n",
" self.errors(self)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"\n",
"require([\"widgets/js/widget\", \"widgets/js/manager\"], function(widget, manager){\n",
"\n",
" var FilePickerView = widget.DOMWidgetView.extend({\n",
" render: function(){\n",
" // Render the view.\n",
" this.setElement($('<input />')\n",
" .attr('type', 'file'));\n",
" },\n",
" \n",
" events: {\n",
" // List of events and their handlers.\n",
" 'change': 'handle_file_change',\n",
" },\n",
" \n",
" handle_file_change: function(evt) { \n",
" // Handle when the user has changed the file.\n",
" \n",
" // Retrieve the first (and only!) File from the FileList object\n",
" var file = evt.target.files[0];\n",
" if (file) {\n",
"\n",
" // Read the file's textual content and set value to those contents.\n",
" var that = this;\n",
" var file_reader = new FileReader();\n",
" file_reader.onload = function(e) {\n",
" that.model.set('value', e.target.result);\n",
" that.touch();\n",
" }\n",
" file_reader.readAsText(file);\n",
" } else {\n",
"\n",
" // The file couldn't be opened. Send an error msg to the\n",
" // back-end.\n",
" this.send({ 'event': 'error' });\n",
" }\n",
"\n",
" // Set the filename of the file.\n",
" this.model.set('filename', file.name);\n",
" this.touch();\n",
" },\n",
" });\n",
" \n",
" // Register the DatePickerView with the widget manager.\n",
" manager.WidgetManager.register_widget_view('FilePickerView', FilePickerView);\n",
"});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following shows how the file widget can be used."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"file_widget = FileWidget()\n",
"\n",
"# Register an event to echo the filename when it has been changed.\n",
"def file_loading():\n",
" print(\"Loading %s\" % file_widget.filename)\n",
"file_widget.on_trait_change(file_loading, 'filename')\n",
"\n",
"# Register an event to echo the filename and contents when a file\n",
"# has been uploaded.\n",
"def file_loaded():\n",
" print(\"Loaded, file contents: %s\" % file_widget.value)\n",
"file_widget.on_trait_change(file_loaded, 'value')\n",
"\n",
"# Register an event to print an error message when a file could not\n",
"# be opened. Since the error messages are not handled through\n",
"# traitlets but instead handled through custom msgs, the registration\n",
"# of the handler is different than the two examples above. Instead\n",
"# the API provided by the CallbackDispatcher must be used.\n",
"def file_failed():\n",
" print(\"Could not load file contents of %s\" % file_widget.filename)\n",
"file_widget.errors.register_callback(file_failed)\n",
"\n",
"file_widget"
]
}
],
"metadata": {
"cell_tags": [
[
"<None>",
null
]
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,119 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Image Browser"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This example shows how to browse through a set of images with a slider."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interact"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from sklearn import datasets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We will use the digits dataset from [scikit-learn](http://scikit-learn.org/stable/)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"digits = datasets.load_digits()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def browse_images(digits):\n",
" n = len(digits.images)\n",
" def view_image(i):\n",
" plt.imshow(digits.images[i], cmap=plt.cm.gray_r, interpolation='nearest')\n",
" plt.title('Training: %s' % digits.target[i])\n",
" plt.show()\n",
" interact(view_image, i=(0,n-1))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"browse_images(digits)"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,162 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Image Manipulation with skimage"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This example builds a simple UI for performing basic image manipulation with [scikit-image](http://scikit-image.org/)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interact, interactive, fixed\n",
"from IPython.display import display"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import skimage\n",
"from skimage import data, filter, io"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"i = data.coffee()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"io.Image(i)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def edit_image(image, sigma=0.1, r=1.0, g=1.0, b=1.0):\n",
" new_image = filter.gaussian_filter(image, sigma=sigma, multichannel=True)\n",
" new_image[:,:,0] = r*new_image[:,:,0]\n",
" new_image[:,:,1] = g*new_image[:,:,1]\n",
" new_image[:,:,2] = b*new_image[:,:,2]\n",
" new_image = io.Image(new_image)\n",
" display(new_image)\n",
" return new_image"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"lims = (0.0,1.0,0.01)\n",
"w = interactive(edit_image, image=fixed(i), sigma=(0.0,10.0,0.1), r=lims, g=lims, b=lims)\n",
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Python 3 only: Function annotations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In Python 3, you can use the new function annotation syntax to describe widgets for interact:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"lims = (0.0,1.0,0.01)\n",
"\n",
"@interact\n",
"def edit_image(image: fixed(i), sigma:(0.0,10.0,0.1)=0.1, r:lims=1.0, g:lims=1.0, b:lims=1.0):\n",
" new_image = filter.gaussian_filter(image, sigma=sigma, multichannel=True)\n",
" new_image[:,:,0] = r*new_image[:,:,0]\n",
" new_image[:,:,1] = g*new_image[:,:,1]\n",
" new_image[:,:,2] = b*new_image[:,:,2]\n",
" new_image = io.Image(new_image)\n",
" display(new_image)\n",
" return new_image"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,108 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src=\"../images/ipython_logo.png\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Back to the main [Index](../Index.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Interactive Widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"IPython includes an architecture for interactive widgets that tie together Python code running in the kernel and JavaScript/HTML/CSS running in the browser. These widgets enable users to explore their code and data interactively."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tutorials"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- [Using Interact](Using Interact.ipynb)\n",
"- [Widget Basics](Widget Basics.ipynb) \n",
"- [Widget Events](Widget Events.ipynb) \n",
"- [Widget List](Widget List.ipynb) \n",
"- [Widget Styling](Widget Styling.ipynb) \n",
"- [Custom Widget](Custom Widget - Hello World.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Examples of custom widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- [Variable Inspector](Variable Inspector.ipynb) \n",
"- [Export As (nbconvert)](Export As (nbconvert%29.ipynb) \n",
"- [Nonblocking Console](Nonblocking Console.ipynb) \n",
"- [File Upload Widget](File Upload Widget.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Examples using `interact`/`interactive`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* [Beat Frequencies](Beat Frequencies.ipynb)\n",
"* [Exploring Graphs](Exploring Graphs.ipynb)\n",
"* [Factoring](Factoring.ipynb)\n",
"* [Image Browser](Image Browser.ipynb)\n",
"* [Image Processing](Image Processing.ipynb)\n",
"* [Lorenz Differential Equations](Lorenz Differential Equations.ipynb)"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,290 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exploring the Lorenz System of Differential Equations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this Notebook we explore the Lorenz system of differential equations:\n",
"\n",
"$$\n",
"\\begin{aligned}\n",
"\\dot{x} & = \\sigma(y-x) \\\\\n",
"\\dot{y} & = \\rho x - y - xz \\\\\n",
"\\dot{z} & = -\\beta z + xy\n",
"\\end{aligned}\n",
"$$\n",
"\n",
"This is one of the classic systems in non-linear differential equations. It exhibits a range of different behaviors as the parameters ($\\sigma$, $\\beta$, $\\rho$) are varied."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Imports"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, we import the needed things from IPython, NumPy, Matplotlib and SciPy."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import interact, interactive\n",
"from IPython.display import clear_output, display, HTML"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy import integrate\n",
"\n",
"from matplotlib import pyplot as plt\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"from matplotlib.colors import cnames\n",
"from matplotlib import animation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Computing the trajectories and plotting the result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We define a function that can integrate the differential equations numerically and then plot the solutions. This function has arguments that control the parameters of the differential equation ($\\sigma$, $\\beta$, $\\rho$), the numerical integration (`N`, `max_time`) and the visualization (`angle`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def solve_lorenz(N=10, angle=0.0, max_time=4.0, sigma=10.0, beta=8./3, rho=28.0):\n",
"\n",
" fig = plt.figure()\n",
" ax = fig.add_axes([0, 0, 1, 1], projection='3d')\n",
" ax.axis('off')\n",
"\n",
" # prepare the axes limits\n",
" ax.set_xlim((-25, 25))\n",
" ax.set_ylim((-35, 35))\n",
" ax.set_zlim((5, 55))\n",
" \n",
" def lorenz_deriv(x_y_z, t0, sigma=sigma, beta=beta, rho=rho):\n",
" \"\"\"Compute the time-derivative of a Lorenz system.\"\"\"\n",
" x, y, z = x_y_z\n",
" return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z]\n",
"\n",
" # Choose random starting points, uniformly distributed from -15 to 15\n",
" np.random.seed(1)\n",
" x0 = -15 + 30 * np.random.random((N, 3))\n",
"\n",
" # Solve for the trajectories\n",
" t = np.linspace(0, max_time, int(250*max_time))\n",
" x_t = np.asarray([integrate.odeint(lorenz_deriv, x0i, t)\n",
" for x0i in x0])\n",
" \n",
" # choose a different color for each trajectory\n",
" colors = plt.cm.jet(np.linspace(0, 1, N))\n",
"\n",
" for i in range(N):\n",
" x, y, z = x_t[i,:,:].T\n",
" lines = ax.plot(x, y, z, '-', c=colors[i])\n",
" plt.setp(lines, linewidth=2)\n",
"\n",
" ax.view_init(30, angle)\n",
" plt.show()\n",
"\n",
" return t, x_t"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's call the function once to view the solutions. For this set of parameters, we see the trajectories swirling around two points, called attractors. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"t, x_t = solve_lorenz(angle=0, N=10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using IPython's `interactive` function, we can explore how the trajectories behave as we change the various parameters."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = interactive(solve_lorenz, angle=(0.,360.), N=(0,50), sigma=(0.0,50.0), rho=(0.0,50.0))\n",
"display(w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The object returned by `interactive` is a `Widget` object and it has attributes that contain the current result and arguments:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"t, x_t = w.result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.kwargs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After interacting with the system, we can take the result and perform further computations. In this case, we compute the average positions in $x$, $y$ and $z$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"xyz_avg = x_t.mean(axis=1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"xyz_avg.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Creating histograms of the average positions (across different trajectories) show that on average the trajectories swirl about the attractors."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"plt.hist(xyz_avg[:,0])\n",
"plt.title('Average $x(t)$')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"plt.hist(xyz_avg[:,1])\n",
"plt.title('Average $y(t)$')"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,239 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Console related imports.\n",
"from subprocess import Popen, PIPE\n",
"import os\n",
"from IPython.utils.py3compat import bytes_to_str, string_types\n",
"\n",
"# Widget related imports.\n",
"from jupyter_notebook import widgets\n",
"from IPython.display import display"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Define function to run a process without blocking the input."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def read_process(process, append_output):\n",
" \"\"\" Try to read the stdout and stderr of a process and render it using \n",
" the append_output method provided\n",
" \n",
" Parameters\n",
" ----------\n",
" process: Popen handle\n",
" append_output: method handle\n",
" Callback to render output. Signature of\n",
" append_output(output, [prefix=])\"\"\"\n",
" \n",
" try:\n",
" stdout = process.stdout.read()\n",
" if stdout is not None and len(stdout) > 0:\n",
" append_output(stdout, prefix=' ')\n",
" except:\n",
" pass\n",
" \n",
" try:\n",
" stderr = process.stderr.read()\n",
" if stderr is not None and len(stderr) > 0:\n",
" append_output(stderr, prefix='ERR ')\n",
" except:\n",
" pass\n",
"\n",
"\n",
"def set_pipe_nonblocking(pipe):\n",
" \"\"\"Set a pipe as non-blocking\"\"\"\n",
" try:\n",
" import fcntl\n",
" fl = fcntl.fcntl(pipe, fcntl.F_GETFL)\n",
" fcntl.fcntl(pipe, fcntl.F_SETFL, fl | os.O_NONBLOCK)\n",
" except:\n",
" pass\n",
"\n",
"kernel = get_ipython().kernel\n",
"def run_command(command, append_output, has_user_exited=None):\n",
" \"\"\"Run a command asyncronously\n",
" \n",
" Parameters\n",
" ----------\n",
" command: str\n",
" Shell command to launch a process with.\n",
" append_output: method handle\n",
" Callback to render output. Signature of\n",
" append_output(output, [prefix=])\n",
" has_user_exited: method handle\n",
" Check to see if the user wants to stop the command.\n",
" Must return a boolean.\"\"\"\n",
" \n",
" # Echo input.\n",
" append_output(command, prefix='>>> ')\n",
" \n",
" # Create the process. Make sure the pipes are set as non-blocking.\n",
" process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)\n",
" set_pipe_nonblocking(process.stdout)\n",
" set_pipe_nonblocking(process.stderr)\n",
" \n",
" # Only continue to read from the command \n",
" while (has_user_exited is None or not has_user_exited()) and process.poll() is None:\n",
" read_process(process, append_output)\n",
" kernel.do_one_iteration() # Run IPython iteration. This is the code that\n",
" # makes this operation non-blocking. This will\n",
" # allow widget messages and callbacks to be \n",
" # processed.\n",
" \n",
" # If the process is still running, the user must have exited.\n",
" if process.poll() is None:\n",
" process.kill()\n",
" else:\n",
" read_process(process, append_output) # Read remainer\n",
" \n",
" \n",
" \n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the console widgets without displaying them."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"console_container = widgets.VBox(visible=False)\n",
"console_container.padding = '10px'\n",
"\n",
"output_box = widgets.Textarea()\n",
"output_box.height = '400px'\n",
"output_box.font_family = 'monospace'\n",
"output_box.color = '#AAAAAA'\n",
"output_box.background_color = 'black'\n",
"output_box.width = '800px'\n",
"\n",
"input_box = widgets.Text()\n",
"input_box.font_family = 'monospace'\n",
"input_box.color = '#AAAAAA'\n",
"input_box.background_color = 'black'\n",
"input_box.width = '800px'\n",
"\n",
"console_container.children = [output_box, input_box]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Hook the process execution methods up to our console widgets."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"\n",
"def append_output(output, prefix):\n",
" if isinstance(output, string_types):\n",
" output_str = output\n",
" else:\n",
" output_str = bytes_to_str(output)\n",
" output_lines = output_str.split('\\n')\n",
" formatted_output = '\\n'.join([prefix + line for line in output_lines if len(line) > 0]) + '\\n'\n",
" output_box.value += formatted_output\n",
" output_box.scroll_to_bottom()\n",
" \n",
"def has_user_exited():\n",
" return not console_container.visible\n",
"\n",
"def handle_input(sender):\n",
" sender.disabled = True\n",
" try:\n",
" command = sender.value\n",
" sender.value = ''\n",
" run_command(command, append_output=append_output, has_user_exited=has_user_exited)\n",
" finally:\n",
" sender.disabled = False\n",
" \n",
"input_box.on_submit(handle_input)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Create the button that will be used to display and hide the console. Display both the console container and the new button used to toggle it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"toggle_button = widgets.Button(description=\"Start Console\")\n",
"def toggle_console(sender):\n",
" console_container.visible = not console_container.visible\n",
" if console_container.visible:\n",
" toggle_button.description=\"Stop Console\"\n",
" input_box.disabled = False\n",
" else:\n",
" toggle_button.description=\"Start Console\"\n",
"toggle_button.on_click(toggle_console)\n",
"\n",
"display(toggle_button)\n",
"display(console_container)"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,629 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using Interact"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `interact` function (`jupyter_notebook.widgets.interact`) automatically creates user interface (UI) controls for exploring code and data interactively. It is the easiest way to get started using IPython's widgets."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from __future__ import print_function\n",
"from jupyter_notebook.widgets import interact, interactive, fixed\n",
"from jupyter_notebook import widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"alert alert-success\">\n",
"As of IPython 3.0, the widgets in this notebook won't show up on http://nbviewer.ipython.org. To view the widgets and interact with them, you will need to download this notebook and run it with an IPython Notebook server.\n",
"\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic `interact`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At the most basic level, `interact` autogenerates UI controls for function arguments, and then calls the function with those arguments when you manipulate the controls interactively. To use `interact`, you need to define a function that you want to explore. Here is a function that prints its only argument `x`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def f(x):\n",
" print(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When you pass this function as the first argument to `interact` along with an integer keyword argument (`x=10`), a slider is generated and bound to the function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=10);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When you move the slider, the function is called and the current value of `x` is printed.\n",
"\n",
"If you pass `True` or `False`, `interact` will generate a checkbox:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=True);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you pass a string, `interact` will generate a text area."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x='Hi there!');"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`interact` can also be used as a decorator. This allows you to define a function and interact with it in a single shot. As this example shows, `interact` also works with functions that have multiple arguments."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"@interact(x=True, y=1.0)\n",
"def g(x, y):\n",
" print(x, y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fixing arguments using `fixed`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are times when you may want to explore a function using `interact`, but fix one or more of its arguments to specific values. This can be accomplished by wrapping values with the `fixed` function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def h(p, q):\n",
" print(p, q)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When we call `interact`, we pass `fixed(20)` for q to hold it fixed at a value of `20`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(h, p=5, q=fixed(20));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice that a slider is only produced for `p` as the value of `q` is fixed."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Widget abbreviations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When you pass an integer valued keyword argument (`x=10`) to `interact`, it generates an integer valued slider control with a range of $[-10,+3\\times10]$. In this case `10` is an *abbreviation* for an actual slider widget:\n",
"\n",
"```python\n",
"IntSlider(min=-10,max=30,step=1,value=10)\n",
"```\n",
"\n",
"In fact, we can get the same result if we pass this `IntSlider` as the keyword argument for `x`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=widgets.IntSlider(min=-10,max=30,step=1,value=10));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This examples clarifies how `interact` proceses its keyword arguments:\n",
"\n",
"1. If the keyword argument is `Widget` instance with a `value` attribute, that widget is used. Any widget with a `value` attribute can be used, even custom ones.\n",
"2. Otherwise, the value is treated as a *widget abbreviation* that is converted to a widget before it is used.\n",
"\n",
"The following table gives an overview of different widget abbreviations:\n",
"\n",
"<table class=\"table table-condensed table-bordered\">\n",
" <tr><td><strong>Keyword argument</strong></td><td><strong>Widget</strong></td></tr> \n",
" <tr><td>`True` or `False`</td><td>Checkbox</td></tr> \n",
" <tr><td>`'Hi there'`</td><td>Text</td></tr>\n",
" <tr><td>`value` or `(min,max)` or `(min,max,step)` if integers are passed</td><td>IntSlider</td></tr>\n",
" <tr><td>`value` or `(min,max)` or `(min,max,step)` if floats are passed</td><td>FloatSlider</td></tr>\n",
" <tr><td>`('orange','apple')` or `{'one':1,'two':2}`</td><td>Dropdown</td></tr>\n",
"</table>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You have seen how the checkbox and textarea widgets work above. Here, more details about the different abbreviations for sliders and dropdowns are given.\n",
"\n",
"If a 2-tuple of integers is passed `(min,max)` a integer valued slider is produced with those minimum and maximum (inclusive) values. In this case, the default step size of `1` is used."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=(0,4));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If a 3-tuple of integers is passed `(min,max,step)` the step size can also be set."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=(0,8,2));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A float valued slider is produced if the elements of the tuples are floats. Here the minimum is `0.0`, the maximum is `10.0` and step size is `0.1` (the default)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=(0.0,10.0));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The step size can be changed by passing a 3rd element in the tuple."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=(0.0,10.0,0.01));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For both integer and float valued sliders, you can pick the initial value of the widget by passing a default keyword argument to the underlying Python function. Here we set the initial value of a float slider to `5.5`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"@interact(x=(0.0,20.0,0.5))\n",
"def h(x=5.5):\n",
" print(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dropdown menus can be produced by passing a tuple of strings. In this case, the strings are both used as the names in the dropdown menu UI and passed to the underlying Python function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x=('apples','oranges'));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want a dropdown menu that passes non-string values to the Python function, you can pass a dictionary. The keys in the dictionary are used for the names in the dropdown menu UI and the values are the arguments that are passed to the underlying Python function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f, x={'one': 10, 'two': 20});"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using function annotations with `interact`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you are using Python 3, you can also specify widget abbreviations using [function annotations](https://docs.python.org/3/tutorial/controlflow.html#function-annotations). This is a convenient approach allows the widget abbreviations to be defined with a function.\n",
"\n",
"Define a function with an checkbox widget abbreviation for the argument `x`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def f(x:True):\n",
" print(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then, because the widget abbreviation has already been defined, you can call `interact` with a single argument."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you are running Python 2, function annotations can be defined using the `@annotate` function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.utils.py3compat import annotate"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"@annotate(x=True)\n",
"def f(x):\n",
" print(x)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"interact(f);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `interactive`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to `interact` IPython provides another function, `interactive`, that is useful when you want to reuse the widget that are produced or access the data that is bound to the UI controls."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is a function that returns the sum of its two arguments."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def f(a, b):\n",
" return a+b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unlike `interact`, `interactive` returns a `Widget` instance rather than immediately displaying the widget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = interactive(f, a=10, b=20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The widget is a `Box`, which is a container for other widgets."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"type(w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The children of the `Box` are two integer valued sliders produced by the widget abbreviations above."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.children"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To actually display the widgets, you can use IPython's `display` function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"display(w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At this point, the UI controls work just like they would if `interact` had been used. You can manipulate them interactively and the function will be called. However, the widget instance returned by `interactive` also give you access to the current keyword arguments and return value of the underlying Python function.\n",
"\n",
"Here are the current keyword arguments. If you rerun this cell after manipulating the sliders, the values will have changed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.kwargs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is the current return value of the function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.result"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,240 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Variable Inspector Widget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A short example implementation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook demonstrates how one can use the widgets already built-in to IPython to create a working variable inspector much like the ones seen in popular commercial scientific computing environments."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook import widgets # Loads the Widget framework.\n",
"from IPython.core.magics.namespace import NamespaceMagics # Used to query namespace.\n",
"\n",
"# For this example, hide these names, just to avoid polluting the namespace further\n",
"get_ipython().user_ns_hidden['widgets'] = widgets\n",
"get_ipython().user_ns_hidden['NamespaceMagics'] = NamespaceMagics"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class VariableInspectorWindow(object):\n",
" instance = None\n",
" \n",
" def __init__(self, ipython):\n",
" \"\"\"Public constructor.\"\"\"\n",
" if VariableInspectorWindow.instance is not None:\n",
" raise Exception(\"\"\"Only one instance of the Variable Inspector can exist at a \n",
" time. Call close() on the active instance before creating a new instance.\n",
" If you have lost the handle to the active instance, you can re-obtain it\n",
" via `VariableInspectorWindow.instance`.\"\"\")\n",
" \n",
" VariableInspectorWindow.instance = self\n",
" self.closed = False\n",
" self.namespace = NamespaceMagics()\n",
" self.namespace.shell = ipython.kernel.shell\n",
" \n",
" self._box = widgets.Box()\n",
" self._box._dom_classes = ['inspector']\n",
" self._box.background_color = '#fff'\n",
" self._box.border_color = '#ccc'\n",
" self._box.border_width = 1\n",
" self._box.border_radius = 5\n",
"\n",
" self._modal_body = widgets.VBox()\n",
" self._modal_body.overflow_y = 'scroll'\n",
"\n",
" self._modal_body_label = widgets.HTML(value = 'Not hooked')\n",
" self._modal_body.children = [self._modal_body_label]\n",
"\n",
" self._box.children = [\n",
" self._modal_body, \n",
" ]\n",
" \n",
" self._ipython = ipython\n",
" self._ipython.events.register('post_run_cell', self._fill)\n",
" \n",
" def close(self):\n",
" \"\"\"Close and remove hooks.\"\"\"\n",
" if not self.closed:\n",
" self._ipython.events.unregister('post_run_cell', self._fill)\n",
" self._box.close()\n",
" self.closed = True\n",
" VariableInspectorWindow.instance = None\n",
"\n",
" def _fill(self):\n",
" \"\"\"Fill self with variable information.\"\"\"\n",
" values = self.namespace.who_ls()\n",
" self._modal_body_label.value = '<table class=\"table table-bordered table-striped\"><tr><th>Name</th><th>Type</th><th>Value</th></tr><tr><td>' + \\\n",
" '</td></tr><tr><td>'.join(['{0}</td><td>{1}</td><td>{2}'.format(v, type(eval(v)).__name__, str(eval(v))) for v in values]) + \\\n",
" '</td></tr></table>'\n",
"\n",
" def _ipython_display_(self):\n",
" \"\"\"Called when display() or pyout is used to display the Variable \n",
" Inspector.\"\"\"\n",
" self._box._ipython_display_()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"inspector = VariableInspectorWindow(get_ipython())\n",
"inspector"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pop the inspector out of the widget area using Javascript. To close the inspector, click the close button on the widget area that it was spawned from."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%javascript\n",
"$('div.inspector')\n",
" .detach()\n",
" .prependTo($('body'))\n",
" .css({\n",
" 'z-index': 999, \n",
" position: 'fixed',\n",
" 'box-shadow': '5px 5px 12px -3px black',\n",
" opacity: 0.9\n",
" })\n",
" .draggable();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"a = 5"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"b = 3.0"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"c = a * b"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"d = \"String\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"del b"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"inspector.close()"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,445 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Next](Widget List.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Simple Widget Introduction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What are widgets?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Widgets are elements that exists in both the front-end and the back-end.\n",
"\n",
"![Kernel & front-end diagram](../images/FrontendKernel.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What can they be used for?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"You can use widgets to build **interactive GUIs** for your notebooks. \n",
"You can also use widgets to **synchronize stateful and stateless information** between Python and JavaScript."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using widgets "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"To use the widget framework, you need to **import `jupyter_notebook.widgets`**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook.widgets import *"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### repr"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Widgets have their own display `repr` which allows them to be displayed using IPython's display framework. Constructing and returning an `IntSlider` automatically displays the widget (as seen below). Widgets are **displayed inside the `widget area`**, which sits between the code cell and output. **You can hide all of the widgets** in the `widget area` by clicking the grey *x* in the margin."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"IntSlider()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### display()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also explicitly display the widget using `display(...)`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"w = IntSlider()\n",
"display(w)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Multiple display() calls"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you display the same widget twice, the displayed instances in the front-end **will remain in sync** with each other."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(w)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Why does displaying the same widget twice work?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Widgets are **represented in the back-end by a single object**. Each time a widget is displayed, **a new representation** of that same object is created in the front-end. These representations are called **views**.\n",
"\n",
"![Kernel & front-end diagram](images/WidgetModelView.png)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Closing widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can close a widget by calling its `close()` method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.close()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Widget properties"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"All of the IPython widgets **share a similar naming scheme**. To read the value of a widget, you can query its `value` property."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = IntSlider()\n",
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similarly, to set a widget's value, you can set its `value` property."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value = 100"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Keys"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to `value`, most widgets share `keys`, `description`, `disabled`, and `visible`. To see the entire list of synchronized, stateful properties, of any specific widget, you can **query the `keys` property**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.keys"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Shorthand for setting the initial values of widget properties"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"While creating a widget, you can set some or all of the initial values of that widget by **defining them as keyword arguments in the widget's constructor** (as seen below)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"Text(value='Hello World!', disabled=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Linking two similar widgets"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"If you need to display the same value two different ways, you'll have to use two different widgets. Instead of **attempting to manually synchronize the values** of the two widgets, you can use the `traitlet` `link` function **to link two properties together**. Below, the values of three widgets are linked together."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from traitlets import link\n",
"a = FloatText()\n",
"b = FloatSlider()\n",
"c = FloatProgress()\n",
"display(a,b,c)\n",
"\n",
"\n",
"mylink = link((a, 'value'), (b, 'value'), (c, 'value'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Unlinking widgets"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Unlinking the widgets is simple. All you have to do is call `.unlink` on the link object."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"mylink.unlink()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Next](Widget List.ipynb)"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,383 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Widget Events"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Special events"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from __future__ import print_function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `Button` is not used to represent a data type. Instead the button widget is used to **handle mouse clicks**. The **`on_click` method** of the `Button` can be used to register function to be called when the button is clicked. The doc string of the `on_click` can be seen below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook import widgets\n",
"print(widgets.Button.on_click.__doc__)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since button clicks are **stateless**, they are **transmitted from the front-end to the back-end using custom messages**. By using the `on_click` method, a button that prints a message when it has been clicked is shown below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"button = widgets.Button(description=\"Click Me!\")\n",
"display(button)\n",
"\n",
"def on_button_clicked(b):\n",
" print(\"Button clicked.\")\n",
"\n",
"button.on_click(on_button_clicked)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### on_sumbit"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The **`Text`** also has a special **`on_submit` event**. The `on_submit` event **fires when the user hits return**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"text = widgets.Text()\n",
"display(text)\n",
"\n",
"def handle_submit(sender):\n",
" print(text.value)\n",
"\n",
"text.on_submit(handle_submit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Traitlet events"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Widget properties are IPython traitlets** and **traitlets are eventful**. To handle changes, the **`on_trait_change` method** of the widget can be used to **register a callback**. The doc string for `on_trait_change` can be seen below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"print(widgets.Widget.on_trait_change.__doc__)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Signatures"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mentioned in the doc string, the callback registered can have **4 possible signatures**:\n",
"\n",
"- callback()\n",
"- callback(trait_name)\n",
"- callback(trait_name, new_value)\n",
"- callback(trait_name, old_value, new_value)\n",
"\n",
"Using this method, an example of how to output an `IntSlider`'s value as it is changed can be seen below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"int_range = widgets.IntSlider()\n",
"display(int_range)\n",
"\n",
"def on_value_change(name, value):\n",
" print(value)\n",
"\n",
"int_range.on_trait_change(on_value_change, 'value')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Linking Widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often, you may want to simply link widget attributes together. Synchronization of attributes can be done in a simpler way than by using bare traitlets events. \n",
"\n",
"The first method is to use the `link` and `directional_link` functions from the `traitlets` module. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Linking traitlets attributes from the server side"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import traitlets"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'The values of slider1, slider2 and slider3 are synchronized')\n",
"sliders1, slider2, slider3 = widgets.IntSlider(description='Slider 1'),\\\n",
" widgets.IntSlider(description='Slider 2'),\\\n",
" widgets.IntSlider(description='Slider 3')\n",
"l = traitlets.link((sliders1, 'value'), (slider2, 'value'), (slider3, 'value'))\n",
"display(caption, sliders1, slider2, slider3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'Changes in source values are reflected in target1 and target2')\n",
"source, target1, target2 = widgets.IntSlider(description='Source'),\\\n",
" widgets.IntSlider(description='Target 1'),\\\n",
" widgets.IntSlider(description='Target 2')\n",
"traitlets.dlink((source, 'value'), (target1, 'value'), (target2, 'value'))\n",
"display(caption, source, target1, target2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Function `traitlets.link` returns a `Link` object. The link can be broken by calling the `unlink` method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# l.unlink()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Linking widgets attributes from the client side"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When synchronizing traitlets attributes, you may experience a lag because of the latency dues to the rountrip to the server side. You can also directly link widgets attributes, either in a unidirectional or a bidirectional fashion using the link widgets. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'The values of range1, range2 and range3 are synchronized')\n",
"range1, range2, range3 = widgets.IntSlider(description='Range 1'),\\\n",
" widgets.IntSlider(description='Range 2'),\\\n",
" widgets.IntSlider(description='Range 3')\n",
"l = widgets.jslink((range1, 'value'), (range2, 'value'), (range3, 'value'))\n",
"display(caption, range1, range2, range3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"caption = widgets.Latex(value = 'Changes in source_range values are reflected in target_range1 and target_range2')\n",
"source_range, target_range1, target_range2 = widgets.IntSlider(description='Source range'),\\\n",
" widgets.IntSlider(description='Target range 1'),\\\n",
" widgets.IntSlider(description='Target range 2')\n",
"widgets.jsdlink((source_range, 'value'), (target_range1, 'value'), (target_range2, 'value'))\n",
"display(caption, source_range, target_range1, target_range2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Function `widgets.jslink` returns a `Link` widget. The link can be broken by calling the `unlink` method."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# l.unlink()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget List.ipynb) - [Next](Widget Styling.ipynb)"
]
}
],
"metadata": {
"cell_tags": [
[
"<None>",
null
]
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,621 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Widget List"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Complete list"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"For a complete list of the widgets available to you, you can list the classes in the widget namespace (as seen below). `Widget` and `DOMWidget`, not listed below, are base classes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook import widgets\n",
"[n for n in dir(widgets) if not n.endswith('Widget') and n[0] == n[0].upper() and not n[0] == '_']"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Numeric widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are 8 widgets distributed with IPython that are designed to display numeric values. Widgets exist for displaying integers and floats, both bounded and unbounded. The integer widgets share a similar naming scheme to their floating point counterparts. By replacing `Float` with `Int` in the widget name, you can find the Integer equivalent."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### FloatSlider"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.FloatSlider(\n",
" value=7.5,\n",
" min=5.0,\n",
" max=10.0,\n",
" step=0.1,\n",
" description='Test:',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sliders can also be **displayed vertically**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.FloatSlider(\n",
" value=7.5,\n",
" min=5.0,\n",
" max=10.0,\n",
" step=0.1,\n",
" description='Test',\n",
" orientation='vertical',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### FloatProgress"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.FloatProgress(\n",
" value=7.5,\n",
" min=5.0,\n",
" max=10.0,\n",
" step=0.1,\n",
" description='Loading:',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### BoundedFloatText"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.BoundedFloatText(\n",
" value=7.5,\n",
" min=5.0,\n",
" max=10.0,\n",
" description='Text:',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### FloatText"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.FloatText(\n",
" value=7.5,\n",
" description='Any:',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Boolean widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are two widgets that are designed to display a boolean value."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### ToggleButton"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.ToggleButton(\n",
" description='Click me',\n",
" value=False,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Checkbox"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Checkbox(\n",
" description='Check me',\n",
" value=True,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Selection widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are four widgets that can be used to display single selection lists, and one that can be used to display multiple selection lists. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list**. You can **also specify the enumeration as a dictionary**, in which case the **keys will be used as the item displayed** in the list and the corresponding **value will be returned** when an item is selected."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Dropdown"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"w = widgets.Dropdown(\n",
" options=['1', '2', '3'],\n",
" value='2',\n",
" description='Number:',\n",
")\n",
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following is also valid:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w = widgets.Dropdown(\n",
" options={'One': 1, 'Two': 2, 'Three': 3},\n",
" value=2,\n",
" description='Number:',\n",
")\n",
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### RadioButtons"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.RadioButtons(\n",
" description='Pizza topping:',\n",
" options=['pepperoni', 'pineapple', 'anchovies'],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Select"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Select(\n",
" description='OS:',\n",
" options=['Linux', 'Windows', 'OSX'],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### ToggleButtons"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.ToggleButtons(\n",
" description='Speed:',\n",
" options=['Slow', 'Regular', 'Fast'],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### SelectMultiple\n",
"Multiple values can be selected with <kbd>shift</kbd> and <kbd>ctrl</kbd> pressed and mouse clicks or arrow keys."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"w = widgets.SelectMultiple(\n",
" description=\"Fruits\",\n",
" options=['Apples', 'Oranges', 'Pears']\n",
")\n",
"display(w)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w.value"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## String widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are 4 widgets that can be used to display a string value. Of those, the **`Text` and `Textarea` widgets accept input**. The **`Latex` and `HTML` widgets display the string** as either Latex or HTML respectively, but **do not accept input**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Text"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Text(\n",
" description='String:',\n",
" value='Hello World',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Textarea"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Textarea(\n",
" description='String:',\n",
" value='Hello World',\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Latex"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Latex(\n",
" value=\"$$\\\\frac{n!}{k!(n-k)!} = \\\\binom{n}{k}$$\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### HTML"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.HTML(\n",
" value=\"Hello <b>World</b>\"\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Button"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"widgets.Button(description='Click me')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Basics.ipynb) - [Next](Widget Events.ipynb)"
]
}
],
"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.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,585 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%%html\n",
"<style>\n",
".example-container { background: #999999; padding: 2px; min-height: 100px; }\n",
".example-container.sm { min-height: 50px; }\n",
".example-box { background: #9999FF; width: 50px; height: 50px; text-align: center; vertical-align: middle; color: white; font-weight: bold; margin: 2px;}\n",
".example-box.med { width: 65px; height: 65px; } \n",
".example-box.lrg { width: 80px; height: 80px; } \n",
"</style>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from jupyter_notebook import widgets\n",
"from IPython.display import display"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Widget Styling"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic styling"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The widgets distributed with IPython can be styled by setting the following traits:\n",
"\n",
"- width \n",
"- height \n",
"- fore_color \n",
"- back_color \n",
"- border_color \n",
"- border_width \n",
"- border_style \n",
"- font_style \n",
"- font_weight \n",
"- font_size \n",
"- font_family \n",
"\n",
"The example below shows how a `Button` widget can be styled:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"button = widgets.Button(\n",
" description='Hello World!',\n",
" width=100, # Integers are interpreted as pixel measurements.\n",
" height='2em', # em is valid HTML unit of measurement.\n",
" color='lime', # Colors can be set by name,\n",
" background_color='#0022FF', # and also by color code.\n",
" border_color='red')\n",
"display(button)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Parent/child relationships"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To display widget A inside widget B, widget A must be a child of widget B. Widgets that can contain other widgets have a **`children` attribute**. This attribute can be **set via a keyword argument** in the widget's constructor **or after construction**. Calling display on an **object with children automatically displays those children**, too."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from IPython.display import display\n",
"\n",
"float_range = widgets.FloatSlider()\n",
"string = widgets.Text(value='hi')\n",
"container = widgets.Box(children=[float_range, string])\n",
"\n",
"container.border_color = 'red'\n",
"container.border_style = 'dotted'\n",
"container.border_width = 3\n",
"display(container) # Displays the `container` and all of it's children."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### After the parent is displayed"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Children **can be added to parents** after the parent has been displayed. The **parent is responsible for rendering its children**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"container = widgets.Box()\n",
"container.border_color = 'red'\n",
"container.border_style = 'dotted'\n",
"container.border_width = 3\n",
"display(container)\n",
"\n",
"int_range = widgets.IntSlider()\n",
"container.children=[int_range]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Fancy boxes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you need to display a more complicated set of widgets, there are **specialized containers** that you can use. To display **multiple sets of widgets**, you can use an **`Accordion` or a `Tab` in combination with one `Box` per set of widgets** (as seen below). The \"pages\" of these widgets are their children. To set the titles of the pages, one can **call `set_title`**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Accordion"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"name1 = widgets.Text(description='Location:')\n",
"zip1 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
"page1 = widgets.Box(children=[name1, zip1])\n",
"\n",
"name2 = widgets.Text(description='Location:')\n",
"zip2 = widgets.BoundedIntText(description='Zip:', min=0, max=99999)\n",
"page2 = widgets.Box(children=[name2, zip2])\n",
"\n",
"accord = widgets.Accordion(children=[page1, page2])\n",
"display(accord)\n",
"\n",
"accord.set_title(0, 'From')\n",
"accord.set_title(1, 'To')"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### TabWidget"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"name = widgets.Text(description='Name:')\n",
"color = widgets.Dropdown(description='Color:', options=['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])\n",
"page1 = widgets.Box(children=[name, color])\n",
"\n",
"age = widgets.IntSlider(description='Age:', min=0, max=120, value=50)\n",
"gender = widgets.RadioButtons(description='Gender:', options=['male', 'female'])\n",
"page2 = widgets.Box(children=[age, gender])\n",
"\n",
"tabs = widgets.Tab(children=[page1, page2])\n",
"display(tabs)\n",
"\n",
"tabs.set_title(0, 'Name')\n",
"tabs.set_title(1, 'Details')"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Alignment"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Most widgets have a **`description` attribute**, which allows a label for the widget to be defined.\n",
"The label of the widget **has a fixed minimum width**.\n",
"The text of the label is **always right aligned and the widget is left aligned**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(widgets.Text(description=\"a:\"))\n",
"display(widgets.Text(description=\"aa:\"))\n",
"display(widgets.Text(description=\"aaa:\"))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"If a **label is longer** than the minimum width, the **widget is shifted to the right**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(widgets.Text(description=\"a:\"))\n",
"display(widgets.Text(description=\"aa:\"))\n",
"display(widgets.Text(description=\"aaa:\"))\n",
"display(widgets.Text(description=\"aaaaaaaaaaaaaaaaaa:\"))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"If a `description` is **not set** for the widget, the **label is not displayed**:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"display(widgets.Text(description=\"a:\"))\n",
"display(widgets.Text(description=\"aa:\"))\n",
"display(widgets.Text(description=\"aaa:\"))\n",
"display(widgets.Text())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Flex boxes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Widgets can be aligned using the `FlexBox`, `HBox`, and `VBox` widgets."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Application to widgets"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Widgets display vertically by default:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"buttons = [widgets.Button(description=str(i)) for i in range(3)]\n",
"display(*buttons)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Using hbox"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To make widgets display horizontally, you need to **child them to a `HBox` widget**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"container = widgets.HBox(children=buttons)\n",
"display(container)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By setting the width of the container to 100% and its `pack` to `center`, you can center the buttons."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"container.width = '100%'\n",
"container.pack = 'center'"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Visibility"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sometimes it is necessary to **hide or show widgets** in place, **without having to re-display** the widget.\n",
"The `visible` property of widgets can be used to hide or show **widgets that have already been displayed** (as seen below). The `visible` property can be:\n",
"* `True` - the widget is displayed\n",
"* `False` - the widget is hidden, and the empty space where the widget would be is collapsed\n",
"* `None` - the widget is hidden, and the empty space where the widget would be is shown"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w1 = widgets.Latex(value=\"First line\")\n",
"w2 = widgets.Latex(value=\"Second line\")\n",
"w3 = widgets.Latex(value=\"Third line\")\n",
"display(w1, w2, w3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"w2.visible=None"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w2.visible=False"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"w2.visible=True"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Another example"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the example below, a form is rendered, which conditionally displays widgets depending on the state of other widgets. Try toggling the student checkbox."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"form = widgets.VBox()\n",
"first = widgets.Text(description=\"First Name:\")\n",
"last = widgets.Text(description=\"Last Name:\")\n",
"\n",
"student = widgets.Checkbox(description=\"Student:\", value=False)\n",
"school_info = widgets.VBox(visible=False, children=[\n",
" widgets.Text(description=\"School:\"),\n",
" widgets.IntText(description=\"Grade:\", min=0, max=12)\n",
" ])\n",
"\n",
"pet = widgets.Text(description=\"Pet's Name:\")\n",
"form.children = [first, last, student, school_info, pet]\n",
"display(form)\n",
"\n",
"def on_student_toggle(name, value):\n",
" if value:\n",
" school_info.visible = True\n",
" else:\n",
" school_info.visible = False\n",
"student.on_trait_change(on_student_toggle, 'value')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Index](Index.ipynb) - [Back](Widget Events.ipynb) - [Next](Custom Widget - Hello World.ipynb)"
]
}
],
"metadata": {
"cell_tags": [
[
"<None>",
null
]
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

@ -1,442 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle</string>
<string>139.18.0.187838</string>
</array>
<key>AutoAdjust</key>
<true/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {576, 733}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2014-05-28 16:41:42 +0000</string>
<key>Creator</key>
<string>bgranger</string>
<key>DisplayScale</key>
<string>1 0/72 in = 1 0/72 in</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>4</integer>
</dict>
<key>ID</key>
<integer>8</integer>
<key>Points</key>
<array>
<string>{301.5, 284.5}</string>
<string>{370.03931790895228, 313.41502474283925}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>3</integer>
</dict>
<key>ID</key>
<integer>7</integer>
<key>Points</key>
<array>
<string>{302, 282}</string>
<string>{370.00010962762133, 280.57591393450008}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>1</integer>
</dict>
<key>ID</key>
<integer>6</integer>
<key>Points</key>
<array>
<string>{301.5, 280.5}</string>
<string>{370.04817900607623, 248.01101932524512}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{241.5, 262}, {58, 36}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FontInfo</key>
<dict>
<key>Font</key>
<string>Helvetica</string>
<key>Size</key>
<real>12</real>
</dict>
<key>ID</key>
<integer>5</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs20 \cf0 Frontend}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{370.5, 307}, {54, 36}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FontInfo</key>
<dict>
<key>Font</key>
<string>Helvetica</string>
<key>Size</key>
<real>12</real>
</dict>
<key>ID</key>
<integer>4</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs20 \cf0 R\
Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{370.5, 262}, {54, 36}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FontInfo</key>
<dict>
<key>Font</key>
<string>Helvetica</string>
<key>Size</key>
<real>12</real>
</dict>
<key>ID</key>
<integer>3</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs20 \cf0 Julia\
Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{370.5, 217}, {54, 36}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FontInfo</key>
<dict>
<key>Font</key>
<string>Helvetica</string>
<key>Size</key>
<real>12</real>
</dict>
<key>ID</key>
<integer>1</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs20 \cf0 Python Kernel}</string>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict/>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2014-05-28 16:45:20 +0000</string>
<key>Modifier</key>
<string>bgranger</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>YES</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{612, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{387, 6}, {710, 872}}</string>
<key>ListView</key>
<true/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<false/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{196.5, 107}, {287.5, 366.5}}</string>
<key>Zoom</key>
<real>2</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>2</real>
<real>1</real>
</array>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@ -1,876 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle</string>
<string>139.18.0.187838</string>
</array>
<key>AutoAdjust</key>
<true/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {576, 733}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>FontInfo</key>
<dict>
<key>Font</key>
<string>xkcd-Regular</string>
<key>Size</key>
<real>11</real>
</dict>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2014-05-27 22:35:15 +0000</string>
<key>Creator</key>
<string>bgranger</string>
<key>DisplayScale</key>
<string>1 0/72 in = 1 0/72 in</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>ID</key>
<integer>24</integer>
<key>Points</key>
<array>
<string>{222.5, 377.5}</string>
<string>{262, 424.66666666666669}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>23</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>4</integer>
</dict>
<key>ID</key>
<integer>23</integer>
<key>Points</key>
<array>
<string>{222.5, 377.5}</string>
<string>{261.50992666237363, 385.39132104238854}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>3</integer>
</dict>
<key>ID</key>
<integer>22</integer>
<key>Points</key>
<array>
<string>{223.5, 376.5}</string>
<string>{261.51605222709946, 366.62761434412533}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>21</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>ID</key>
<integer>21</integer>
<key>Points</key>
<array>
<string>{223.5, 376.5}</string>
<string>{262, 323.33333333333331}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>18</integer>
</dict>
<key>ID</key>
<integer>20</integer>
<key>Points</key>
<array>
<string>{136, 376.24998788995731}</string>
<string>{167.5, 376.24998788995731}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>19</integer>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{66.5, 364.5}, {69, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>19</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 FRONTEND}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{168, 364.5}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>18</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{436, 410}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>17</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{436, 379}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>16</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{436, 348}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>15</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{436, 317}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>14</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{378, 410}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>13</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{378, 379}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>12</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{378, 348}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>11</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{378, 317}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>10</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{320, 410}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>9</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{320, 379}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>8</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{320, 348}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>7</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{320, 317}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>6</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{262, 410}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>5</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{262, 379}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>4</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{262, 348}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>3</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{262, 317}, {52, 23.5}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>1</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel}</string>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict/>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2014-05-27 22:41:37 +0000</string>
<key>Modifier</key>
<string>bgranger</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>YES</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{612, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{367, 6}, {710, 872}}</string>
<key>ListView</key>
<true/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<false/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{0, 0}, {575, 733}}</string>
<key>Zoom</key>
<real>1</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>1</real>
<real>1.5</real>
</array>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

@ -1,426 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle</string>
<string>139.18.0.187838</string>
</array>
<key>AutoAdjust</key>
<true/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {576, 733}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2013-11-07 05:58:18 +0000</string>
<key>Creator</key>
<string>bgranger</string>
<key>DisplayScale</key>
<string>1 0/72 in = 1.0000 in</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>28</integer>
</dict>
<key>ID</key>
<integer>33</integer>
<key>Points</key>
<array>
<string>{241.59308245327554, 385.40907928007584}</string>
<string>{270.40691754672446, 347.59092071992416}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>LineType</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>30</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>30</integer>
</dict>
<key>ID</key>
<integer>32</integer>
<key>Points</key>
<array>
<string>{313.49998123780057, 408.50001815456494}</string>
<string>{262.50001876219557, 408.50001815456494}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>LineType</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>29</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>29</integer>
</dict>
<key>ID</key>
<integer>31</integer>
<key>Points</key>
<array>
<string>{305.59308378474134, 347.59092246747298}</string>
<string>{334.40691621525866, 385.40907753252702}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>LineType</key>
<integer>1</integer>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>28</integer>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{186.5, 383.5}, {75, 50}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>30</integer>
<key>Shape</key>
<string>Circle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs22 \cf0 ComputE}</string>
<key>VerticalPad</key>
<integer>0</integer>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{314.5, 383.5}, {75, 50}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>29</integer>
<key>Shape</key>
<string>Circle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs22 \cf0 Interact}</string>
<key>VerticalPad</key>
<integer>0</integer>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{250.5, 299.5}, {75, 50}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>28</integer>
<key>Shape</key>
<string>Circle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Width</key>
<real>2</real>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs22 \cf0 Visualize}</string>
<key>VerticalPad</key>
<integer>0</integer>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict/>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2014-05-28 16:49:32 +0000</string>
<key>Modifier</key>
<string>bgranger</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>YES</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{612, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{340, 6}, {710, 872}}</string>
<key>ListView</key>
<true/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<false/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{0, 0}, {575, 733}}</string>
<key>Zoom</key>
<real>1</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>1</real>
<real>1</real>
</array>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

@ -1,322 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle</string>
<string>139.18.0.187838</string>
</array>
<key>AutoAdjust</key>
<true/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {576, 733}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2013-11-09 20:06:39 +0000</string>
<key>Creator</key>
<string>bgranger</string>
<key>DisplayScale</key>
<string>1 0/72 in = 1.0000 in</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>Bounds</key>
<string>{{212.5, 269.5}, {124.5, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>5</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Interact}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{212.5, 318}, {124.5, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>4</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Widgets}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{212.5, 366.5}, {124.5, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>3</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Comm}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{212.5, 415}, {124.5, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>1</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 WebSockets/ZeroMQ}</string>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict/>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2014-05-28 16:53:16 +0000</string>
<key>Modifier</key>
<string>bgranger</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>YES</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{612, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{367, 6}, {710, 872}}</string>
<key>ListView</key>
<true/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<false/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{143.5, 183}, {287.5, 366.5}}</string>
<key>Zoom</key>
<real>2</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>2</real>
<real>1</real>
</array>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@ -1,523 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle</string>
<string>139.18.0.187838</string>
</array>
<key>AutoAdjust</key>
<true/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {576, 733}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2014-07-06 03:46:05 +0000</string>
<key>Creator</key>
<string>bgranger</string>
<key>DisplayScale</key>
<string>1 0/72 in = 1 0/72 in</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>Bounds</key>
<string>{{230.33332316080816, 214.66666666666825}, {171, 15}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FitText</key>
<string>YES</string>
<key>Flow</key>
<string>Resize</string>
<key>ID</key>
<integer>21</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>fill</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Pad</key>
<integer>0</integer>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 frontend (HTML/JavaScript)}</string>
<key>VerticalPad</key>
<integer>0</integer>
</dict>
<key>Wrap</key>
<string>NO</string>
</dict>
<dict>
<key>Bounds</key>
<string>{{70.166664123535156, 214.66667683919241}, {95, 15}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>FitText</key>
<string>YES</string>
<key>Flow</key>
<string>Resize</string>
<key>ID</key>
<integer>20</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>fill</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Pad</key>
<integer>0</integer>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 Kernel (Python)}</string>
<key>VerticalPad</key>
<integer>0</integer>
</dict>
<key>Wrap</key>
<string>NO</string>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>8</integer>
</dict>
<key>ID</key>
<integer>18</integer>
<key>Points</key>
<array>
<string>{302.62321350991539, 355.71147093607129}</string>
<string>{329.06618954727776, 386.2881834750354}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>FilledArrow</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>6</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>7</integer>
</dict>
<key>ID</key>
<integer>16</integer>
<key>Points</key>
<array>
<string>{302.60973386333222, 314.95496159151998}</string>
<string>{329.03248543221167, 284.3780603888232}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>FilledArrow</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>6</integer>
</dict>
</dict>
<dict>
<key>Class</key>
<string>LineGraphic</string>
<key>Head</key>
<dict>
<key>ID</key>
<integer>6</integer>
</dict>
<key>ID</key>
<integer>15</integer>
<key>Points</key>
<array>
<string>{143.33332567510072, 335.32575675071013}</string>
<string>{229.83332831581788, 335.30805933679687}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>HeadArrow</key>
<string>FilledArrow</string>
<key>Legacy</key>
<true/>
<key>Pattern</key>
<integer>1</integer>
<key>TailArrow</key>
<string>FilledArrow</string>
</dict>
</dict>
<key>Tail</key>
<dict>
<key>ID</key>
<integer>5</integer>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{291.99996948242188, 386.66658655802428}, {109.33333587646484, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>8</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 widget View}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{291.99998982747394, 243.99996948241886}, {109.33333587646484, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>7</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 widget View}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{230.33332824706363, 315.33327865600415}, {109.33333587646484, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>6</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 widget model}</string>
</dict>
</dict>
<dict>
<key>Bounds</key>
<string>{{70.166661580401851, 315.33329264322913}, {72.666664123535156, 40}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
<key>ID</key>
<integer>5</integer>
<key>Shape</key>
<string>Rectangle</string>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
<key>Text</key>
<dict>
<key>Text</key>
<string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 xkcd-Regular;}
{\colortbl;\red255\green255\blue255;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
\f0\fs24 \cf0 widget}</string>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict/>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2014-07-06 03:57:02 +0000</string>
<key>Modifier</key>
<string>bgranger</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>YES</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{612, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{17, 3}, {1112, 875}}</string>
<key>ListView</key>
<false/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<true/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{0, 105.33333333333333}, {556, 490.66666666666669}}</string>
<key>Zoom</key>
<real>1.5</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>1.5</real>
<real>1</real>
</array>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

@ -1 +0,0 @@
from .widgets import *

@ -1,21 +0,0 @@
#!/usr/bin/env python
# Thanks @takluyver for your cite2c install.py.
from os.path import dirname, abspath, join as pjoin
from jupyter_notebook.nbextensions import install_nbextension
from jupyter_notebook.services.config import ConfigManager
import argparse
parser = argparse.ArgumentParser(description="Installs the IPython widgets")
parser.add_argument("-u", "--user", help="Install as current user", action="store_true")
parser.add_argument("-s", "--symlink", help="Symlink instead of copying files", action="store_true")
args = parser.parse_args()
print("Installing nbextension ...")
staticdir = pjoin(dirname(abspath(__file__)), 'static')
install_nbextension(staticdir, destination='widgets', user=args.user, symlink=args.symlink)
print("Enabling the extension ...")
cm = ConfigManager()
cm.update('notebook', {"load_extensions": {"widgets/notebook/js/extension": True}})
print("Done.")

@ -1,66 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'nbextensions/widgets/widgets/js/init',
'nbextensions/widgets/notebook/js/widgetarea',
'base/js/events'
], function(widgetmanager, widgetarea, events) {
"use strict";
/**
* Create a widget manager for a kernel instance.
*/
var handle_kernel = function(kernel) {
if (kernel.comm_manager && kernel.widget_manager === undefined) {
// Create a widget manager instance. Use the global
// IPython.notebook handle.
var manager = new widgetmanager.WidgetManager(kernel.comm_manager, IPython.notebook);
// Store a handle to the manager so we know not to
// another for this kernel. This also is a convinience
// for the user.
kernel.widget_manager = manager;
}
};
// If a kernel already exists, create a widget manager.
if (IPython.notebook && IPython.notebook.kernel) {
handle_kernel(IPython.notebook.kernel);
}
// When the kernel is created, create a widget manager.
events.on('kernel_created.Kernel kernel_created.Session', function(event, data) {
handle_kernel(data.kernel);
});
/**
* Creates a widgetarea for the cell if it is a CodeCell.
* If the cell isn't a CodeCell, no action is taken.
*/
var handle_cell = function(cell) {
if (cell.cell_type==='code') {
var area = new widgetarea.WidgetArea(cell);
cell.widgetarea = area;
}
};
// Create widget areas for cells that already exist.
var cells = IPython.notebook.get_cells();
for (var i = 0; i < cells.length; i++) {
handle_cell(cells[i]);
}
// Listen to cell creation and deletion events. When a
// cell is created, create a widget area for that cell.
events.on('create.Cell', function(event, data) {
handle_cell(data.cell);
});
// When a cell is deleted, delete the widget area if it
// exists.
events.on('delete.Cell', function(event, data) {
if (data.cell && data.cell.widgetarea) {
data.cell.widgetarea.dispose();
}
});
});

@ -1,163 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(['jquery', 'base/js/events'], function($, events) {
/**
* WidgetArea
*/
var WidgetArea = function(cell) {
this.widget_views = [];
this._cell = cell;
this._widgets_live = true;
this.disposed = false;
this._create_elements();
this._bind_events();
}
/**
* Display a widget view in the cell.
*/
WidgetArea.prototype.display_widget_view = function(view_promise) {
// Display a dummy element
var dummy = $('<div/>');
this.widget_subarea.append(dummy);
// Display the view.
var that = this;
return view_promise.then(function(view) {
that.widget_area.show();
dummy.replaceWith(view.$el);
that.widget_views.push(view);
// Check the live state of the view's model.
if (view.model.comm_live) {
that._widget_live(view);
} else {
that._widget_dead(view);
}
// Listen to comm live events for the view.
view.on('comm:live', that._widget_live, that);
view.on('comm:dead', that._widget_dead, that);
return view;
});
};
/**
* Disposes of the widget area and its widgets.
*/
WidgetArea.prototype.dispose = function() {
this._clear();
this.disposed = true;
};
/**
* Creates the elements of the widget area and appends them
* to the associated cell.
*/
WidgetArea.prototype._create_elements = function() {
var widget_area = $('<div/>')
.addClass('widget-area')
.hide();
this.widget_area = widget_area;
var widget_prompt = $('<div/>')
.addClass('prompt')
.appendTo(widget_area);
var widget_subarea = $('<div/>')
.addClass('widget-subarea')
.appendTo(widget_area);
this.widget_subarea = widget_subarea;
var that = this;
var widget_clear_buton = $('<button />')
.addClass('close')
.html('&times;')
.click(function() {
widget_area.slideUp('', function(){
for (var i = 0; i < that.widget_views.length; i++) {
var view = that.widget_views[i];
view.remove();
// Remove widget live events.
view.off('comm:live', that._widget_live);
view.off('comm:dead', that._widget_dead);
}
that.widget_views = [];
widget_subarea.html('');
});
})
.appendTo(widget_prompt);
if (this._cell.input) {
this._cell.input.after(widget_area);
} else {
throw new Error('Cell does not have an `input` element. Is it not a CodeCell?');
}
}
/**
* Listens to events of the cell.
*/
WidgetArea.prototype._bind_events = function() {
var that = this;
events.on('execute.CodeCell', function(event, data) {
if (data.cell===that._cell) {
that._clear();
}
});
};
/**
* Handles when a widget loses it's comm connection.
* @param {WidgetView} view
*/
WidgetArea.prototype._widget_dead = function(view) {
if (this._widgets_live) {
this._widgets_live = false;
this.widget_area.addClass('connection-problems');
}
};
/**
* Handles when a widget is connected to a live comm.
* @param {WidgetView} view
*/
WidgetArea.prototype._widget_live = function(view) {
if (!this._widgets_live) {
// Check that the other widgets are live too. O(N) operation.
// Abort the function at the first dead widget found.
for (var i = 0; i < this.widget_views.length; i++) {
if (!this.widget_views[i].model.comm_live) return;
}
this._widgets_live = true;
this.widget_area.removeClass('connection-problems');
}
};
/**
* Clears the widgets in the widget area.
*/
WidgetArea.prototype._clear = function() {
// Clear widget area
for (var i = 0; i < this.widget_views.length; i++) {
var view = this.widget_views[i];
view.remove();
// Remove widget live events.
view.off('comm:live', this._widget_live);
view.off('comm:dead', this._widget_dead);
}
this.widget_views = [];
this.widget_subarea.html('');
this.widget_subarea.height('');
this.widget_area.height('');
this.widget_area.hide();
};
return {WidgetArea: WidgetArea};
});

@ -1,34 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/manager",
"nbextensions/widgets/widgets/js/widget",
"nbextensions/widgets/widgets/js/widget_link",
"nbextensions/widgets/widgets/js/widget_bool",
"nbextensions/widgets/widgets/js/widget_button",
"nbextensions/widgets/widgets/js/widget_box",
"nbextensions/widgets/widgets/js/widget_float",
"nbextensions/widgets/widgets/js/widget_image",
"nbextensions/widgets/widgets/js/widget_int",
"nbextensions/widgets/widgets/js/widget_output",
"nbextensions/widgets/widgets/js/widget_selection",
"nbextensions/widgets/widgets/js/widget_selectioncontainer",
"nbextensions/widgets/widgets/js/widget_string",
], function(widgetmanager, widget) {
// Register all of the loaded models and views with the widget manager.
for (var i = 2; i < arguments.length; i++) {
var module = arguments[i];
for (var target_name in module) {
if (module.hasOwnProperty(target_name)) {
var target = module[target_name];
if (target.prototype instanceof widget.WidgetModel) {
widgetmanager.WidgetManager.register_widget_model(target_name, target);
} else if (target.prototype instanceof widget.WidgetView) {
widgetmanager.WidgetManager.register_widget_view(target_name, target);
}
}
}
}
return {'WidgetManager': widgetmanager.WidgetManager};
});

@ -1,489 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"underscore",
"backbone",
"jquery",
"base/js/utils",
"base/js/namespace",
"services/kernels/comm"
], function (_, Backbone, $, utils, IPython, comm) {
"use strict";
//--------------------------------------------------------------------
// WidgetManager class
//--------------------------------------------------------------------
var WidgetManager = function (comm_manager, notebook) {
/**
* Public constructor
*/
WidgetManager._managers.push(this);
// Attach a comm manager to the
this.keyboard_manager = notebook.keyboard_manager;
this.notebook = notebook;
this.comm_manager = comm_manager;
this.comm_target_name = 'ipython.widget';
this._models = {}; /* Dictionary of model ids and model instance promises */
// Register with the comm manager.
this.comm_manager.register_target(this.comm_target_name, $.proxy(this._handle_comm_open, this));
// Load the initial state of the widget manager if a load callback was
// registered.
var that = this;
if (WidgetManager._load_callback) {
Promise.resolve().then(function () {
return WidgetManager._load_callback.call(that);
}).then(function(state) {
that.set_state(state);
}).catch(utils.reject('Error loading widget manager state', true));
}
// Setup state saving code.
this.notebook.events.on('before_save.Notebook', function() {
var save_callback = WidgetManager._save_callback;
var options = WidgetManager._get_state_options;
if (save_callback) {
that.get_state(options).then(function(state) {
save_callback.call(that, state);
}).catch(utils.reject('Could not call widget save state callback.', true));
}
});
};
//--------------------------------------------------------------------
// Class level
//--------------------------------------------------------------------
WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
WidgetManager._managers = []; /* List of widget managers */
WidgetManager._load_callback = null;
WidgetManager._save_callback = null;
WidgetManager.register_widget_model = function (model_name, model_type) {
/**
* Registers a widget model by name.
*/
WidgetManager._model_types[model_name] = model_type;
};
WidgetManager.register_widget_view = function (view_name, view_type) {
/**
* Registers a widget view by name.
*/
WidgetManager._view_types[view_name] = view_type;
};
WidgetManager.set_state_callbacks = function (load_callback, save_callback, options) {
/**
* Registers callbacks for widget state persistence.
*
* Parameters
* ----------
* load_callback: function()
* function that is called when the widget manager state should be
* loaded. This function should return a promise for the widget
* manager state. An empty state is an empty dictionary `{}`.
* save_callback: function(state as dictionary)
* function that is called when the notebook is saved or autosaved.
* The current state of the widget manager is passed in as the first
* argument.
*/
WidgetManager._load_callback = load_callback;
WidgetManager._save_callback = save_callback;
WidgetManager._get_state_options = options;
// Use the load callback to immediately load widget states.
WidgetManager._managers.forEach(function(manager) {
if (load_callback) {
Promise.resolve().then(function () {
return load_callback.call(manager);
}).then(function(state) {
manager.set_state(state);
}).catch(utils.reject('Error loading widget manager state', true));
}
});
};
// Use local storage to persist widgets across page refresh by default.
// LocalStorage is per domain, so we need to explicitly set the URL
// that the widgets are associated with so they don't show on other
// pages hosted by the noteboook server.
var url = [window.location.protocol, '//', window.location.host, window.location.pathname].join('');
var key = 'widgets:' + url;
WidgetManager.set_state_callbacks(function() {
if (localStorage[key]) {
return JSON.parse(localStorage[key]);
}
return {};
}, function(state) {
localStorage[key] = JSON.stringify(state);
});
//--------------------------------------------------------------------
// Instance level
//--------------------------------------------------------------------
WidgetManager.prototype.display_view = function(msg, model) {
/**
* Displays a view for a particular model.
*/
var cell = this.get_msg_cell(msg.parent_header.msg_id);
if (cell === null) {
return Promise.reject(new Error("Could not determine where the display" +
" message was from. Widget will not be displayed"));
} else {
return this.display_view_in_cell(cell, model)
.catch(utils.reject('Could not display view', true));
}
};
WidgetManager.prototype.display_view_in_cell = function(cell, model) {
// Displays a view in a cell.
if (cell.widgetarea) {
var that = this;
return cell.widgetarea.display_widget_view(this.create_view(model, {
cell: cell,
// Only set cell_index when view is displayed as directly.
cell_index: that.notebook.find_cell_index(cell),
})).then(function(view) {
that._handle_display_view(view);
view.trigger('displayed');
return view;
}).catch(utils.reject('Could not create or display view', true));
} else {
return Promise.reject(new Error('Cell does not have a `widgetarea` defined'));
}
};
WidgetManager.prototype._handle_display_view = function (view) {
/**
* Have the IPython keyboard manager disable its event
* handling so the widget can capture keyboard input.
* Note, this is only done on the outer most widgets.
*/
if (this.keyboard_manager) {
this.keyboard_manager.register_events(view.$el);
if (view.additional_elements) {
for (var i = 0; i < view.additional_elements.length; i++) {
this.keyboard_manager.register_events(view.additional_elements[i]);
}
}
}
};
WidgetManager.prototype.create_view = function(model, options) {
/**
* Creates a promise for a view of a given model
*
* Make sure the view creation is not out of order with
* any state updates.
*/
model.state_change = model.state_change.then(function() {
return utils.load_class(model.get('_view_name'), model.get('_view_module'),
WidgetManager._view_types).then(function(ViewType) {
// If a view is passed into the method, use that view's cell as
// the cell for the view that is created.
options = options || {};
if (options.parent !== undefined) {
options.cell = options.parent.options.cell;
}
// Create and render the view...
var parameters = {model: model, options: options};
var view = new ViewType(parameters);
view.listenTo(model, 'destroy', view.remove);
return Promise.resolve(view.render()).then(function() {return view;});
}).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
});
var id = utils.uuid();
model.views[id] = model.state_change;
model.state_change.then(function(view) {
view.once('remove', function() {
delete view.model.views[id];
}, this);
});
return model.state_change;
};
WidgetManager.prototype.get_msg_cell = function (msg_id) {
var cell = null;
// First, check to see if the msg was triggered by cell execution.
if (this.notebook) {
cell = this.notebook.get_msg_cell(msg_id);
}
if (cell !== null) {
return cell;
}
// Second, check to see if a get_cell callback was defined
// for the message. get_cell callbacks are registered for
// widget messages, so this block is actually checking to see if the
// message was triggered by a widget.
var kernel = this.comm_manager.kernel;
if (kernel) {
var callbacks = kernel.get_callbacks_for_msg(msg_id);
if (callbacks && callbacks.iopub &&
callbacks.iopub.get_cell !== undefined) {
return callbacks.iopub.get_cell();
}
}
// Not triggered by a cell or widget (no get_cell callback
// exists).
return null;
};
WidgetManager.prototype.callbacks = function (view) {
/**
* callback handlers specific a view
*/
var callbacks = {};
if (view && view.options.cell) {
// Try to get output handlers
var cell = view.options.cell;
var handle_output = null;
var handle_clear_output = null;
if (cell.output_area) {
handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
}
// Create callback dictionary using what is known
var that = this;
callbacks = {
iopub : {
output : handle_output,
clear_output : handle_clear_output,
// Special function only registered by widget messages.
// Allows us to get the cell for a message so we know
// where to add widgets if the code requires it.
get_cell : function () {
return cell;
},
},
};
}
return callbacks;
};
WidgetManager.prototype.get_model = function (model_id) {
/**
* Get a promise for a model by model id.
*/
return this._models[model_id];
};
WidgetManager.prototype._handle_comm_open = function (comm, msg) {
/**
* Handle when a comm is opened.
*/
return this.create_model({
model_name: msg.content.data.model_name,
model_module: msg.content.data.model_module,
comm: comm}).catch(utils.reject("Couldn't create a model.", true));
};
WidgetManager.prototype.create_model = function (options) {
/**
* Create and return a promise for a new widget model
*
* Minimally, one must provide the model_name and widget_class
* parameters to create a model from Javascript.
*
* Example
* --------
* JS:
* IPython.notebook.kernel.widget_manager.create_model({
* model_name: 'WidgetModel',
* widget_class: 'jupyter_notebook.widgets.widget_int.IntSlider'})
* .then(function(model) { console.log('Create success!', model); },
* $.proxy(console.error, console));
*
* Parameters
* ----------
* options: dictionary
* Dictionary of options with the following contents:
* model_name: string
* Target name of the widget model to create.
* model_module: (optional) string
* Module name of the widget model to create.
* widget_class: (optional) string
* Target name of the widget in the back-end.
* comm: (optional) Comm
*
* Create a comm if it wasn't provided.
*/
var comm = options.comm;
if (!comm) {
comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
}
var that = this;
var model_id = comm.comm_id;
var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
.then(function(ModelType) {
var widget_model = new ModelType(that, model_id, comm);
widget_model.once('comm:close', function () {
delete that._models[model_id];
});
widget_model.name = options.model_name;
widget_model.module = options.model_module;
return widget_model;
}, function(error) {
delete that._models[model_id];
var wrapped_error = new utils.WrappedError("Couldn't create model", error);
return Promise.reject(wrapped_error);
});
this._models[model_id] = model_promise;
return model_promise;
};
WidgetManager.prototype.get_state = function(options) {
/**
* Asynchronously get the state of the widget manager.
*
* This includes all of the widget models and the cells that they are
* displayed in.
*
* Parameters
* ----------
* options: dictionary
* Dictionary of options with the following contents:
* only_displayed: (optional) boolean=false
* Only return models with one or more displayed views.
* not_live: (optional) boolean=false
* Include models that have comms with severed connections.
*
* Returns
* -------
* Promise for a state dictionary
*/
var that = this;
return utils.resolve_promises_dict(this._models).then(function(models) {
var state = {};
var model_promises = [];
for (var model_id in models) {
if (models.hasOwnProperty(model_id)) {
var model = models[model_id];
// If the model has one or more views defined for it,
// consider it displayed.
var displayed_flag = !(options && options.only_displayed) || Object.keys(model.views).length > 0;
var live_flag = (options && options.not_live) || model.comm_live;
if (displayed_flag && live_flag) {
state[model_id] = {
model_name: model.name,
model_module: model.module,
state: model.get_state(),
views: [],
};
// Get the views that are displayed *now*.
(function(local_state) {
model_promises.push(utils.resolve_promises_dict(model.views).then(function(model_views) {
for (var id in model_views) {
if (model_views.hasOwnProperty(id)) {
var view = model_views[id];
if (view.options.cell_index) {
local_state.views.push(view.options.cell_index);
}
}
}
}));
})(state[model_id]);
}
}
}
return Promise.all(model_promises).then(function() { return state; });
}).catch(utils.reject('Could not get state of widget manager', true));
};
WidgetManager.prototype.set_state = function(state) {
/**
* Set the notebook's state.
*
* Reconstructs all of the widget models and attempts to redisplay the
* widgets in the appropriate cells by cell index.
*/
// Get the kernel when it's available.
var that = this;
return this._get_connected_kernel().then(function(kernel) {
// Recreate all the widget models for the given state and
// display the views.
that.all_views = [];
var model_ids = Object.keys(state);
for (var i = 0; i < model_ids.length; i++) {
var model_id = model_ids[i];
// Recreate a comm using the widget's model id (model_id == comm_id).
var new_comm = new comm.Comm(kernel.widget_manager.comm_target_name, model_id);
kernel.comm_manager.register_comm(new_comm);
// Create the model using the recreated comm. When the model is
// created we don't know yet if the comm is valid so set_comm_live
// false. Once we receive the first state push from the back-end
// we know the comm is alive.
var views = kernel.widget_manager.create_model({
comm: new_comm,
model_name: state[model_id].model_name,
model_module: state[model_id].model_module})
.then(function(model) {
model.set_comm_live(false);
var view_promise = Promise.resolve().then(function() {
return model.set_state(state[model.id].state);
}).then(function() {
model.request_state().then(function() {
model.set_comm_live(true);
});
// Display the views of the model.
var views = [];
var model_views = state[model.id].views;
for (var j=0; j<model_views.length; j++) {
var cell_index = model_views[j];
var cell = that.notebook.get_cell(cell_index);
views.push(that.display_view_in_cell(cell, model));
}
return Promise.all(views);
});
return view_promise;
});
that.all_views.push(views);
}
return Promise.all(that.all_views);
}).catch(utils.reject('Could not set widget manager state.', true));
};
WidgetManager.prototype._get_connected_kernel = function() {
/**
* Gets a promise for a connected kernel
*/
var that = this;
return new Promise(function(resolve, reject) {
if (that.comm_manager &&
that.comm_manager.kernel &&
that.comm_manager.kernel.is_connected()) {
resolve(that.comm_manager.kernel);
} else {
that.notebook.events.on('kernel_connected.Kernel', function(event, data) {
resolve(data.kernel);
});
}
});
};
// Backwards compatibility.
IPython.WidgetManager = WidgetManager;
return {'WidgetManager': WidgetManager};
});

@ -1,808 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(["nbextensions/widgets/widgets/js/manager",
"underscore",
"backbone",
"jquery",
"base/js/utils",
"base/js/namespace",
], function(widgetmanager, _, Backbone, $, utils, IPython){
"use strict";
var unpack_models = function unpack_models(value, model) {
/**
* Replace model ids with models recursively.
*/
var unpacked;
if ($.isArray(value)) {
unpacked = [];
_.each(value, function(sub_value, key) {
unpacked.push(unpack_models(sub_value, model));
});
return Promise.all(unpacked);
} else if (value instanceof Object) {
unpacked = {};
_.each(value, function(sub_value, key) {
unpacked[key] = unpack_models(sub_value, model);
});
return utils.resolve_promises_dict(unpacked);
} else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
// get_model returns a promise already
return model.widget_manager.get_model(value.slice(10, value.length));
} else {
return Promise.resolve(value);
}
};
var WidgetModel = Backbone.Model.extend({
constructor: function (widget_manager, model_id, comm) {
/**
* Constructor
*
* Creates a WidgetModel instance.
*
* Parameters
* ----------
* widget_manager : WidgetManager instance
* model_id : string
* An ID unique to this model.
* comm : Comm instance (optional)
*/
this.widget_manager = widget_manager;
this.state_change = Promise.resolve();
this._buffered_state_diff = {};
this.pending_msgs = 0;
this.msg_buffer = null;
this.state_lock = null;
this.id = model_id;
this.views = {};
this._resolve_received_state = {};
if (comm !== undefined) {
// Remember comm associated with the model.
this.comm = comm;
comm.model = this;
// Hook comm messages up to model.
comm.on_close($.proxy(this._handle_comm_closed, this));
comm.on_msg($.proxy(this._handle_comm_msg, this));
// Assume the comm is alive.
this.set_comm_live(true);
} else {
this.set_comm_live(false);
}
// Listen for the events that lead to the websocket being terminated.
var that = this;
var died = function() {
that.set_comm_live(false);
};
widget_manager.notebook.events.on('kernel_disconnected.Kernel', died);
widget_manager.notebook.events.on('kernel_killed.Kernel', died);
widget_manager.notebook.events.on('kernel_restarting.Kernel', died);
widget_manager.notebook.events.on('kernel_dead.Kernel', died);
return Backbone.Model.apply(this);
},
send: function (content, callbacks, buffers) {
/**
* Send a custom msg over the comm.
*/
if (this.comm !== undefined) {
var data = {method: 'custom', content: content};
this.comm.send(data, callbacks, {}, buffers);
this.pending_msgs++;
}
},
request_state: function(callbacks) {
/**
* Request a state push from the back-end.
*/
if (!this.comm) {
console.error("Could not request_state because comm doesn't exist!");
return;
}
var msg_id = this.comm.send({method: 'request_state'}, callbacks || this.widget_manager.callbacks());
// Promise that is resolved when a state is received
// from the back-end.
var that = this;
var received_state = new Promise(function(resolve) {
that._resolve_received_state[msg_id] = resolve;
});
return received_state;
},
set_comm_live: function(live) {
/**
* Change the comm_live state of the model.
*/
if (this.comm_live === undefined || this.comm_live != live) {
this.comm_live = live;
this.trigger(live ? 'comm:live' : 'comm:dead', {model: this});
}
},
close: function(comm_closed) {
/**
* Close model
*/
if (this.comm && !comm_closed) {
this.comm.close();
}
this.stopListening();
this.trigger('destroy', this);
delete this.comm.model; // Delete ref so GC will collect widget model.
delete this.comm;
delete this.model_id; // Delete id from model so widget manager cleans up.
_.each(this.views, function(v, id, views) {
v.then(function(view) {
view.remove();
delete views[id];
});
});
},
_handle_comm_closed: function (msg) {
/**
* Handle when a widget is closed.
*/
this.trigger('comm:close');
this.close(true);
},
_handle_comm_msg: function (msg) {
/**
* Handle incoming comm msg.
*/
var method = msg.content.data.method;
var that = this;
switch (method) {
case 'update':
this.state_change = this.state_change
.then(function() {
var state = msg.content.data.state || {};
var buffer_keys = msg.content.data.buffers || [];
var buffers = msg.buffers || [];
for (var i=0; i<buffer_keys.length; i++) {
state[buffer_keys[i]] = buffers[i];
}
// deserialize fields that have custom deserializers
var serializers = that.constructor.serializers;
if (serializers) {
for (var k in state) {
if (serializers[k] && serializers[k].deserialize) {
state[k] = (serializers[k].deserialize)(state[k], that);
}
}
}
return utils.resolve_promises_dict(state);
}).then(function(state) {
return that.set_state(state);
}).catch(utils.reject("Couldn't process update msg for model id '" + String(that.id) + "'", true))
.then(function() {
var parent_id = msg.parent_header.msg_id;
if (that._resolve_received_state[parent_id] !== undefined) {
that._resolve_received_state[parent_id].call();
delete that._resolve_received_state[parent_id];
}
}).catch(utils.reject("Couldn't resolve state request promise", true));
break;
case 'custom':
this.trigger('msg:custom', msg.content.data.content, msg.buffers);
break;
case 'display':
this.state_change = this.state_change.then(function() {
that.widget_manager.display_view(msg, that);
}).catch(utils.reject('Could not process display view msg', true));
break;
}
},
set_state: function (state) {
var that = this;
// Handle when a widget is updated via the python side.
return new Promise(function(resolve, reject) {
that.state_lock = state;
try {
WidgetModel.__super__.set.call(that, state);
} finally {
that.state_lock = null;
}
resolve();
}).catch(utils.reject("Couldn't set model state", true));
},
get_state: function() {
// Get the serializable state of the model.
// Equivalent to Backbone.Model.toJSON()
return _.clone(this.attributes);
},
_handle_status: function (msg, callbacks) {
/**
* Handle status msgs.
*
* execution_state : ('busy', 'idle', 'starting')
*/
if (this.comm !== undefined) {
if (msg.content.execution_state ==='idle') {
// Send buffer if this message caused another message to be
// throttled.
if (this.msg_buffer !== null &&
(this.get('msg_throttle') || 3) === this.pending_msgs) {
var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
this.comm.send(data, callbacks);
this.msg_buffer = null;
} else {
--this.pending_msgs;
}
}
}
},
callbacks: function(view) {
/**
* Create msg callbacks for a comm msg.
*/
var callbacks = this.widget_manager.callbacks(view);
if (callbacks.iopub === undefined) {
callbacks.iopub = {};
}
var that = this;
callbacks.iopub.status = function (msg) {
that._handle_status(msg, callbacks);
};
return callbacks;
},
set: function(key, val, options) {
/**
* Set a value.
*/
var return_value = WidgetModel.__super__.set.apply(this, arguments);
// Backbone only remembers the diff of the most recent set()
// operation. Calling set multiple times in a row results in a
// loss of diff information. Here we keep our own running diff.
this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
return return_value;
},
sync: function (method, model, options) {
/**
* Handle sync to the back-end. Called when a model.save() is called.
*
* Make sure a comm exists.
* Parameters
* ----------
* method : create, update, patch, delete, read
* create/update always send the full attribute set
* patch - only send attributes listed in options.attrs, and if we are queuing
* up messages, combine with previous messages that have not been sent yet
* model : the model we are syncing
* will normally be the same as `this`
* options : dict
* the `attrs` key, if it exists, gives an {attr: value} dict that should be synced,
* otherwise, sync all attributes
*
*/
var error = options.error || function() {
console.error('Backbone sync error:', arguments);
};
if (this.comm === undefined) {
error();
return false;
}
var attrs = (method === 'patch') ? options.attrs : model.get_state(options);
// the state_lock lists attributes that are currently be changed right now from a kernel message
// we don't want to send these non-changes back to the kernel, so we delete them out of attrs
// (but we only delete them if the value hasn't changed from the value stored in the state_lock
if (this.state_lock !== null) {
var keys = Object.keys(this.state_lock);
for (var i=0; i<keys.length; i++) {
var key = keys[i];
if (attrs[key] === this.state_lock[key]) {
delete attrs[key];
}
}
}
if (_.size(attrs) > 0) {
// If this message was sent via backbone itself, it will not
// have any callbacks. It's important that we create callbacks
// so we can listen for status messages, etc...
var callbacks = options.callbacks || this.callbacks();
// Check throttle.
if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
// The throttle has been exceeded, buffer the current msg so
// it can be sent once the kernel has finished processing
// some of the existing messages.
// Combine updates if it is a 'patch' sync, otherwise replace updates
switch (method) {
case 'patch':
this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
break;
case 'update':
case 'create':
this.msg_buffer = attrs;
break;
default:
error();
return false;
}
this.msg_buffer_callbacks = callbacks;
} else {
// We haven't exceeded the throttle, send the message like
// normal.
this.send_sync_message(attrs, callbacks);
this.pending_msgs++;
}
}
// Since the comm is a one-way communication, assume the message
// arrived. Don't call success since we don't have a model back from the server
// this means we miss out on the 'sync' event.
this._buffered_state_diff = {};
},
send_sync_message: function(attrs, callbacks) {
// prepare and send a comm message syncing attrs
var that = this;
// first, build a state dictionary with key=the attribute and the value
// being the value or the promise of the serialized value
var serializers = this.constructor.serializers;
if (serializers) {
for (var k in attrs) {
if (serializers[k] && serializers[k].serialize) {
attrs[k] = (serializers[k].serialize)(attrs[k], this);
}
}
}
utils.resolve_promises_dict(attrs).then(function(state) {
// get binary values, then send
var keys = Object.keys(state);
var buffers = [];
var buffer_keys = [];
for (var i=0; i<keys.length; i++) {
var key = keys[i];
var value = state[key];
if (value) {
if (value.buffer instanceof ArrayBuffer
|| value instanceof ArrayBuffer) {
buffers.push(value);
buffer_keys.push(key);
delete state[key];
}
}
}
that.comm.send({method: 'backbone', sync_data: state, buffer_keys: buffer_keys}, callbacks, {}, buffers);
}).catch(function(error) {
that.pending_msgs--;
return (utils.reject("Couldn't send widget sync message", true))(error);
});
},
save_changes: function(callbacks) {
/**
* Push this model's state to the back-end
*
* This invokes a Backbone.Sync.
*/
this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
},
on_some_change: function(keys, callback, context) {
/**
* on_some_change(["key1", "key2"], foo, context) differs from
* on("change:key1 change:key2", foo, context).
* If the widget attributes key1 and key2 are both modified,
* the second form will result in foo being called twice
* while the first will call foo only once.
*/
this.on('change', function() {
if (keys.some(this.hasChanged, this)) {
callback.apply(context, arguments);
}
}, this);
},
toJSON: function(options) {
/**
* Serialize the model. See the types.js deserialization function
* and the kernel-side serializer/deserializer
*/
return "IPY_MODEL_"+this.id;
}
});
widgetmanager.WidgetManager.register_widget_model('WidgetModel', WidgetModel);
var WidgetView = Backbone.View.extend({
initialize: function(parameters) {
/**
* Public constructor.
*/
this.model.on('change',this.update,this);
// Bubble the comm live events.
this.model.on('comm:live', function() {
this.trigger('comm:live', this);
}, this);
this.model.on('comm:dead', function() {
this.trigger('comm:dead', this);
}, this);
this.options = parameters.options;
this.on('displayed', function() {
this.is_displayed = true;
}, this);
},
update: function(){
/**
* Triggered on model change.
*
* Update view to be consistent with this.model
*/
},
create_child_view: function(child_model, options) {
/**
* Create and promise that resolves to a child view of a given model
*/
var that = this;
options = $.extend({ parent: this }, options || {});
return this.model.widget_manager.create_view(child_model, options).catch(utils.reject("Couldn't create child view", true));
},
callbacks: function(){
/**
* Create msg callbacks for a comm msg.
*/
return this.model.callbacks(this);
},
render: function(){
/**
* Render the view.
*
* By default, this is only called the first time the view is created
*/
},
send: function (content, buffers) {
/**
* Send a custom msg associated with this view.
*/
this.model.send(content, this.callbacks(), buffers);
},
touch: function () {
this.model.save_changes(this.callbacks());
},
after_displayed: function (callback, context) {
/**
* Calls the callback right away is the view is already displayed
* otherwise, register the callback to the 'displayed' event.
*/
if (this.is_displayed) {
callback.apply(context);
} else {
this.on('displayed', callback, context);
}
},
remove: function () {
// Raise a remove event when the view is removed.
WidgetView.__super__.remove.apply(this, arguments);
this.trigger('remove');
}
});
var DOMWidgetView = WidgetView.extend({
initialize: function (parameters) {
/**
* Public constructor
*/
DOMWidgetView.__super__.initialize.apply(this, [parameters]);
this.model.on('change:visible', this.update_visible, this);
this.model.on('change:_css', this.update_css, this);
this.model.on('change:_dom_classes', function(model, new_classes) {
var old_classes = model.previous('_dom_classes');
this.update_classes(old_classes, new_classes);
}, this);
this.model.on('change:color', function (model, value) {
this.update_attr('color', value); }, this);
this.model.on('change:background_color', function (model, value) {
this.update_attr('background', value); }, this);
this.model.on('change:width', function (model, value) {
this.update_attr('width', value); }, this);
this.model.on('change:height', function (model, value) {
this.update_attr('height', value); }, this);
this.model.on('change:border_color', function (model, value) {
this.update_attr('border-color', value); }, this);
this.model.on('change:border_width', function (model, value) {
this.update_attr('border-width', value); }, this);
this.model.on('change:border_style', function (model, value) {
this.update_attr('border-style', value); }, this);
this.model.on('change:font_style', function (model, value) {
this.update_attr('font-style', value); }, this);
this.model.on('change:font_weight', function (model, value) {
this.update_attr('font-weight', value); }, this);
this.model.on('change:font_size', function (model, value) {
this.update_attr('font-size', this._default_px(value)); }, this);
this.model.on('change:font_family', function (model, value) {
this.update_attr('font-family', value); }, this);
this.model.on('change:padding', function (model, value) {
this.update_attr('padding', value); }, this);
this.model.on('change:margin', function (model, value) {
this.update_attr('margin', this._default_px(value)); }, this);
this.model.on('change:border_radius', function (model, value) {
this.update_attr('border-radius', this._default_px(value)); }, this);
this.after_displayed(function() {
this.update_visible(this.model, this.model.get("visible"));
this.update_classes([], this.model.get('_dom_classes'));
this.update_attr('color', this.model.get('color'));
this.update_attr('background', this.model.get('background_color'));
this.update_attr('width', this.model.get('width'));
this.update_attr('height', this.model.get('height'));
this.update_attr('border-color', this.model.get('border_color'));
this.update_attr('border-width', this.model.get('border_width'));
this.update_attr('border-style', this.model.get('border_style'));
this.update_attr('font-style', this.model.get('font_style'));
this.update_attr('font-weight', this.model.get('font_weight'));
this.update_attr('font-size', this._default_px(this.model.get('font_size')));
this.update_attr('font-family', this.model.get('font_family'));
this.update_attr('padding', this.model.get('padding'));
this.update_attr('margin', this._default_px(this.model.get('margin')));
this.update_attr('border-radius', this._default_px(this.model.get('border_radius')));
this.update_css(this.model, this.model.get("_css"));
}, this);
},
_default_px: function(value) {
/**
* Makes browser interpret a numerical string as a pixel value.
*/
if (value && /^\d+\.?(\d+)?$/.test(value.trim())) {
return value.trim() + 'px';
}
return value;
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
this.$el.css(name, value);
},
update_visible: function(model, value) {
/**
* Update visibility
*/
switch(value) {
case null: // python None
this.$el.show().css('visibility', 'hidden'); break;
case false:
this.$el.hide(); break;
case true:
this.$el.show().css('visibility', ''); break;
}
},
update_css: function (model, css) {
/**
* Update the css styling of this view.
*/
if (css === undefined) {return;}
for (var i = 0; i < css.length; i++) {
// Apply the css traits to all elements that match the selector.
var selector = css[i][0];
var elements = this._get_selector_element(selector);
if (elements.length > 0) {
var trait_key = css[i][1];
var trait_value = css[i][2];
elements.css(trait_key ,trait_value);
}
}
},
update_classes: function (old_classes, new_classes, $el) {
/**
* Update the DOM classes applied to an element, default to this.$el.
*/
if ($el===undefined) {
$el = this.$el;
}
_.difference(old_classes, new_classes).map(function(c) {$el.removeClass(c);})
_.difference(new_classes, old_classes).map(function(c) {$el.addClass(c);})
},
update_mapped_classes: function(class_map, trait_name, previous_trait_value, $el) {
/**
* Update the DOM classes applied to the widget based on a single
* trait's value.
*
* Given a trait value classes map, this function automatically
* handles applying the appropriate classes to the widget element
* and removing classes that are no longer valid.
*
* Parameters
* ----------
* class_map: dictionary
* Dictionary of trait values to class lists.
* Example:
* {
* success: ['alert', 'alert-success'],
* info: ['alert', 'alert-info'],
* warning: ['alert', 'alert-warning'],
* danger: ['alert', 'alert-danger']
* };
* trait_name: string
* Name of the trait to check the value of.
* previous_trait_value: optional string, default ''
* Last trait value
* $el: optional jQuery element handle, defaults to this.$el
* Element that the classes are applied to.
*/
var key = previous_trait_value;
if (key === undefined) {
key = this.model.previous(trait_name);
}
var old_classes = class_map[key] ? class_map[key] : [];
key = this.model.get(trait_name);
var new_classes = class_map[key] ? class_map[key] : [];
this.update_classes(old_classes, new_classes, $el || this.$el);
},
_get_selector_element: function (selector) {
/**
* Get the elements via the css selector.
*/
var elements;
if (!selector) {
elements = this.$el;
} else {
elements = this.$el.find(selector).addBack(selector);
}
return elements;
},
typeset: function(element, text){
utils.typeset.apply(null, arguments);
},
});
var ViewList = function(create_view, remove_view, context) {
/**
* - create_view and remove_view are default functions called when adding or removing views
* - create_view takes a model and returns a view or a promise for a view for that model
* - remove_view takes a view and destroys it (including calling `view.remove()`)
* - each time the update() function is called with a new list, the create and remove
* callbacks will be called in an order so that if you append the views created in the
* create callback and remove the views in the remove callback, you will duplicate
* the order of the list.
* - the remove callback defaults to just removing the view (e.g., pass in null for the second parameter)
* - the context defaults to the created ViewList. If you pass another context, the create and remove
* will be called in that context.
*/
this.initialize.apply(this, arguments);
};
_.extend(ViewList.prototype, {
initialize: function(create_view, remove_view, context) {
this._handler_context = context || this;
this._models = [];
this.views = []; // list of promises for views
this._create_view = create_view;
this._remove_view = remove_view || function(view) {view.remove();};
},
update: function(new_models, create_view, remove_view, context) {
/**
* the create_view, remove_view, and context arguments override the defaults
* specified when the list is created.
* after this function, the .views attribute is a list of promises for views
* if you want to perform some action on the list of views, do something like
* `Promise.all(myviewlist.views).then(function(views) {...});`
*/
var remove = remove_view || this._remove_view;
var create = create_view || this._create_view;
context = context || this._handler_context;
var i = 0;
// first, skip past the beginning of the lists if they are identical
for (; i < new_models.length; i++) {
if (i >= this._models.length || new_models[i] !== this._models[i]) {
break;
}
}
var first_removed = i;
// Remove the non-matching items from the old list.
var removed = this.views.splice(first_removed, this.views.length-first_removed);
for (var j = 0; j < removed.length; j++) {
removed[j].then(function(view) {
remove.call(context, view)
});
}
// Add the rest of the new list items.
for (; i < new_models.length; i++) {
this.views.push(Promise.resolve(create.call(context, new_models[i])));
}
// make a copy of the input array
this._models = new_models.slice();
},
remove: function() {
/**
* removes every view in the list; convenience function for `.update([])`
* that should be faster
* returns a promise that resolves after this removal is done
*/
var that = this;
return Promise.all(this.views).then(function(views) {
for (var i = 0; i < that.views.length; i++) {
that._remove_view.call(that._handler_context, views[i]);
}
that.views = [];
that._models = [];
});
},
});
var widget = {
'unpack_models': unpack_models,
'WidgetModel': WidgetModel,
'WidgetView': WidgetView,
'DOMWidgetView': DOMWidgetView,
'ViewList': ViewList,
};
// For backwards compatability.
$.extend(IPython, widget);
return widget;
});

@ -1,193 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
"bootstrap",
], function(widget, $){
var CheckboxView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-checkbox');
this.$label = $('<div />')
.addClass('widget-label')
.appendTo(this.$el)
.hide();
this.$checkbox = $('<input />')
.attr('type', 'checkbox')
.appendTo(this.$el)
.click($.proxy(this.handle_click, this));
this.update(); // Set defaults.
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$checkbox.css(name, value);
}
},
handle_click: function() {
/**
* Handles when the checkbox is clicked.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
var value = this.model.get('value');
this.model.set('value', ! value, {updated_view: this});
this.touch();
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
this.$checkbox.prop('checked', this.model.get('value'));
if (options === undefined || options.updated_view != this) {
this.$checkbox.prop("disabled", this.model.get("disabled"));
var description = this.model.get("description");
if (description.trim().length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return CheckboxView.__super__.update.apply(this);
},
});
var ToggleButtonView = widget.DOMWidgetView.extend({
render : function() {
/**
* Called when view is rendered.
*/
var that = this;
this.setElement($('<button />')
.addClass('btn btn-default')
.attr('type', 'button')
.on('click', function (e) {
e.preventDefault();
that.handle_click();
}));
this.$el.attr("data-toggle", "tooltip");
this.model.on('change:button_style', function(model, value) {
this.update_button_style();
}, this);
this.update_button_style('');
this.update(); // Set defaults.
},
update_button_style: function(previous_trait_value) {
var class_map = {
primary: ['btn-primary'],
success: ['btn-success'],
info: ['btn-info'],
warning: ['btn-warning'],
danger: ['btn-danger']
};
this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (this.model.get('value')) {
this.$el.addClass('active');
} else {
this.$el.removeClass('active');
}
if (options === undefined || options.updated_view != this) {
this.$el.prop("disabled", this.model.get("disabled"));
this.$el.attr("title", this.model.get("tooltip"));
var description = this.model.get("description");
var icon = this.model.get("icon");
if (description.trim().length === 0 && icon.trim().length ===0) {
this.$el.html("&nbsp;"); // Preserve button height
} else {
this.$el.text(description);
$('<i class="fa"></i>').prependTo(this.$el).addClass(icon);
}
}
return ToggleButtonView.__super__.update.apply(this);
},
handle_click: function(e) {
/**
* Handles and validates user input.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
var value = this.model.get('value');
this.model.set('value', ! value, {updated_view: this});
this.touch();
},
});
var ValidView = widget.DOMWidgetView.extend({
render: function() {
/**
* Called when view is rendered.
*/
this.$el.addClass("widget-valid");
this.model.on("change", this.update, this);
this.update();
},
update: function() {
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
var icon, color, readout;
if (this.model.get("value")) {
icon = "fa-check";
color = "green";
readout = "";
} else {
icon = "fa-close";
color = "red";
readout = this.model.get("readout");
}
this.$el.text(readout);
$('<i class="fa"></i>').prependTo(this.$el).addClass(icon);
this.after_displayed(function() {
this.$el.css("color", color);
}, this);
}
});
return {
'CheckboxView': CheckboxView,
'ToggleButtonView': ToggleButtonView,
'ValidView': ValidView,
};
});

@ -1,162 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jqueryui",
"underscore",
"base/js/utils",
"bootstrap",
], function(widget, $, _, utils){
"use strict";
var BoxModel = widget.WidgetModel.extend({}, {
serializers: _.extend({
children: {deserialize: widget.unpack_models}
}, widget.WidgetModel.serializers)
});
var BoxView = widget.DOMWidgetView.extend({
initialize: function(){
/**
* Public constructor
*/
BoxView.__super__.initialize.apply(this, arguments);
this.children_views = new widget.ViewList(this.add_child_model, null, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.children_views.update(value);
}, this);
this.listenTo(this.model, 'change:overflow_x', function(model, value) {
this.update_overflow_x();
}, this);
this.listenTo(this.model, 'change:overflow_y', function(model, value) {
this.update_overflow_y();
}, this);
this.listenTo(this.model, 'change:box_style', function(model, value) {
this.update_box_style();
}, this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
this.$box.css(name, value);
},
render: function(){
/**
* Called when view is rendered.
*/
this.$box = this.$el;
this.$box.addClass('widget-box');
this.children_views.update(this.model.get('children'));
this.update_overflow_x();
this.update_overflow_y();
this.update_box_style('');
},
update_overflow_x: function() {
/**
* Called when the x-axis overflow setting is changed.
*/
this.$box.css('overflow-x', this.model.get('overflow_x'));
},
update_overflow_y: function() {
/**
* Called when the y-axis overflow setting is changed.
*/
this.$box.css('overflow-y', this.model.get('overflow_y'));
},
update_box_style: function(previous_trait_value) {
var class_map = {
success: ['alert', 'alert-success'],
info: ['alert', 'alert-info'],
warning: ['alert', 'alert-warning'],
danger: ['alert', 'alert-danger']
};
this.update_mapped_classes(class_map, 'box_style', previous_trait_value, this.$box);
},
add_child_model: function(model) {
/**
* Called when a model is added to the children list.
*/
var that = this;
var dummy = $('<div/>');
that.$box.append(dummy);
return this.create_child_view(model).then(function(view) {
dummy.replaceWith(view.el);
// Trigger the displayed event of the child view.
that.after_displayed(function() {
view.trigger('displayed');
});
return view;
}).catch(utils.reject("Couldn't add child view to box", true));
},
remove: function() {
/**
* We remove this widget before removing the children as an optimization
* we want to remove the entire container from the DOM first before
* removing each individual child separately.
*/
BoxView.__super__.remove.apply(this, arguments);
this.children_views.remove();
},
});
var FlexBoxView = BoxView.extend({
render: function(){
FlexBoxView.__super__.render.apply(this);
this.listenTo(this.model, 'change:orientation', this.update_orientation, this);
this.listenTo(this.model, 'change:flex', this._flex_changed, this);
this.listenTo(this.model, 'change:pack', this._pack_changed, this);
this.listenTo(this.model, 'change:align', this._align_changed, this);
this._flex_changed();
this._pack_changed();
this._align_changed();
this.update_orientation();
},
update_orientation: function(){
var orientation = this.model.get("orientation");
if (orientation == "vertical") {
this.$box.removeClass("hbox").addClass("vbox");
} else {
this.$box.removeClass("vbox").addClass("hbox");
}
},
_flex_changed: function(){
if (this.model.previous('flex')) {
this.$box.removeClass('box-flex' + this.model.previous('flex'));
}
this.$box.addClass('box-flex' + this.model.get('flex'));
},
_pack_changed: function(){
if (this.model.previous('pack')) {
this.$box.removeClass(this.model.previous('pack'));
}
this.$box.addClass(this.model.get('pack'));
},
_align_changed: function(){
if (this.model.previous('align')) {
this.$box.removeClass('align-' + this.model.previous('align'));
}
this.$box.addClass('align-' + this.model.get('align'));
},
});
return {
'BoxModel': BoxModel,
'BoxView': BoxView,
'FlexBoxView': FlexBoxView,
};
});

@ -1,75 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
"bootstrap",
], function(widget, $){
var ButtonView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.setElement($("<button />")
.addClass('btn btn-default'));
this.$el.attr("data-toggle", "tooltip");
this.model.on('change:button_style', function(model, value) {
this.update_button_style();
}, this);
this.update_button_style('');
this.update(); // Set defaults.
},
update : function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
this.$el.prop("disabled", this.model.get("disabled"));
this.$el.attr("title", this.model.get("tooltip"));
var description = this.model.get("description");
var icon = this.model.get("icon");
if (description.trim().length === 0 && icon.trim().length ===0) {
this.$el.html("&nbsp;"); // Preserve button height
} else {
this.$el.text(description);
$('<i class="fa"></i>').prependTo(this.$el).addClass(icon);
}
return ButtonView.__super__.update.apply(this);
},
update_button_style: function(previous_trait_value) {
var class_map = {
primary: ['btn-primary'],
success: ['btn-success'],
info: ['btn-info'],
warning: ['btn-warning'],
danger: ['btn-danger']
};
this.update_mapped_classes(class_map, 'button_style', previous_trait_value);
},
events: {
// Dictionary of events and their handlers.
'click': '_handle_click',
},
_handle_click: function(){
/**
* Handles when the button is clicked.
*/
this.send({event: 'click'});
},
});
return {
'ButtonView': ButtonView,
};
});

@ -1,34 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"nbextensions/widgets/widgets/js/widget_int",
], function(widget, int_widgets){
var IntSliderView = int_widgets.IntSliderView;
var IntTextView = int_widgets.IntTextView;
var FloatSliderView = IntSliderView.extend({
_parse_value: parseFloat,
// matches: whitespace?, float, whitespace?, [-:], whitespace?, float
_range_regex: /^\s*([+-]?(?:\d*\.?\d+|\d+\.)(?:[eE][+-]?\d+)?)\s*[-:]\s*([+-]?(?:\d*\.?\d+|\d+\.)(?:[eE][+-]?\d+)?)/,
_validate_slide_value: function(x) {
/**
* Validate the value of the slider before sending it to the back-end
* and applying it to the other views on the page.
*/
return x;
},
});
var FloatTextView = IntTextView.extend({
_parse_value: parseFloat
});
return {
'FloatSliderView': FloatSliderView,
'FloatTextView': FloatTextView,
};
});

@ -1,48 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
], function(widget, $){
var ImageView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.setElement($("<img />"));
this.update(); // Set defaults.
},
update : function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
var image_src = 'data:image/' + this.model.get('format') + ';base64,' + this.model.get('_b64value');
this.$el.attr('src', image_src);
var width = this.model.get('width');
if (width !== undefined && width.length > 0) {
this.$el.attr('width', width);
} else {
this.$el.removeAttr('width');
}
var height = this.model.get('height');
if (height !== undefined && height.length > 0) {
this.$el.attr('height', height);
} else {
this.$el.removeAttr('height');
}
return ImageView.__super__.update.apply(this);
},
});
return {
'ImageView': ImageView,
};
});

@ -1,491 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jqueryui",
"base/js/keyboard",
"bootstrap"
], function(widget, $, keyboard){
var IntSliderView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-slider');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$slider = $('<div />')
.slider({})
.addClass('slider');
// Put the slider in a container
this.$slider_container = $('<div />')
.addClass('widget-hslider')
.append(this.$slider);
this.$el.append(this.$slider_container);
this.$readout = $('<div/>')
.appendTo(this.$el)
.addClass('widget-readout')
.attr('contentEditable', true)
.hide();
this.model.on('change:slider_color', function(sender, value) {
this.$slider.find('a').css('background', value);
}, this);
this.$slider.find('a').css('background', this.model.get('slider_color'));
// Set defaults.
this.update();
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'color') {
this.$readout.css(name, value);
} else if (name.substring(0, 4) == 'font') {
this.$readout.css(name, value);
} else if (name.substring(0, 6) == 'border') {
this.$slider.find('a').css(name, value);
this.$slider_container.css(name, value);
} else if (name == 'width' || name == 'height' || name == 'background') {
this.$slider_container.css(name, value);
} else if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$slider.css(name, value);
}
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
// JQuery slider option keys. These keys happen to have a
// one-to-one mapping with the corrosponding keys of the model.
var jquery_slider_keys = ['step', 'disabled'];
var that = this;
that.$slider.slider({});
_.each(jquery_slider_keys, function(key, i) {
var model_value = that.model.get(key);
if (model_value !== undefined) {
that.$slider.slider("option", key, model_value);
}
});
var max = this.model.get('max');
var min = this.model.get('min');
if (min <= max) {
if (max !== undefined) this.$slider.slider('option', 'max', max);
if (min !== undefined) this.$slider.slider('option', 'min', min);
}
var range_value = this.model.get("_range");
if (range_value !== undefined) {
this.$slider.slider("option", "range", range_value);
}
// WORKAROUND FOR JQUERY SLIDER BUG.
// The horizontal position of the slider handle
// depends on the value of the slider at the time
// of orientation change. Before applying the new
// workaround, we set the value to the minimum to
// make sure that the horizontal placement of the
// handle in the vertical slider is always
// consistent.
var orientation = this.model.get('orientation');
var min = this.model.get('min');
var max = this.model.get('max');
if (this.model.get('_range')) {
this.$slider.slider('option', 'values', [min, min]);
} else {
this.$slider.slider('option', 'value', min);
}
this.$slider.slider('option', 'orientation', orientation);
var value = this.model.get('value');
if (this.model.get('_range')) {
// values for the range case are validated python-side in
// _Bounded{Int,Float}RangeWidget._validate
this.$slider.slider('option', 'values', value);
this.$readout.text(value.join("-"));
} else {
if(value > max) {
value = max;
}
else if(value < min){
value = min;
}
this.$slider.slider('option', 'value', value);
this.$readout.text(value);
}
if(this.model.get('value')!=value) {
this.model.set('value', value, {updated_view: this});
this.touch();
}
// Use the right CSS classes for vertical & horizontal sliders
if (orientation=='vertical') {
this.$slider_container
.removeClass('widget-hslider')
.addClass('widget-vslider');
this.$el
.removeClass('widget-hbox')
.addClass('widget-vbox');
} else {
this.$slider_container
.removeClass('widget-vslider')
.addClass('widget-hslider');
this.$el
.removeClass('widget-vbox')
.addClass('widget-hbox');
}
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
var readout = this.model.get('readout');
if (readout) {
this.$readout.show();
} else {
this.$readout.hide();
}
}
return IntSliderView.__super__.update.apply(this);
},
events: {
// Dictionary of events and their handlers.
"slide" : "handleSliderChange",
"blur [contentEditable=true]": "handleTextChange",
"keydown [contentEditable=true]": "handleKeyDown"
},
handleKeyDown: function(e) {
if (e.keyCode == keyboard.keycodes.enter) {
e.preventDefault();
this.handleTextChange();
}
},
handleTextChange: function() {
/**
* this handles the entry of text into the contentEditable label
* first, the value is checked if it contains a parseable number
* (or pair of numbers, for the _range case)
* then it is clamped within the min-max range of the slider
* finally, the model is updated if the value is to be changed
*
* if any of these conditions are not met, the text is reset
*
* the step size is not enforced
*/
var text = this.$readout.text();
var vmin = this.model.get('min');
var vmax = this.model.get('max');
if (this.model.get("_range")) {
// range case
// ranges can be expressed either "val-val" or "val:val" (+spaces)
var match = this._range_regex.exec(text);
if (match) {
var values = [this._parse_value(match[1]),
this._parse_value(match[2])];
// reject input where NaN or lower > upper
if (isNaN(values[0]) ||
isNaN(values[1]) ||
(values[0] > values[1])) {
this.$readout.text(this.model.get('value').join('-'));
} else {
// clamp to range
values = [Math.max(Math.min(values[0], vmax), vmin),
Math.max(Math.min(values[1], vmax), vmin)];
if ((values[0] != this.model.get('value')[0]) ||
(values[1] != this.model.get('value')[1])) {
this.$readout.text(values.join('-'));
this.model.set('value', values, {updated_view: this});
this.touch();
} else {
this.$readout.text(this.model.get('value').join('-'));
}
}
} else {
this.$readout.text(this.model.get('value').join('-'));
}
} else {
// single value case
var value = this._parse_value(text);
if (isNaN(value)) {
this.$readout.text(this.model.get('value'));
} else {
value = Math.max(Math.min(value, vmax), vmin);
if (value != this.model.get('value')) {
this.$readout.text(value);
this.model.set('value', value, {updated_view: this});
this.touch();
} else {
this.$readout.text(this.model.get('value'));
}
}
}
},
_parse_value: parseInt,
_range_regex: /^\s*([+-]?\d+)\s*[-:]\s*([+-]?\d+)/,
handleSliderChange: function(e, ui) {
/**
* Called when the slider value is changed.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
var actual_value;
if (this.model.get("_range")) {
actual_value = ui.values.map(this._validate_slide_value);
this.$readout.text(actual_value.join("-"));
} else {
actual_value = this._validate_slide_value(ui.value);
this.$readout.text(actual_value);
}
this.model.set('value', actual_value, {updated_view: this});
this.touch();
},
_validate_slide_value: function(x) {
/**
* Validate the value of the slider before sending it to the back-end
* and applying it to the other views on the page.
*
* Double bit-wise not truncates the decimel (int cast).
*/
return ~~x;
},
});
var IntTextView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-numeric-text');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$textbox = $('<input type="text" />')
.addClass('form-control')
.addClass('widget-numeric-text')
.appendTo(this.$el);
this.update(); // Set defaults.
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
var value = this.model.get('value');
if (this._parse_value(this.$textbox.val()) != value) {
this.$textbox.val(value);
}
if (this.model.get('disabled')) {
this.$textbox.attr('disabled','disabled');
} else {
this.$textbox.removeAttr('disabled');
}
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return IntTextView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$textbox.css(name, value);
}
},
events: {
// Dictionary of events and their handlers.
"keyup input" : "handleChanging",
"paste input" : "handleChanging",
"cut input" : "handleChanging",
// Fires only when control is validated or looses focus.
"change input" : "handleChanged"
},
handleChanging: function(e) {
/**
* Handles and validates user input.
*
* Try to parse value as a int.
*/
var numericalValue = 0;
var trimmed = e.target.value.trim();
if (trimmed === '') {
return;
} else {
if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
numericalValue = this._parse_value(e.target.value);
}
}
// If parse failed, reset value to value stored in model.
if (isNaN(numericalValue)) {
e.target.value = this.model.get('value');
} else if (!isNaN(numericalValue)) {
if (this.model.get('max') !== undefined) {
numericalValue = Math.min(this.model.get('max'), numericalValue);
}
if (this.model.get('min') !== undefined) {
numericalValue = Math.max(this.model.get('min'), numericalValue);
}
// Apply the value if it has changed.
if (numericalValue != this.model.get('value')) {
// Calling model.set will trigger all of the other views of the
// model to update.
this.model.set('value', numericalValue, {updated_view: this});
this.touch();
}
}
},
handleChanged: function(e) {
/**
* Applies validated input.
*/
if (e.target.value.trim() === '' || e.target.value !== this.model.get('value')) {
e.target.value = this.model.get('value');
}
},
_parse_value: parseInt
});
var ProgressView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-progress');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$progress = $('<div />')
.addClass('progress')
.addClass('widget-progress')
.appendTo(this.$el);
this.$bar = $('<div />')
.addClass('progress-bar')
.css('width', '50%')
.appendTo(this.$progress);
this.update(); // Set defaults.
this.model.on('change:bar_style', function(model, value) {
this.update_bar_style();
}, this);
this.update_bar_style('');
},
update : function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
var value = this.model.get('value');
var max = this.model.get('max');
var min = this.model.get('min');
var percent = 100.0 * (value - min) / (max - min);
this.$bar.css('width', percent + '%');
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
return ProgressView.__super__.update.apply(this);
},
update_bar_style: function(previous_trait_value) {
var class_map = {
success: ['progress-bar-success'],
info: ['progress-bar-info'],
warning: ['progress-bar-warning'],
danger: ['progress-bar-danger']
};
this.update_mapped_classes(class_map, 'bar_style', previous_trait_value, this.$bar);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name.substring(0, 6) == 'border' || name == 'width' ||
name == 'height' || name == 'background' || name == 'margin' ||
name == 'padding') {
this.$progress.css(name, value);
} else if (name == 'color') {
this.$bar.css('background', value);
} else if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$bar.css(name, value);
}
},
});
return {
'IntSliderView': IntSliderView,
'IntTextView': IntTextView,
'ProgressView': ProgressView,
};
});

@ -1,86 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
], function(widget, $){
var LinkModel = widget.WidgetModel.extend({
initialize: function() {
this.on("change:widgets", function(model, value, options) {
this.update_bindings(model.previous("widgets") || [], value);
this.update_value(this.get("widgets")[0]);
}, this);
this.once("destroy", function(model, collection, options) {
this.update_bindings(this.get("widgets"), []);
}, this);
},
update_bindings: function(oldlist, newlist) {
var that = this;
_.each(oldlist, function(elt) {elt[0].off("change:" + elt[1], null, that);});
_.each(newlist, function(elt) {elt[0].on("change:" + elt[1],
function(model, value, options) {
that.update_value(elt);
}, that);
// TODO: register for any destruction handlers
// to take an item out of the list
});
},
update_value: function(elt) {
if (this.updating) {return;}
var model = elt[0];
var attr = elt[1];
var new_value = model.get(attr);
this.updating = true;
_.each(_.without(this.get("widgets"), elt),
function(element, index, list) {
if (element[0]) {
element[0].set(element[1], new_value);
element[0].save_changes();
}
}, this);
this.updating = false;
},
});
var DirectionalLinkModel = widget.WidgetModel.extend({
initialize: function() {
this.on("change", this.update_bindings, this);
this.once("destroy", function() {
if (this.source) {
this.source[0].off("change:" + this.source[1], null, this);
}
}, this);
},
update_bindings: function() {
if (this.source) {
this.source[0].off("change:" + this.source[1], null, this);
}
this.source = this.get("source");
if (this.source) {
this.source[0].on("change:" + this.source[1], function() { this.update_value(this.source); }, this);
this.update_value(this.source);
}
},
update_value: function(elt) {
if (this.updating) {return;}
var model = elt[0];
var attr = elt[1];
var new_value = model.get(attr);
this.updating = true;
_.each(this.get("targets"),
function(element, index, list) {
if (element[0]) {
element[0].set(element[1], new_value);
element[0].save_changes();
}
}, this);
this.updating = false;
},
});
return {
"LinkModel": LinkModel,
"DirectionalLinkModel": DirectionalLinkModel,
}
});

@ -1,64 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
'notebook/js/outputarea',
], function(widget, $, outputarea) {
'use strict';
var OutputView = widget.DOMWidgetView.extend({
/**
* Public constructor
*/
initialize: function (parameters) {
OutputView.__super__.initialize.apply(this, [parameters]);
this.model.on('msg:custom', this._handle_route_msg, this);
},
/**
* Called when view is rendered.
*/
render: function(){
this.output_area = new outputarea.OutputArea({
selector: this.$el,
prompt_area: false,
events: this.model.widget_manager.notebook.events,
keyboard_manager: this.model.widget_manager.keyboard_manager });
// Make output area reactive.
var that = this;
this.output_area.element.on('changed', function() {
that.model.set('contents', that.output_area.element.html());
});
this.model.on('change:contents', function(){
var html = this.model.get('contents');
if (this.output_area.element.html() != html) {
this.output_area.element.html(html);
}
}, this);
// Set initial contents.
this.output_area.element.html(this.model.get('contents'));
},
/**
* Handles re-routed iopub messages.
*/
_handle_route_msg: function(msg) {
if (msg) {
var msg_type = msg.msg_type;
if (msg_type=='clear_output') {
this.output_area.handle_clear_output(msg);
} else {
this.output_area.handle_output(msg);
}
}
},
});
return {
'OutputView': OutputView,
};
});

@ -1,592 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"base/js/utils",
"jquery",
"underscore",
"bootstrap",
], function(widget, utils, $, _){
var DropdownView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-dropdown');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$buttongroup = $('<div />')
.addClass('widget_item')
.addClass('btn-group')
.appendTo(this.$el);
this.$droplabel = $('<button />')
.addClass('btn btn-default')
.addClass('widget-combo-btn')
.html("&nbsp;")
.appendTo(this.$buttongroup);
this.$dropbutton = $('<button />')
.addClass('btn btn-default')
.addClass('dropdown-toggle')
.addClass('widget-combo-carrot-btn')
.attr('data-toggle', 'dropdown')
.append($('<span />').addClass("caret"))
.appendTo(this.$buttongroup);
this.$droplist = $('<ul />')
.addClass('dropdown-menu')
.appendTo(this.$buttongroup);
this.model.on('change:button_style', function(model, value) {
this.update_button_style();
}, this);
this.update_button_style('');
// Set defaults.
this.update();
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
var selected_item_text = this.model.get('selected_label');
if (selected_item_text.trim().length === 0) {
this.$droplabel.html("&nbsp;");
} else {
this.$droplabel.text(selected_item_text);
}
var items = this.model.get('_options_labels');
var $replace_droplist = $('<ul />')
.addClass('dropdown-menu');
// Copy the style
$replace_droplist.attr('style', this.$droplist.attr('style'));
var that = this;
_.each(items, function(item, i) {
var item_button = $('<a href="#"/>')
.text(item)
.on('click', $.proxy(that.handle_click, that));
$replace_droplist.append($('<li />').append(item_button));
});
this.$droplist.replaceWith($replace_droplist);
this.$droplist.remove();
this.$droplist = $replace_droplist;
if (this.model.get('disabled')) {
this.$buttongroup.attr('disabled','disabled');
this.$droplabel.attr('disabled','disabled');
this.$dropbutton.attr('disabled','disabled');
this.$droplist.attr('disabled','disabled');
} else {
this.$buttongroup.removeAttr('disabled');
this.$droplabel.removeAttr('disabled');
this.$dropbutton.removeAttr('disabled');
this.$droplist.removeAttr('disabled');
}
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return DropdownView.__super__.update.apply(this);
},
update_button_style: function(previous_trait_value) {
var class_map = {
primary: ['btn-primary'],
success: ['btn-success'],
info: ['btn-info'],
warning: ['btn-warning'],
danger: ['btn-danger']
};
this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
this.$droplabel.css(name, value);
this.$dropbutton.css(name, value);
this.$droplist.css(name, value);
} else if (name == 'width') {
this.$droplist.css(name, value);
this.$droplabel.css(name, value);
} else if (name == 'padding') {
this.$droplist.css(name, value);
this.$buttongroup.css(name, value);
} else if (name == 'margin') {
this.$buttongroup.css(name, value);
} else if (name == 'height') {
this.$droplabel.css(name, value);
this.$dropbutton.css(name, value);
} else if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$droplist.css(name, value);
this.$droplabel.css(name, value);
}
},
handle_click: function (e) {
/**
* Handle when a value is clicked.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('selected_label', $(e.target).text(), {updated_view: this});
this.touch();
// Manually hide the droplist.
e.stopPropagation();
e.preventDefault();
this.$buttongroup.removeClass('open');
},
});
var RadioButtonsView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-radio');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$container = $('<div />')
.appendTo(this.$el)
.addClass('widget-radio-box');
this.update();
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
// Add missing items to the DOM.
var items = this.model.get('_options_labels');
var disabled = this.model.get('disabled');
var that = this;
_.each(items, function(item, index) {
var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
if (that.$el.find(item_query).length === 0) {
var $label = $('<label />')
.addClass('radio')
.text(item)
.appendTo(that.$container);
$('<input />')
.attr('type', 'radio')
.addClass(that.model)
.val(item)
.attr('data-value', encodeURIComponent(item))
.prependTo($label)
.on('click', $.proxy(that.handle_click, that));
}
var $item_element = that.$container.find(item_query);
if (that.model.get('selected_label') == item) {
$item_element.prop('checked', true);
} else {
$item_element.prop('checked', false);
}
$item_element.prop('disabled', disabled);
});
// Remove items that no longer exist.
this.$container.find('input').each(function(i, obj) {
var value = $(obj).val();
var found = false;
_.each(items, function(item, index) {
if (item == value) {
found = true;
return false;
}
});
if (!found) {
$(obj).parent().remove();
}
});
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.$label.text(description);
this.typeset(this.$label, description);
this.$label.show();
}
}
return RadioButtonsView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$container.css(name, value);
}
},
handle_click: function (e) {
/**
* Handle when a value is clicked.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('selected_label', $(e.target).val(), {updated_view: this});
this.touch();
},
});
var ToggleButtonsView = widget.DOMWidgetView.extend({
initialize: function() {
this._css_state = {};
ToggleButtonsView.__super__.initialize.apply(this, arguments);
},
render: function() {
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-toggle-buttons');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$buttongroup = $('<div />')
.addClass('btn-group')
.appendTo(this.$el);
this.model.on('change:button_style', function(model, value) {
this.update_button_style();
}, this);
this.update_button_style('');
this.update();
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
// Add missing items to the DOM.
var items = this.model.get('_options_labels');
var icons = this.model.get('icons');
var previous_icons = this.model.previous('icons') || [];
var disabled = this.model.get('disabled');
var that = this;
var item_html;
_.each(items, function(item, index) {
if (item.trim().length === 0 && (!icons[index] ||
icons[index].trim().length === 0)) {
item_html = "&nbsp;";
} else {
item_html = utils.escape_html(item);
}
var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
var $item_element = that.$buttongroup.find(item_query);
var $icon_element = $item_element.find('.fa');
if (!$item_element.length) {
$item_element = $('<button/>')
.attr('type', 'button')
.addClass('btn btn-default')
.html(item_html)
.appendTo(that.$buttongroup)
.attr('data-value', encodeURIComponent(item))
.attr('data-toggle', 'tooltip')
.attr('value', item)
.on('click', $.proxy(that.handle_click, that));
that.update_style_traits($item_element);
$icon_element = $('<i class="fa"></i>').prependTo($item_element);
}
if (that.model.get('selected_label') == item) {
$item_element.addClass('active');
} else {
$item_element.removeClass('active');
}
$item_element.prop('disabled', disabled);
$item_element.attr('title', that.model.get('tooltips')[index]);
$icon_element
.removeClass(previous_icons[index])
.addClass(icons[index]);
});
// Remove items that no longer exist.
this.$buttongroup.find('button').each(function(i, obj) {
var value = $(obj).attr('value');
var found = false;
_.each(items, function(item, index) {
if (item == value) {
found = true;
return false;
}
});
if (!found) {
$(obj).remove();
}
});
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.$label.text();
this.typeset(this.$label, description);
this.$label.show();
}
}
return ToggleButtonsView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this._css_state[name] = value;
this.update_style_traits();
}
},
update_style_traits: function(button) {
for (var name in this._css_state) {
if (this._css_state.hasOwnProperty(name)) {
if (name == 'margin') {
this.$buttongroup.css(name, this._css_state[name]);
} else if (name != 'width') {
if (button) {
button.css(name, this._css_state[name]);
} else {
this.$buttongroup.find('button').css(name, this._css_state[name]);
}
}
}
}
},
update_button_style: function(previous_trait_value) {
var class_map = {
primary: ['btn-primary'],
success: ['btn-success'],
info: ['btn-info'],
warning: ['btn-warning'],
danger: ['btn-danger']
};
this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
},
handle_click: function (e) {
/**
* Handle when a value is clicked.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
this.touch();
},
});
var SelectView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-select');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$listbox = $('<select />')
.addClass('widget-listbox form-control')
.attr('size', 6)
.appendTo(this.$el)
.on('change', $.proxy(this.handle_change, this));
this.update();
},
update : function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
// Add missing items to the DOM.
var items = this.model.get('_options_labels');
var that = this;
_.each(items, function(item, index) {
var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
if (that.$listbox.find(item_query).length === 0) {
$('<option />')
.text(item)
.attr('data-value', encodeURIComponent(item))
.attr('selected_label', item)
.on("click", $.proxy(that.handle_click, that))
.appendTo(that.$listbox);
}
});
// Select the correct element
this.$listbox.val(this.model.get('selected_label'));
// Disable listbox if needed
var disabled = this.model.get('disabled');
this.$listbox.prop('disabled', disabled);
// Remove items that no longer exist.
this.$listbox.find('option').each(function(i, obj) {
var value = $(obj).text();
var found = false;
_.each(items, function(item, index) {
if (item == value) {
found = true;
return false;
}
});
if (!found) {
$(obj).remove();
}
});
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return SelectView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$listbox.css(name, value);
}
},
handle_click: function (e) {
/**
* Handle when a new value is clicked.
*/
this.$listbox.val($(e.target).val()).change();
},
handle_change: function (e) {
/**
* Handle when a new value is selected.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('selected_label', this.$listbox.val(), {updated_view: this});
this.touch();
},
});
var SelectMultipleView = SelectView.extend({
render: function(){
/**
* Called when view is rendered.
*/
SelectMultipleView.__super__.render.apply(this);
this.$el.removeClass('widget-select')
.addClass('widget-select-multiple');
this.$listbox.attr('multiple', true)
.on('change', $.proxy(this.handle_change, this));
return this;
},
update: function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
SelectMultipleView.__super__.update.apply(this, arguments);
this.$listbox.val(this.model.get('selected_labels'));
},
handle_click: function(){
/**
* Overload click from select
*
* Apparently it's needed from there for testing purposes,
* but breaks behavior of this.
*/
},
handle_change: function (e) {
/**
* Handle when a new value is selected.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('selected_labels',
(this.$listbox.val() || []).slice(),
{updated_view: this});
this.touch();
},
});
return {
'DropdownView': DropdownView,
'RadioButtonsView': RadioButtonsView,
'ToggleButtonsView': ToggleButtonsView,
'SelectView': SelectView,
'SelectMultipleView': SelectMultipleView,
};
});

@ -1,324 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"base/js/utils",
"jquery",
"bootstrap",
], function(widget, utils, $){
var AccordionView = widget.DOMWidgetView.extend({
initialize: function(){
AccordionView.__super__.initialize.apply(this, arguments);
this.containers = [];
this.model_containers = {};
this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.children_views.update(value);
}, this);
},
render: function(){
/**
* Called when view is rendered.
*/
var guid = 'panel-group' + utils.uuid();
this.$el
.attr('id', guid)
.addClass('panel-group');
this.model.on('change:selected_index', function(model, value, options) {
this.update_selected_index(options);
}, this);
this.model.on('change:_titles', function(model, value, options) {
this.update_titles(options);
}, this);
this.on('displayed', function() {
this.update_titles();
}, this);
this.children_views.update(this.model.get('children'));
},
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
update: function(options) {
this.update_titles();
this.update_selected_index(options);
return TabView.__super__.update.apply(this);
},
update_titles: function() {
/**
* Set tab titles
*/
var titles = this.model.get('_titles');
var that = this;
_.each(titles, function(title, page_index) {
var accordian = that.containers[page_index];
if (accordian !== undefined) {
accordian
.find('.panel-heading')
.find('.accordion-toggle')
.text(title);
}
});
},
update_selected_index: function(options) {
/**
* Only update the selection if the selection wasn't triggered
* by the front-end. It must be triggered by the back-end.
*/
if (options === undefined || options.updated_view != this) {
var old_index = this.model.previous('selected_index');
var new_index = this.model.get('selected_index');
this.containers[old_index].find('.panel-collapse').collapse('hide');
if (0 <= new_index && new_index < this.containers.length) {
this.containers[new_index].find('.panel-collapse').collapse('show');
}
}
},
remove_child_view: function(view) {
/**
* Called when a child is removed from children list.
* TODO: does this handle two different views of the same model as children?
*/
var model = view.model;
var accordion_group = this.model_containers[model.id];
this.containers.splice(accordion_group.container_index, 1);
delete this.model_containers[model.id];
accordion_group.remove();
},
add_child_view: function(model) {
/**
* Called when a child is added to children list.
*/
var index = this.containers.length;
var uuid = utils.uuid();
var accordion_group = $('<div />')
.addClass('panel panel-default')
.appendTo(this.$el);
var accordion_heading = $('<div />')
.addClass('panel-heading')
.appendTo(accordion_group);
var that = this;
var accordion_toggle = $('<a />')
.addClass('accordion-toggle')
.attr('data-toggle', 'collapse')
.attr('data-parent', '#' + this.$el.attr('id'))
.attr('href', '#' + uuid)
.click(function(evt){
// Calling model.set will trigger all of the other views of the
// model to update.
that.model.set("selected_index", index, {updated_view: that});
that.touch();
})
.text('Page ' + index)
.appendTo(accordion_heading);
var accordion_body = $('<div />', {id: uuid})
.addClass('panel-collapse collapse')
.appendTo(accordion_group);
var accordion_inner = $('<div />')
.addClass('panel-body')
.appendTo(accordion_body);
var container_index = this.containers.push(accordion_group) - 1;
accordion_group.container_index = container_index;
this.model_containers[model.id] = accordion_group;
var dummy = $('<div/>');
accordion_inner.append(dummy);
return this.create_child_view(model).then(function(view) {
dummy.replaceWith(view.$el);
that.update();
that.update_titles();
// Trigger the displayed event of the child view.
that.after_displayed(function() {
view.trigger('displayed');
});
return view;
}).catch(utils.reject("Couldn't add child view to box", true));
},
remove: function() {
/**
* We remove this widget before removing the children as an optimization
* we want to remove the entire container from the DOM first before
* removing each individual child separately.
*/
AccordionView.__super__.remove.apply(this, arguments);
this.children_views.remove();
},
});
var TabView = widget.DOMWidgetView.extend({
initialize: function() {
/**
* Public constructor.
*/
TabView.__super__.initialize.apply(this, arguments);
this.containers = [];
this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
this.listenTo(this.model, 'change:children', function(model, value) {
this.children_views.update(value);
}, this);
},
render: function(){
/**
* Called when view is rendered.
*/
var uuid = 'tabs'+utils.uuid();
this.$tabs = $('<div />', {id: uuid})
.addClass('nav')
.addClass('nav-tabs')
.appendTo(this.$el);
this.$tab_contents = $('<div />', {id: uuid + 'Content'})
.addClass('tab-content')
.appendTo(this.$el);
this.children_views.update(this.model.get('children'));
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$tabs.css(name, value);
}
},
remove_child_view: function(view) {
/**
* Called when a child is removed from children list.
*/
this.containers.splice(view.parent_tab.tab_text_index, 1);
view.parent_tab.remove();
view.parent_container.remove();
view.remove();
},
add_child_view: function(model) {
/**
* Called when a child is added to children list.
*/
var index = this.containers.length;
var uuid = utils.uuid();
var that = this;
var tab = $('<li />')
.css('list-style-type', 'none')
.appendTo(this.$tabs);
var tab_text = $('<a />')
.attr('href', '#' + uuid)
.attr('data-toggle', 'tab')
.text('Page ' + index)
.appendTo(tab)
.click(function (e) {
// Calling model.set will trigger all of the other views of the
// model to update.
that.model.set("selected_index", index, {updated_view: that});
that.touch();
that.select_page(index);
});
tab.tab_text_index = that.containers.push(tab_text) - 1;
var dummy = $('<div />');
var contents_div = $('<div />', {id: uuid})
.addClass('tab-pane')
.addClass('fade')
.append(dummy)
.appendTo(that.$tab_contents);
this.update();
return this.create_child_view(model).then(function(view) {
dummy.replaceWith(view.$el);
view.parent_tab = tab;
view.parent_container = contents_div;
// Trigger the displayed event of the child view.
that.after_displayed(function() {
view.trigger('displayed');
that.update();
});
return view;
}).catch(utils.reject("Couldn't add child view to box", true));
},
update: function(options) {
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
this.update_titles();
this.update_selected_index(options);
return TabView.__super__.update.apply(this);
},
/**
* Updates the tab page titles.
*/
update_titles: function() {
var titles = this.model.get('_titles');
var that = this;
_.each(titles, function(title, page_index) {
var tab_text = that.containers[page_index];
if (tab_text !== undefined) {
tab_text.text(title);
}
});
},
/**
* Updates the tab page titles.
*/
update_selected_index: function(options) {
if (options === undefined || options.updated_view != this) {
var selected_index = this.model.get('selected_index');
if (0 <= selected_index && selected_index < this.containers.length) {
this.select_page(selected_index);
}
}
},
select_page: function(index) {
/**
* Select a page.
*/
this.$tabs.find('li')
.removeClass('active');
this.containers[index].tab('show');
},
remove: function() {
/**
* We remove this widget before removing the children as an optimization
* we want to remove the entire container from the DOM first before
* removing each individual child separately.
*/
TabView.__super__.remove.apply(this, arguments);
this.children_views.remove();
},
});
return {
'AccordionView': AccordionView,
'TabView': TabView,
};
});

@ -1,288 +0,0 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define([
"nbextensions/widgets/widgets/js/widget",
"jquery",
"bootstrap",
], function(widget, $){
var HTMLView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.update(); // Set defaults.
},
update : function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
this.$el.html(this.model.get('value')); // CAUTION! .html(...) CALL MANDITORY!!!
return HTMLView.__super__.update.apply(this);
},
});
var LatexView = widget.DOMWidgetView.extend({
render : function(){
/**
* Called when view is rendered.
*/
this.update(); // Set defaults.
},
update : function(){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
this.typeset(this.$el, this.model.get('value'));
return LatexView.__super__.update.apply(this);
},
});
var TextareaView = widget.DOMWidgetView.extend({
render: function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-textarea');
this.$label = $('<div />')
.appendTo(this.$el)
.addClass('widget-label')
.hide();
this.$textbox = $('<textarea />')
.attr('rows', 5)
.addClass('widget-text form-control')
.appendTo(this.$el);
this.update(); // Set defaults.
this.model.on('msg:custom', $.proxy(this._handle_textarea_msg, this));
this.model.on('change:placeholder', function(model, value, options) {
this.update_placeholder(value);
}, this);
this.update_placeholder();
},
_handle_textarea_msg: function (content){
/**
* Handle when a custom msg is recieved from the back-end.
*/
if (content.method == "scroll_to_bottom") {
this.scroll_to_bottom();
}
},
update_placeholder: function(value) {
if (!value) {
value = this.model.get('placeholder');
}
this.$textbox.attr('placeholder', value);
},
scroll_to_bottom: function (){
/**
* Scroll the text-area view to the bottom.
*/
this.$textbox.scrollTop(this.$textbox[0].scrollHeight);
},
update: function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
this.$textbox.val(this.model.get('value'));
var disabled = this.model.get('disabled');
this.$textbox.prop('disabled', disabled);
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return TextareaView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$textbox.css(name, value);
}
},
events: {
// Dictionary of events and their handlers.
"keyup textarea" : "handleChanging",
"paste textarea" : "handleChanging",
"cut textarea" : "handleChanging"
},
handleChanging: function(e) {
/**
* Handles and validates user input.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('value', e.target.value, {updated_view: this});
this.touch();
},
});
var TextView = widget.DOMWidgetView.extend({
render: function(){
/**
* Called when view is rendered.
*/
this.$el
.addClass('widget-hbox widget-text');
this.$label = $('<div />')
.addClass('widget-label')
.appendTo(this.$el)
.hide();
this.$textbox = $('<input type="text" />')
.addClass('input')
.addClass('widget-text form-control')
.appendTo(this.$el);
this.update(); // Set defaults.
this.model.on('change:placeholder', function(model, value, options) {
this.update_placeholder(value);
}, this);
this.update_placeholder();
},
update_placeholder: function(value) {
if (!value) {
value = this.model.get('placeholder');
}
this.$textbox.attr('placeholder', value);
},
update: function(options){
/**
* Update the contents of this view
*
* Called when the model is changed. The model may have been
* changed by another view or by a state update from the back-end.
*/
if (options === undefined || options.updated_view != this) {
if (this.$textbox.val() != this.model.get('value')) {
this.$textbox.val(this.model.get('value'));
}
var disabled = this.model.get('disabled');
this.$textbox.prop('disabled', disabled);
var description = this.model.get('description');
if (description.length === 0) {
this.$label.hide();
} else {
this.typeset(this.$label, description);
this.$label.show();
}
}
return TextView.__super__.update.apply(this);
},
update_attr: function(name, value) {
/**
* Set a css attr of the widget view.
*/
if (name == 'padding' || name == 'margin') {
this.$el.css(name, value);
} else {
this.$textbox.css(name, value);
}
},
events: {
// Dictionary of events and their handlers.
"keyup input" : "handleChanging",
"paste input" : "handleChanging",
"cut input" : "handleChanging",
"keypress input" : "handleKeypress",
"blur input" : "handleBlur",
"focusout input" : "handleFocusOut"
},
handleChanging: function(e) {
/**
* Handles user input.
*
* Calling model.set will trigger all of the other views of the
* model to update.
*/
this.model.set('value', e.target.value, {updated_view: this});
this.touch();
},
handleKeypress: function(e) {
/**
* Handles text submition
*/
if (e.keyCode == 13) { // Return key
this.send({event: 'submit'});
e.stopPropagation();
e.preventDefault();
return false;
}
},
handleBlur: function(e) {
/**
* Prevent a blur from firing if the blur was not user intended.
* This is a workaround for the return-key focus loss bug.
* TODO: Is the original bug actually a fault of the keyboard
* manager?
*/
if (e.relatedTarget === null) {
e.stopPropagation();
e.preventDefault();
return false;
}
},
handleFocusOut: function(e) {
/**
* Prevent a blur from firing if the blur was not user intended.
* This is a workaround for the return-key focus loss bug.
*/
if (e.relatedTarget === null) {
e.stopPropagation();
e.preventDefault();
return false;
}
},
});
return {
'HTMLView': HTMLView,
'LatexView': LatexView,
'TextareaView': TextareaView,
'TextView': TextView,
};
});

@ -1,283 +0,0 @@
@widget-width: 350px;
@widget-width-short: 150px;
// Pad interact widgets by default.
.widget-interact {
>div, >input {
padding: 2.5px;
}
}
.widget-area {
/*
LESS file that styles IPython notebook widgets and the area they sit in.
The widget area typically looks something like this:
+------------------------------------------+
| widget-area |
| +--------+---------------------------+ |
| | prompt | widget-subarea | |
| | | +--------+ +--------+ | |
| | | | widget | | widget | | |
| | | +--------+ +--------+ | |
| +--------+---------------------------+ |
+------------------------------------------+
*/
page-break-inside : avoid;
.hbox();
.widget-subarea {
padding : 0.44em 0.4em 0.4em 1px;
margin-left : 6px;
.border-box-sizing();
.vbox();
.box-flex2();
.align-start();
}
&.connection-problems .prompt:after {
content: @fa-var-chain-broken;
font-family: 'FontAwesome';
color: @brand-danger;
font-size: @notebook_font_size;
top: 3px;
padding: 3px;
}
}
/* THE CLASSES BELOW CAN APPEAR ANYWHERE IN THE DOM (POSSIBLEY OUTSIDE OF
THE WIDGET AREA). */
.slide-track {
/* Slider Track */
border : 1px solid #CCCCCC;
background : #FFFFFF;
.corner-all(); /* Round the corners of the slide track */
}
.widget-hslider {
/* Horizontal jQuery Slider
Both the horizontal and vertical versions of the slider are characterized
by a styled div that contains an invisible jQuery slide div which
contains a visible slider handle div. This is requred so we can control
how the slider is drawn and 'fix' the issue where the slide handle
doesn't stop at the end of the slide.
Both horizontal and vertical sliders have this div nesting:
+------------------------------------------+
| widget-(h/v)slider |
| +--------+---------------------------+ |
| | ui-slider | |
| | +------------------+ | |
| | | ui-slider-handle | | |
| | +------------------+ | |
| +--------+---------------------------+ |
+------------------------------------------+
*/
/* Fix the padding of the slide track so the ui-slider is sized
correctly. */
padding-left : 8px;
padding-right : 2px;
overflow : visible;
/* Default size of the slider */
width : @widget-width;
height : 5px;
max-height : 5px;
margin-top : 13px;
margin-bottom: 10px;
/* Style the slider track */
.slide-track();
/* Make the div a flex box (makes FF behave correctly). */
.hbox();
.ui-slider {
/* Inner, invisible slide div */
border : 0px;
background : none;
.hbox();
.box-flex1();
.ui-slider-handle {
width: 12px;
height: 28px;
margin-top: -8px;
border-radius: @border-radius-base;
}
.ui-slider-range {
height : 12px;
margin-top : -4px;
background : @page-backdrop-color;
}
}
}
.widget-vslider {
/* Vertical jQuery Slider */
/* Fix the padding of the slide track so the ui-slider is sized
correctly. */
padding-bottom : 5px;
overflow : visible;
/* Default size of the slider */
width : 5px;
max-width : 5px;
height : 250px;
margin-left : 12px;
/* Style the slider track */
.slide-track();
/* Make the div a flex box (makes FF behave correctly). */
.vbox();
.ui-slider {
/* Inner, invisible slide div */
border : 0px;
background : none;
margin-left : -4px;
margin-top : 5px;
.vbox();
.box-flex1();
.ui-slider-handle {
width : 28px;
height : 12px;
margin-left : -9px;
border-radius: @border-radius-base;
}
.ui-slider-range {
width : 12px;
margin-left : -1px;
background : @page-backdrop-color;
}
}
}
.widget-text {
/* String Textbox - used for TextBoxView and TextAreaView */
width : @widget-width;
margin : 0px;
}
.widget-listbox {
/* Listbox */
width : @widget-width;
margin-bottom : 0px;
}
.widget-numeric-text {
/* Single Line Textbox - used for IntTextView and FloatTextView */
width : @widget-width-short;
margin : 0px;
}
.widget-progress {
/* Progress Bar */
margin-top: 6px;
min-width : @widget-width;
.progress-bar {
/* Disable progress bar animation */
-webkit-transition : none;
-moz-transition : none;
-ms-transition : none;
-o-transition : none;
transition : none;
}
}
.widget-combo-btn {
/* ComboBox Main Button */
/* Subtract 25px to account for the drop arrow button */
min-width : @widget-width-short - 25px;
}
.widget_item .dropdown-menu li a {
color: inherit;
}
.widget-valid {
margin-top: 9px;
margin-bottom: 10px;
margin-left: 3px;
margin-right: 3px;
}
.widget-hbox {
/* Horizontal widgets */
.hbox();
input[type="checkbox"] {
margin-top: 9px;
margin-bottom: 10px;
}
.widget-label {
/* Horizontal Label */
min-width : 10ex;
padding-right : 8px;
padding-top : 5px;
text-align : right;
vertical-align : text-top;
}
.widget-readout {
padding-left : 8px;
padding-top : 5px;
text-align : left;
vertical-align : text-top;
}
}
.widget-vbox {
/* Vertical widgets */
.vbox();
.widget-label {
/* Vertical Label */
padding-bottom : 5px;
text-align : center;
vertical-align : text-bottom;
}
.widget-readout {
/* Vertical Label */
padding-top : 5px;
text-align : center;
vertical-align : text-top;
}
}
.widget-box {
/* Box */
.border-box-sizing();
.align-start();
}
.widget-radio-box {
/* Contains RadioButtonsWidget */
.vbox();
.border-box-sizing();
padding-top: 4px;
label {
margin-top: 0px;
margin-left: 20px;
}
}

@ -1,94 +0,0 @@
// Test the widget manager.
casper.notebook_test(function () {
var index;
this.then(function () {
// Check if the WidgetManager class is defined.
this.test.assert(this.evaluate(function() {
return IPython.WidgetManager !== undefined;
}), 'WidgetManager class is defined');
// Check if the widget manager has been instantiated.
this.test.assert(this.evaluate(function() {
return IPython.notebook.kernel.widget_manager !== undefined;
}), 'Notebook widget manager instantiated');
// Try creating a widget from Javascript.
this.evaluate(function() {
IPython.notebook.kernel.widget_manager.create_model({
model_name: 'WidgetModel',
widget_class: 'jupyter_notebook.widgets.widget_int.IntSlider'})
.then(function(model) {
console.log('Create success!', model);
window.slider_id = model.id;
}, function(error) { console.log(error); });
});
});
// Wait for the state to be recieved.
this.waitFor(function check() {
return this.evaluate(function() {
return window.slider_id !== undefined;
});
});
index = this.append_cell(
'from jupyter_notebook.widgets import Widget\n' +
'widget = list(Widget.widgets.values())[0]\n' +
'print(widget.model_id)');
this.execute_cell_then(index, function(index) {
var output = this.get_output_cell(index).text.trim();
var slider_id = this.evaluate(function() { return window.slider_id; });
this.test.assertEquals(output, slider_id, "Widget created from the front-end.");
});
// Widget persistence tests.
index = this.append_cell(
'from jupyter_notebook.widgets import HTML\n' +
'from IPython.display import display\n' +
'display(HTML(value="<div id=\'hello\'></div>"))');
this.execute_cell_then(index, function() {});
index = this.append_cell(
'display(HTML(value="<div id=\'world\'></div>"))');
this.execute_cell_then(index, function() {});
var that = this;
this.then(function() {
// Wait for the widgets to be shown.
that.waitForSelector('#hello', function() {
that.waitForSelector('#world', function() {
that.test.assertExists('#hello', 'Hello HTML widget constructed.');
that.test.assertExists('#world', 'World HTML widget constructed.');
// Save the notebook.
that.evaluate(function() {
IPython.notebook.save_notebook(false).then(function() {
window.was_saved = true;
});
});
that.waitFor(function check() {
return that.evaluate(function() {
return window.was_saved;
});
}, function then() {
// Reload the page
that.reload(function() {
// Wait for the elements to show up again.
that.waitForSelector('#hello', function() {
that.waitForSelector('#world', function() {
that.test.assertExists('#hello', 'Hello HTML widget persisted.');
that.test.assertExists('#world', 'World HTML widget persisted.');
});
});
});
});
});
});
});
});

@ -1,309 +0,0 @@
var xor = function (a, b) {return !a ^ !b;};
var isArray = function (a) {
try {
return Object.toString.call(a) === "[object Array]" || Object.toString.call(a) === "[object RuntimeArray]";
} catch (e) {
return Array.isArray(a);
}
};
var recursive_compare = function(a, b) {
// Recursively compare two objects.
var same = true;
same = same && !xor(a instanceof Object || typeof a == 'object', b instanceof Object || typeof b == 'object');
same = same && !xor(isArray(a), isArray(b));
if (same) {
if (a instanceof Object) {
var key;
for (key in a) {
if (a.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
same = false;
break;
}
}
for (key in b) {
if (b.hasOwnProperty(key) && !recursive_compare(a[key], b[key])) {
same = false;
break;
}
}
} else {
return a === b;
}
}
return same;
};
// Test the widget framework.
casper.notebook_test(function () {
var index;
index = this.append_cell(
['from jupyter_notebook import widgets',
'from IPython.display import display, clear_output',
'print("Success")'].join('\n'));
this.execute_cell_then(index);
this.then(function () {
// Test multi-set, single touch code. First create a custom widget.
this.thenEvaluate(function() {
var MultiSetView = IPython.DOMWidgetView.extend({
render: function(){
this.model.set('a', 1);
this.model.set('b', 2);
this.model.set('c', 3);
this.touch();
},
});
IPython.WidgetManager.register_widget_view('MultiSetView', MultiSetView);
}, {});
});
// Try creating the multiset widget, verify that sets the values correctly.
var multiset = {};
multiset.index = this.append_cell([
'from traitlets import Unicode, CInt',
'class MultiSetWidget(widgets.Widget):',
' _view_name = Unicode("MultiSetView", sync=True)',
' a = CInt(0, sync=True)',
' b = CInt(0, sync=True)',
' c = CInt(0, sync=True)',
' d = CInt(-1, sync=True)', // See if it sends a full state.
' def set_state(self, sync_data):',
' widgets.Widget.set_state(self, sync_data)',
' self.d = len(sync_data)',
'multiset = MultiSetWidget()',
'display(multiset)',
'print(multiset.model_id)'].join('\n'));
this.execute_cell_then(multiset.index, function(index) {
multiset.model_id = this.get_output_cell(index).text.trim();
});
this.wait_for_widget(multiset);
index = this.append_cell(
'print("%d%d%d" % (multiset.a, multiset.b, multiset.c))');
this.execute_cell_then(index, function(index) {
this.test.assertEquals(this.get_output_cell(index).text.trim(), '123',
'Multiple model.set calls and one view.touch update state in back-end.');
});
index = this.append_cell(
'print("%d" % (multiset.d))');
this.execute_cell_then(index, function(index) {
this.test.assertEquals(this.get_output_cell(index).text.trim(), '3',
'Multiple model.set calls sent a partial state.');
});
var textbox = {};
throttle_index = this.append_cell([
'import time',
'textbox = widgets.Text()',
'display(textbox)',
'textbox._dom_classes = ["my-throttle-textbox"]',
'def handle_change(name, old, new):',
' display(len(new))',
' time.sleep(0.5)',
'textbox.on_trait_change(handle_change, "value")',
'print(textbox.model_id)'].join('\n'));
this.execute_cell_then(throttle_index, function(index){
textbox.model_id = this.get_output_cell(index).text.trim();
this.test.assert(this.cell_element_exists(index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(index,
'.my-throttle-textbox'), 'Textbox exists.');
// Send 20 characters
this.sendKeys('.my-throttle-textbox input', '12345678901234567890');
});
this.wait_for_widget(textbox);
this.then(function () {
var outputs = this.evaluate(function(i) {
return IPython.notebook.get_cell(i).output_area.outputs;
}, {i : throttle_index});
// Only 4 outputs should have printed, but because of timing, sometimes
// 5 outputs will print. All we need to do is verify num outputs <= 5
// because that is much less than 20.
this.test.assert(outputs.length <= 5, 'Messages throttled.');
// We also need to verify that the last state sent was correct.
var last_state = outputs[outputs.length-1].data['text/plain'];
this.test.assertEquals(last_state, "20", "Last state sent when throttling.");
});
this.thenEvaluate(function() {
define('TestWidget', ['widgets/js/widget', 'base/js/utils', 'underscore'], function(widget, utils, _) {
var floatArray = {
deserialize: function (value, model) {
if (value===null) {return null;}
// DataView -> float64 typed array
return new Float64Array(value.buffer);
},
// serialization automatically handled since the
// attribute is an ArrayBuffer view
};
var floatList = {
deserialize: function (value, model) {
// list of floats -> list of strings
return value.map(function(x) {return x.toString()});
},
serialize: function(value, model) {
// list of strings -> list of floats
return value.map(function(x) {return parseFloat(x);})
}
};
var TestWidgetModel = widget.WidgetModel.extend({}, {
serializers: _.extend({
array_list: floatList,
array_binary: floatArray
}, widget.WidgetModel.serializers)
});
var TestWidgetView = widget.DOMWidgetView.extend({
render: function () {
this.listenTo(this.model, 'msg:custom', this.handle_msg);
},
handle_msg: function(content, buffers) {
this.msg = [content, buffers];
}
});
return {TestWidgetModel: TestWidgetModel, TestWidgetView: TestWidgetView};
});
});
var testwidget = {};
this.append_cell_execute_then([
'from jupyter_notebook import widgets',
'from traitlets import Unicode, Instance, List',
'from IPython.display import display',
'from array import array',
'def _array_to_memoryview(x):',
' if x is None: return None',
' try:',
' y = memoryview(x)',
' except TypeError:',
' # in python 2, arrays do not support the new buffer protocol',
' y = memoryview(buffer(x))',
' return y',
'def _memoryview_to_array(x):',
' if x is None: return None',
' return array("d", x.tobytes())',
'arrays_binary = {',
' "from_json": _memoryview_to_array,',
' "to_json": _array_to_memoryview',
'}',
'',
'def _array_to_list(x):',
' return list(x)',
'def _list_to_array(x):',
' return array("d",x)',
'arrays_list = {',
' "from_json": _list_to_array,',
' "to_json": _array_to_list',
'}',
'',
'class TestWidget(widgets.DOMWidget):',
' _model_module = Unicode("TestWidget", sync=True)',
' _model_name = Unicode("TestWidgetModel", sync=True)',
' _view_module = Unicode("TestWidget", sync=True)',
' _view_name = Unicode("TestWidgetView", sync=True)',
' array_binary = Instance(array, allow_none=True, sync=True, **arrays_binary)',
' array_list = Instance(array, args=("d", [3.0]), allow_none=False, sync=True, **arrays_list)',
' msg = {}',
' def __init__(self, **kwargs):',
' super(widgets.DOMWidget, self).__init__(**kwargs)',
' self.on_msg(self._msg)',
' def _msg(self, _, content, buffers):',
' self.msg = [content, buffers]',
'x=TestWidget()',
'display(x)',
'print(x.model_id)'].join('\n'), function(index){
testwidget.index = index;
testwidget.model_id = this.get_output_cell(index).text.trim();
});
this.wait_for_widget(testwidget);
this.append_cell_execute_then('x.array_list = array("d", [1.5, 2.0, 3.1])');
this.wait_for_widget(testwidget);
this.then(function() {
var result = this.evaluate(function(index) {
var v = IPython.notebook.get_cell(index).widget_views[0];
var result = v.model.get('array_list');
var z = result.slice();
z[0]+="1234";
z[1]+="5678";
v.model.set('array_list', z);
v.touch();
return result;
}, testwidget.index);
this.test.assertEquals(result, ["1.5", "2", "3.1"], "JSON custom serializer kernel -> js");
});
this.assert_output_equals('print(x.array_list.tolist() == [1.51234, 25678.0, 3.1])',
'True', 'JSON custom serializer js -> kernel');
if (this.slimerjs) {
this.append_cell_execute_then("x.array_binary=array('d', [1.5,2.5,5])", function() {
this.evaluate(function(index) {
var v = IPython.notebook.get_cell(index).widget_views[0];
var z = v.model.get('array_binary');
z[0]*=3;
z[1]*=3;
z[2]*=3;
// we set to null so that we recognize the change
// when we set data back to z
v.model.set('array_binary', null);
v.model.set('array_binary', z);
v.touch();
}, textwidget.index);
});
this.wait_for_widget(testwidget);
this.assert_output_equals('x.array_binary.tolist() == [4.5, 7.5, 15.0]',
'True\n', 'Binary custom serializer js -> kernel')
this.append_cell_execute_then('x.send("some content", [memoryview(b"binarycontent"), memoryview("morecontent")])');
this.wait_for_widget(testwidget);
this.then(function() {
var result = this.evaluate(function(index) {
var v = IPython.notebook.get_cell(index).widget_views[0];
var d = new TextDecoder('utf-8');
return {text: v.msg[0],
binary0: d.decode(v.msg[1][0]),
binary1: d.decode(v.msg[1][1])};
}, testwidget.index);
this.test.assertEquals(result, {text: 'some content',
binary0: 'binarycontent',
binary1: 'morecontent'},
"Binary widget messages kernel -> js");
});
this.then(function() {
this.evaluate(function(index) {
var v = IPython.notebook.get_cell(index).widget_views[0];
v.send('content back', [new Uint8Array([1,2,3,4]), new Float64Array([2.1828, 3.14159])])
}, testwidget.index);
});
this.wait_for_widget(testwidget);
this.assert_output_equals([
'all([x.msg[0] == "content back",',
' x.msg[1][0].tolist() == [1,2,3,4],',
' array("d", x.msg[1][1].tobytes()).tolist() == [2.1828, 3.14159]])'].join('\n'),
'True', 'Binary buffers message js -> kernel');
} else {
console.log("skipping binary websocket tests on phantomjs");
}
});

@ -1,92 +0,0 @@
// Test widget bool class
casper.notebook_test(function () {
"use strict";
// Create a checkbox and togglebutton.
var bool_index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'bool_widgets = [widgets.Checkbox(description="Title", value=True),\n' +
' widgets.ToggleButton(description="Title", value=True)]\n' +
'display(bool_widgets[0])\n' +
'display(bool_widgets[1])\n' +
'print("Success")');
this.execute_cell_then(bool_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create bool widget cell executed with correct output.');
});
// Wait for the widgets to actually display.
var widget_checkbox_selector = '.widget-area .widget-subarea .widget-hbox input';
var widget_togglebutton_selector = '.widget-area .widget-subarea button';
this.wait_for_element(bool_index, widget_checkbox_selector);
this.wait_for_element(bool_index, widget_togglebutton_selector);
// Continue the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(bool_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(bool_index,
widget_checkbox_selector),
'Checkbox exists.');
this.test.assert(this.cell_element_function(bool_index,
widget_checkbox_selector, 'prop', ['checked']),
'Checkbox is checked.');
this.test.assert(this.cell_element_exists(bool_index,
'.widget-area .widget-subarea .widget-hbox .widget-label'),
'Checkbox label exists.');
this.test.assert(this.cell_element_function(bool_index,
'.widget-area .widget-subarea .widget-hbox .widget-label', 'html')=="Title",
'Checkbox labeled correctly.');
this.test.assert(this.cell_element_exists(bool_index,
widget_togglebutton_selector),
'Toggle button exists.');
this.test.assert(this.cell_element_function(bool_index,
widget_togglebutton_selector, 'html')=='<i class="fa"></i>Title',
'Toggle button labeled correctly.');
this.test.assert(this.cell_element_function(bool_index,
widget_togglebutton_selector, 'hasClass', ['active']),
'Toggle button is toggled.');
});
// Try changing the state of the widgets programatically.
var index = this.append_cell(
'bool_widgets[0].value = False\n' +
'bool_widgets[1].value = False\n' +
'print("Success")');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Change bool widget value cell executed with correct output.');
this.test.assert(! this.cell_element_function(bool_index,
widget_checkbox_selector, 'prop', ['checked']),
'Checkbox is not checked. (1)');
this.test.assert(! this.cell_element_function(bool_index,
widget_togglebutton_selector, 'hasClass', ['active']),
'Toggle button is not toggled. (1)');
// Try toggling the bool by clicking on the checkbox.
this.cell_element_function(bool_index, widget_checkbox_selector, 'click');
this.test.assert(this.cell_element_function(bool_index,
widget_checkbox_selector, 'prop', ['checked']),
'Checkbox is checked. (2)');
// Try toggling the bool by clicking on the toggle button.
this.cell_element_function(bool_index, widget_togglebutton_selector, 'click');
this.test.assert(this.cell_element_function(bool_index,
widget_togglebutton_selector, 'hasClass', ['active']),
'Toggle button is toggled. (3)');
});
});

@ -1,92 +0,0 @@
// Test container class
casper.notebook_test(function () {
// Create a box widget.
var container_index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'container = widgets.Box()\n' +
'button = widgets.Button()\n'+
'container.children = [button]\n'+
'display(container)\n'+
'container._dom_classes = ["my-test-class"]\n'+
'print("Success")\n');
this.execute_cell_then(container_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create container cell executed with correct output.');
});
// Wait for the widgets to actually display.
var widget_box_selector = '.widget-area .widget-subarea .widget-box';
var widget_box_button_selector = '.widget-area .widget-subarea .widget-box button';
this.wait_for_element(container_index, widget_box_selector);
this.wait_for_element(container_index, widget_box_button_selector);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(container_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(container_index,
widget_box_selector),
'Widget container exists.');
this.test.assert(this.cell_element_exists(container_index,
'.widget-area .widget-subarea .my-test-class'),
'_dom_classes works.');
this.test.assert(this.cell_element_exists(container_index,
widget_box_button_selector),
'Container parent/child relationship works.');
});
index = this.append_cell(
'container.box_style = "success"\n'+
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Set box_style cell executed with correct output.');
this.test.assert(this.cell_element_exists(container_index,
'.widget-box.alert-success'),
'Set box_style works.');
});
index = this.append_cell(
'container._dom_classes = []\n'+
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Remove container class cell executed with correct output.');
this.test.assert(! this.cell_element_exists(container_index,
'.widget-area .widget-subarea .my-test-class'),
'_dom_classes can be used to remove a class.');
});
var boxalone_index = this.append_cell(
'display(button)\n'+
'print("Success")\n');
this.execute_cell_then(boxalone_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Display container child executed with correct output.');
});
// Wait for the widget to actually display.
var widget_button_selector = '.widget-area .widget-subarea button';
this.wait_for_element(boxalone_index, widget_button_selector);
// Continue with the tests.
this.then(function() {
this.test.assert(! this.cell_element_exists(boxalone_index,
widget_box_selector),
'Parent container not displayed.');
this.test.assert(this.cell_element_exists(boxalone_index,
widget_button_selector),
'Child displayed.');
});
});

@ -1,48 +0,0 @@
// Test widget button class
casper.notebook_test(function () {
var button_index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'button = widgets.Button(description="Title")\n' +
'display(button)\n' +
'print("Success")\n' +
'def handle_click(sender):\n' +
' display("Clicked")\n' +
'button.on_click(handle_click)');
this.execute_cell_then(button_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create button cell executed with correct output.');
});
// Wait for the widgets to actually display.
var widget_button_selector = '.widget-area .widget-subarea button';
this.wait_for_element(button_index, widget_button_selector);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(button_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(button_index,
widget_button_selector),
'Widget button exists.');
this.test.assert(this.cell_element_function(button_index,
widget_button_selector, 'html')=='<i class="fa"></i>Title',
'Set button description.');
this.cell_element_function(button_index,
widget_button_selector, 'click');
});
this.wait_for_output(button_index, 1);
this.then(function () {
var warning_text = this.get_output_cell(button_index, 1).text;
this.test.assertNotEquals(warning_text.indexOf('Warning'), -1,
'Importing widgets show a warning');
this.test.assertEquals(this.get_output_cell(button_index, 2).data['text/plain'], "'Clicked'",
'Button click event fires.');
});
});

@ -1,107 +0,0 @@
// Test widget float class
casper.notebook_test(function () {
var float_text = {};
float_text.query = '.widget-area .widget-subarea .my-second-float-text input';
float_text.index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'float_widget = widgets.FloatText()\n' +
'display(float_widget)\n' +
'float_widget._dom_classes = ["my-second-float-text"]\n' +
'print(float_widget.model_id)\n');
this.execute_cell_then(float_text.index, function(index){
float_text.model_id = this.get_output_cell(index).text.trim();
});
// Wait for the widget to actually display.
this.wait_for_element(float_text.index, float_text.query);
// Continue with the tests
this.then(function(){
this.test.assert(this.cell_element_exists(float_text.index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(float_text.index, float_text.query),
'Widget float textbox exists.');
this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
this.sendKeys(float_text.query, '1.05');
});
this.wait_for_widget(float_text);
index = this.append_cell('print(float_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '1.05\n',
'Float textbox value set.');
this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
this.sendKeys(float_text.query, '123456789.0');
});
this.wait_for_widget(float_text);
index = this.append_cell('print(float_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '123456789.0\n',
'Long float textbox value set (probably triggers throttling).');
this.cell_element_function(float_text.index, float_text.query, 'val', ['']);
this.sendKeys(float_text.query, '12hello');
});
this.wait_for_widget(float_text);
index = this.append_cell('print(float_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '12.0\n',
'Invald float textbox value caught and filtered.');
});
var float_text_query = '.widget-area .widget-subarea .widget-numeric-text';
var slider = {};
slider.query = '.widget-area .widget-subarea .slider';
slider.index = this.append_cell(
'floatrange = [widgets.BoundedFloatText(), \n' +
' widgets.FloatSlider()]\n' +
'[display(floatrange[i]) for i in range(2)]\n' +
'print("Success")\n');
this.execute_cell_then(slider.index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create float range cell executed with correct output.');
});
// Wait for the widgets to actually display.
this.wait_for_element(slider.index, slider.query);
this.wait_for_element(slider.index, float_text_query);
this.then(function(){
this.test.assert(this.cell_element_exists(slider.index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(slider.index, slider.query),
'Widget slider exists.');
this.test.assert(this.cell_element_exists(slider.index, float_text_query),
'Widget float textbox exists.');
});
index = this.append_cell(
'for widget in floatrange:\n' +
' widget.max = 50.0\n' +
' widget.min = -50.0\n' +
' widget.value = 25.0\n' +
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Float range properties cell executed with correct output.');
this.test.assert(this.cell_element_exists(slider.index, slider.query),
'Widget slider exists.');
this.test.assert(this.cell_element_function(slider.index, slider.query,
'slider', ['value']) == 25.0,
'Slider set to Python value.');
});
});

@ -1,49 +0,0 @@
// Test image class
casper.notebook_test(function () {
"use strict";
var index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'print("Success")');
this.execute_cell_then(index);
// Get the temporary directory that the test server is running in.
var cwd = '';
index = this.append_cell('!echo $(pwd)');
this.execute_cell_then(index, function(index){
cwd = this.get_output_cell(index).text.trim();
});
var test_jpg = '/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wgARCAABAAEDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAAA//EABUBAQEAAAAAAAAAAAAAAAAAAAME/9oADAMBAAIQAxAAAAECv//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQn//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z';
var image_index = this.append_cell(
'import base64\n' +
'data = base64.b64decode("' + test_jpg + '")\n' +
'image = widgets.Image()\n' +
'image.format = "jpeg"\n' +
'image.value = data\n' +
'image.width = "50px"\n' +
'image.height = "50px"\n' +
'display(image)\n' +
'print("Success")\n');
this.execute_cell_then(image_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create image executed with correct output.');
});
// Wait for the widget to actually display.
var img_selector = '.widget-area .widget-subarea img';
this.wait_for_element(image_index, img_selector);
this.then(function(){
this.test.assert(this.cell_element_exists(image_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(image_index, img_selector), 'Image exists.');
// Verify that the image's base64 data has made it into the DOM.
var img_src = this.cell_element_function(image_index, img_selector, 'attr', ['src']);
this.test.assert(img_src.indexOf(test_jpg) > -1, 'Image src data exists.');
});
});

@ -1,177 +0,0 @@
// Test widget int class
casper.notebook_test(function () {
var int_text = {};
int_text.query = '.widget-area .widget-subarea .my-second-int-text input';
int_text.index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'int_widget = widgets.IntText()\n' +
'display(int_widget)\n' +
'int_widget._dom_classes = ["my-second-int-text"]\n' +
'print(int_widget.model_id)\n');
this.execute_cell_then(int_text.index, function(index){
int_text.model_id = this.get_output_cell(index).text.trim();
});
// Wait for the widget to actually display.
this.wait_for_element(int_text.index, int_text.query);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(int_text.index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(int_text.index, int_text.query),
'Widget int textbox exists.');
this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
this.sendKeys(int_text.query, '1.05');
});
this.wait_for_widget(int_text);
index = this.append_cell('print(int_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '1\n',
'Int textbox value set.');
this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
this.sendKeys(int_text.query, '123456789');
});
this.wait_for_widget(int_text);
index = this.append_cell('print(int_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '123456789\n',
'Long int textbox value set (probably triggers throttling).');
this.cell_element_function(int_text.index, int_text.query, 'val', ['']);
this.sendKeys(int_text.query, '12hello');
});
this.wait_for_widget(int_text);
index = this.append_cell('print(int_widget.value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '12\n',
'Invald int textbox value caught and filtered.');
});
var slider_query = '.widget-area .widget-subarea .slider';
var int_text2 = {};
int_text2.query = '.widget-area .widget-subarea .my-second-num-test-text input';
int_text2.index = this.append_cell(
'intrange = [widgets.BoundedIntTextWidget(),\n' +
' widgets.IntSliderWidget()]\n' +
'[display(intrange[i]) for i in range(2)]\n' +
'intrange[0]._dom_classes = ["my-second-num-test-text"]\n' +
'print(intrange[0].model_id)\n');
this.execute_cell_then(int_text2.index, function(index){
int_text2.model_id = this.get_output_cell(index).text.trim();
});
// Wait for the widgets to actually display.
this.wait_for_element(int_text2.index, int_text2.query);
this.wait_for_element(int_text2.index, slider_query);
// Continue with the tests.
this.then(function(){
this.test.assert(this.cell_element_exists(int_text2.index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(int_text2.index, slider_query),
'Widget slider exists.');
this.test.assert(this.cell_element_exists(int_text2.index, int_text2.query),
'Widget int textbox exists.');
});
index = this.append_cell(
'for widget in intrange:\n' +
' widget.max = 50\n' +
' widget.min = -50\n' +
' widget.value = 25\n' +
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Int range properties cell executed with correct output.');
this.test.assert(this.cell_element_exists(int_text2.index, slider_query),
'Widget slider exists.');
this.test.assert(this.cell_element_function(int_text2.index, slider_query,
'slider', ['value']) == 25,
'Slider set to Python value.');
this.test.assert(this.cell_element_function(int_text2.index, int_text2.query,
'val') == 25, 'Int textbox set to Python value.');
// Clear the int textbox value and then set it to 1 by emulating
// keyboard presses.
this.evaluate(function(q){
var textbox = IPython.notebook.element.find(q);
textbox.val('1');
textbox.trigger('keyup');
}, {q: int_text2.query});
});
this.wait_for_widget(int_text2);
index = this.append_cell('print(intrange[0].value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '1\n',
'Int textbox set int range value');
// Clear the int textbox value and then set it to 120 by emulating
// keyboard presses.
this.evaluate(function(q){
var textbox = IPython.notebook.element.find(q);
textbox.val('120');
textbox.trigger('keyup');
}, {q: int_text2.query});
});
this.wait_for_widget(int_text2);
index = this.append_cell('print(intrange[0].value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '50\n',
'Int textbox value bound');
// Clear the int textbox value and then set it to 'hello world' by
// emulating keyboard presses. 'hello world' should get filtered...
this.evaluate(function(q){
var textbox = IPython.notebook.element.find(q);
textbox.val('hello world');
textbox.trigger('keyup');
}, {q: int_text2.query});
});
this.wait_for_widget(int_text2);
index = this.append_cell('print(intrange[0].value)\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '50\n',
'Invalid int textbox characters ignored');
});
index = this.append_cell(
'a = widgets.IntSlider()\n' +
'display(a)\n' +
'a.max = -1\n' +
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(0, 0, 'Invalid int range max bound does not cause crash.');
}, true);
index = this.append_cell(
'a = widgets.IntSlider()\n' +
'display(a)\n' +
'a.min = 101\n' +
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(0, 0, 'Invalid int range min bound does not cause crash.');
}, true);
});

@ -1,148 +0,0 @@
// Test selection class
casper.notebook_test(function () {
index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'print("Success")');
this.execute_cell_then(index);
var combo_selector = '.widget-area .widget-subarea .widget-hbox .btn-group .widget-combo-btn';
var multibtn_selector = '.widget-area .widget-subarea .widget-hbox.widget-toggle-buttons .btn-group';
var radio_selector = '.widget-area .widget-subarea .widget-hbox .widget-radio-box';
var list_selector = '.widget-area .widget-subarea .widget-hbox .widget-listbox';
var selection_index;
var selection_values = 'abcd';
var check_state = function(context, index, state){
if (0 <= index && index < selection_values.length) {
var multibtn_state = context.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(' + (index + 1) + ')', 'hasClass', ['active']);
var radio_state = context.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(' + (index + 1) + ') input', 'prop', ['checked']);
var list_val = context.cell_element_function(selection_index, list_selector, 'val');
var combo_val = context.cell_element_function(selection_index, combo_selector, 'html');
var val = selection_values.charAt(index);
var list_state = (val == list_val);
var combo_state = (val == combo_val);
return multibtn_state == state &&
radio_state == state &&
list_state == state &&
combo_state == state;
}
return true;
};
var verify_selection = function(context, index){
for (var i = 0; i < selection_values.length; i++) {
if (!check_state(context, i, i==index)) {
return false;
}
}
return true;
};
//values=["' + selection_values + '"[i] for i in range(4)]
selection_index = this.append_cell(
'options=["' + selection_values + '"[i] for i in range(4)]\n' +
'selection = [widgets.Dropdown(options=options),\n' +
' widgets.ToggleButtons(options=options),\n' +
' widgets.RadioButtons(options=options),\n' +
' widgets.Select(options=options)]\n' +
'[display(selection[i]) for i in range(4)]\n' +
'for widget in selection:\n' +
' def handle_change(name,old,new):\n' +
' for other_widget in selection:\n' +
' other_widget.value = new\n' +
' widget.on_trait_change(handle_change, "value")\n' +
'print("Success")\n');
this.execute_cell_then(selection_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create selection cell executed with correct output.');
});
// Wait for the widgets to actually display.
this.wait_for_element(selection_index, combo_selector);
this.wait_for_element(selection_index, multibtn_selector);
this.wait_for_element(selection_index, radio_selector);
this.wait_for_element(selection_index, list_selector);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(selection_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(selection_index, combo_selector),
'Widget combobox exists.');
this.test.assert(this.cell_element_exists(selection_index, multibtn_selector),
'Widget multibutton exists.');
this.test.assert(this.cell_element_exists(selection_index, radio_selector),
'Widget radio buttons exists.');
this.test.assert(this.cell_element_exists(selection_index, list_selector),
'Widget list exists.');
// Verify that no items are selected.
this.test.assert(verify_selection(this, 0), 'Default first item selected.');
});
index = this.append_cell(
'for widget in selection:\n' +
' widget.value = "a"\n' +
'print("Success")\n');
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Python select item executed with correct output.');
// Verify that the first item is selected.
this.test.assert(verify_selection(this, 0), 'Python selected');
// Verify that selecting a radio button updates all of the others.
this.cell_element_function(selection_index, radio_selector + ' .radio:nth-child(2) input', 'click');
});
this.wait_for_idle();
this.then(function () {
this.test.assert(verify_selection(this, 1), 'Radio button selection updated view states correctly.');
// Verify that selecting a list option updates all of the others.
this.cell_element_function(selection_index, list_selector + ' option:nth-child(3)', 'click');
});
this.wait_for_idle();
this.then(function () {
this.test.assert(verify_selection(this, 2), 'List selection updated view states correctly.');
// Verify that selecting a multibutton option updates all of the others.
// Bootstrap3 has changed the toggle button group behavior. Two clicks
// are required to actually select an item.
this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
this.cell_element_function(selection_index, multibtn_selector + ' .btn:nth-child(4)', 'click');
});
this.wait_for_idle();
this.then(function () {
this.test.assert(verify_selection(this, 3), 'Multibutton selection updated view states correctly.');
// Verify that selecting a combobox option updates all of the others.
this.cell_element_function(selection_index, '.widget-area .widget-subarea .widget-hbox .btn-group ul.dropdown-menu li:nth-child(3) a', 'click');
});
this.wait_for_idle();
this.then(function () {
this.test.assert(verify_selection(this, 2), 'Combobox selection updated view states correctly.');
});
this.wait_for_idle();
index = this.append_cell(
'from copy import copy\n' +
'for widget in selection:\n' +
' d = copy(widget.options)\n' +
' d.append("z")\n' +
' widget.options = d\n' +
'selection[0].value = "z"');
this.execute_cell_then(index, function(index){
// Verify that selecting a combobox option updates all of the others.
this.test.assert(verify_selection(this, 4), 'Item added to selection widget.');
});
});

@ -1,120 +0,0 @@
// Test multicontainer class
casper.notebook_test(function () {
index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'print("Success")');
this.execute_cell_then(index);
// Test tab view
var multicontainer1_query = '.widget-area .widget-subarea div div.nav-tabs';
var multicontainer1_index = this.append_cell(
'multicontainer = widgets.Tab()\n' +
'page1 = widgets.Text()\n' +
'page2 = widgets.Text()\n' +
'page3 = widgets.Text()\n' +
'multicontainer.children = [page1, page2, page3]\n' +
'display(multicontainer)\n' +
'multicontainer.selected_index = 0\n' +
'print("Success")\n');
this.execute_cell_then(multicontainer1_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create multicontainer cell executed with correct output. (1)');
});
// Wait for the widget to actually display.
this.wait_for_element(multicontainer1_index, multicontainer1_query);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(multicontainer1_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(multicontainer1_index, multicontainer1_query),
'Widget tab list exists.');
// JQuery selector is 1 based
this.click(multicontainer1_query + ' li:nth-child(2) a');
});
this.wait_for_idle();
index = this.append_cell(
'print(multicontainer.selected_index)\n' +
'multicontainer.selected_index = 2'); // 0 based
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
'selected_index property updated with tab change.');
// JQuery selector is 1 based
this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(1)', 'hasClass', ['active']),
"Tab 1 is not selected.");
this.test.assert(!this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(2)', 'hasClass', ['active']),
"Tab 2 is not selected.");
this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query + ' li:nth-child(3)', 'hasClass', ['active']),
"Tab 3 is selected.");
});
index = this.append_cell('multicontainer.set_title(1, "hello")\nprint("Success")'); // 0 based
this.execute_cell_then(index, function(index){
this.test.assert(this.cell_element_function(multicontainer1_index, multicontainer1_query +
' li:nth-child(2) a', 'html') == 'hello',
'Tab page title set (after display).');
});
// Test accordion view
var multicontainer2_query = '.widget-area .widget-subarea .panel-group';
var multicontainer2_index = this.append_cell(
'multicontainer = widgets.Accordion()\n' +
'page1 = widgets.Text()\n' +
'page2 = widgets.Text()\n' +
'page3 = widgets.Text()\n' +
'multicontainer.children = [page1, page2, page3]\n' +
'multicontainer.set_title(2, "good")\n' +
'display(multicontainer)\n' +
'multicontainer.selected_index = 0\n' +
'print("Success")\n');
this.execute_cell_then(multicontainer2_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create multicontainer cell executed with correct output. (2)');
});
// Wait for the widget to actually display.
this.wait_for_element(multicontainer2_index, multicontainer2_query);
// Continue with the tests.
this.then(function() {
this.test.assert(this.cell_element_exists(multicontainer2_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(multicontainer2_index, multicontainer2_query),
'Widget accordion exists.');
this.test.assert(this.cell_element_exists(multicontainer2_index, multicontainer2_query +
' .panel:nth-child(1) .panel-collapse'),
'First accordion page exists.');
// JQuery selector is 1 based
this.test.assert(this.cell_element_function(multicontainer2_index, multicontainer2_query +
' .panel.panel-default:nth-child(3) .panel-heading .accordion-toggle',
'html')=='good', 'Accordion page title set (before display).');
// JQuery selector is 1 based
this.click(multicontainer2_query + ' .panel:nth-child(2) .panel-heading .accordion-toggle');
});
this.wait_for_idle();
index = this.append_cell('print(multicontainer.selected_index)'); // 0 based
this.execute_cell_then(index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, '1\n', // 0 based
'selected_index property updated with tab change.');
var is_collapsed = this.evaluate(function(s){
return $(s + ' div.panel:nth-child(2) a').hasClass('collapsed'); // 1 based
}, {s: multicontainer2_query});
this.test.assertEquals(is_collapsed, false, 'Was tab actually opened?');
});
});

@ -1,59 +0,0 @@
// Test widget string class
casper.notebook_test(function () {
var string_index = this.append_cell(
'from jupyter_notebook import widgets\n' +
'from IPython.display import display, clear_output\n' +
'string_widget = [widgets.Text(value = "xyz", placeholder = "abc"),\n' +
' widgets.Textarea(value = "xyz", placeholder = "def"),\n' +
' widgets.HTML(value = "xyz"),\n' +
' widgets.Latex(value = "$\\\\LaTeX{}$")]\n' +
'[display(widget) for widget in string_widget]\n'+
'print("Success")');
this.execute_cell_then(string_index, function(index){
this.test.assertEquals(this.get_output_cell(index).text, 'Success\n',
'Create string widget cell executed with correct output.');
});
// Wait for the widget to actually display.
var textbox_selector = '.widget-area .widget-subarea .widget-hbox input[type=text]';
var textarea_selector = '.widget-area .widget-subarea .widget-hbox textarea';
var latex_selector = '.widget-area .widget-subarea div span.MathJax_Preview';
this.wait_for_element(string_index, textbox_selector);
this.wait_for_element(string_index, textarea_selector);
this.wait_for_element(string_index, latex_selector);
// Continue with the tests.
this.then(function(){
this.test.assert(this.cell_element_exists(string_index,
'.widget-area .widget-subarea'),
'Widget subarea exists.');
this.test.assert(this.cell_element_exists(string_index,
textbox_selector),
'Textbox exists.');
this.test.assert(this.cell_element_exists(string_index,
textarea_selector),
'Textarea exists.');
this.test.assert(this.cell_element_function(string_index,
textarea_selector, 'val')=='xyz',
'Python set textarea value.');
this.test.assert(this.cell_element_function(string_index,
textbox_selector, 'val')=='xyz',
'Python set textbox value.');
this.test.assert(this.cell_element_exists(string_index,
latex_selector),
'MathJax parsed the LaTeX successfully.');
this.test.assert(this.cell_element_function(string_index,
textarea_selector, 'attr', ['placeholder'])=='def',
'Python set textarea placeholder.');
this.test.assert(this.cell_element_function(string_index,
textbox_selector, 'attr', ['placeholder'])=='abc',
'Python set textbox placehoder.');
});
});

@ -1,40 +0,0 @@
from .widget import Widget, DOMWidget, CallbackDispatcher, register, widget_serialization
from .trait_types import Color, EventfulDict, EventfulList
from .widget_bool import Checkbox, ToggleButton, Valid
from .widget_button import Button
from .widget_box import Box, FlexBox, HBox, VBox
from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
from .widget_image import Image
from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
from .widget_output import Output
from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select, SelectMultiple
from .widget_selectioncontainer import Tab, Accordion
from .widget_string import HTML, Latex, Text, Textarea
from .interaction import interact, interactive, fixed, interact_manual
from .widget_link import jslink, jsdlink
# Deprecated classes
from .widget_bool import CheckboxWidget, ToggleButtonWidget
from .widget_button import ButtonWidget
from .widget_box import ContainerWidget
from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
from .widget_image import ImageWidget
from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
from .widget_selectioncontainer import TabWidget, AccordionWidget
from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
# We use warn_explicit so we have very brief messages without file or line numbers.
# The concern is that file or line numbers will confuse the interactive user.
# To ignore this warning, do:
#
# from warnings import filterwarnings
# filterwarnings('ignore', module='jupyter_notebook.widgets')
from warnings import warn_explicit
__warningregistry__ = {}
warn_explicit("IPython widgets are experimental and may change in the future.",
FutureWarning, '', 0, module = 'jupyter_notebook.widgets',
registry = __warningregistry__, module_globals = globals)

@ -1,22 +0,0 @@
"""Decorator for warning about deprecated widget classes"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from warnings import warn
def DeprecatedClass(base, class_name):
"""Warn about a deprecated class on instantiation"""
# Hook the init method of the base class.
def init_hook(self, *pargs, **kwargs):
base.__init__(self, *pargs, **kwargs)
# Warn once per class.
if base not in DeprecatedClass._warned_classes:
DeprecatedClass._warned_classes.append(base)
warn('"{}" is deprecated, please use "{}" instead.'.format(
class_name, base.__name__))
return type(class_name, (base,), {'__init__': init_hook})
DeprecatedClass._warned_classes = []

@ -1,299 +0,0 @@
"""Contains eventful dict and list implementations."""
# void function used as a callback placeholder.
def _void(*p, **k): return None
class EventfulDict(dict):
"""Eventful dictionary.
This class inherits from the Python intrinsic dictionary class, dict. It
adds events to the get, set, and del actions and optionally allows you to
intercept and cancel these actions. The eventfulness isn't recursive. In
other words, if you add a dict as a child, the events of that dict won't be
listened to. If you find you need something recursive, listen to the `add`
and `set` methods, and then cancel `dict` values from being set, and instead
set EventfulDicts that wrap those dicts. Then you can wire the events
to the same handlers if necessary.
See the on_events, on_add, on_set, and on_del methods for registering
event handlers."""
def __init__(self, *args, **kwargs):
"""Public constructor"""
self._add_callback = _void
self._del_callback = _void
self._set_callback = _void
dict.__init__(self, *args, **kwargs)
def on_events(self, add_callback=None, set_callback=None, del_callback=None):
"""Register callbacks for add, set, and del actions.
See the doctstrings for on_(add/set/del) for details about each
callback.
add_callback: [callback = None]
set_callback: [callback = None]
del_callback: [callback = None]"""
self.on_add(add_callback)
self.on_set(set_callback)
self.on_del(del_callback)
def on_add(self, callback):
"""Register a callback for when an item is added to the dict.
Allows the listener to detect when items are added to the dictionary and
optionally cancel the addition.
callback: callable or None
If you want to ignore the addition event, pass None as the callback.
The callback should have a signature of callback(key, value). The
callback should return a boolean True if the additon should be
canceled, False or None otherwise."""
self._add_callback = callback if callable(callback) else _void
def on_del(self, callback):
"""Register a callback for when an item is deleted from the dict.
Allows the listener to detect when items are deleted from the dictionary
and optionally cancel the deletion.
callback: callable or None
If you want to ignore the deletion event, pass None as the callback.
The callback should have a signature of callback(key). The
callback should return a boolean True if the deletion should be
canceled, False or None otherwise."""
self._del_callback = callback if callable(callback) else _void
def on_set(self, callback):
"""Register a callback for when an item is changed in the dict.
Allows the listener to detect when items are changed in the dictionary
and optionally cancel the change.
callback: callable or None
If you want to ignore the change event, pass None as the callback.
The callback should have a signature of callback(key, value). The
callback should return a boolean True if the change should be
canceled, False or None otherwise."""
self._set_callback = callback if callable(callback) else _void
def pop(self, key):
"""Returns the value of an item in the dictionary and then deletes the
item from the dictionary."""
if self._can_del(key):
return dict.pop(self, key)
else:
raise Exception('Cannot `pop`, deletion of key "{}" failed.'.format(key))
def popitem(self):
"""Pop the next key/value pair from the dictionary."""
key = next(iter(self))
return key, self.pop(key)
def update(self, other_dict):
"""Copy the key/value pairs from another dictionary into this dictionary,
overwriting any conflicting keys in this dictionary."""
for (key, value) in other_dict.items():
self[key] = value
def clear(self):
"""Clear the dictionary."""
for key in list(self.keys()):
del self[key]
def __setitem__(self, key, value):
if (key in self and self._can_set(key, value)) or \
(key not in self and self._can_add(key, value)):
return dict.__setitem__(self, key, value)
def __delitem__(self, key):
if self._can_del(key):
return dict.__delitem__(self, key)
def _can_add(self, key, value):
"""Check if the item can be added to the dict."""
return not bool(self._add_callback(key, value))
def _can_del(self, key):
"""Check if the item can be deleted from the dict."""
return not bool(self._del_callback(key))
def _can_set(self, key, value):
"""Check if the item can be changed in the dict."""
return not bool(self._set_callback(key, value))
class EventfulList(list):
"""Eventful list.
This class inherits from the Python intrinsic `list` class. It adds events
that allow you to listen for actions that modify the list. You can
optionally cancel the actions.
See the on_del, on_set, on_insert, on_sort, and on_reverse methods for
registering an event handler.
Some of the method docstrings were taken from the Python documentation at
https://docs.python.org/2/tutorial/datastructures.html"""
def __init__(self, *pargs, **kwargs):
"""Public constructor"""
self._insert_callback = _void
self._set_callback = _void
self._del_callback = _void
self._sort_callback = _void
self._reverse_callback = _void
list.__init__(self, *pargs, **kwargs)
def on_events(self, insert_callback=None, set_callback=None,
del_callback=None, reverse_callback=None, sort_callback=None):
"""Register callbacks for add, set, and del actions.
See the doctstrings for on_(insert/set/del/reverse/sort) for details
about each callback.
insert_callback: [callback = None]
set_callback: [callback = None]
del_callback: [callback = None]
reverse_callback: [callback = None]
sort_callback: [callback = None]"""
self.on_insert(insert_callback)
self.on_set(set_callback)
self.on_del(del_callback)
self.on_reverse(reverse_callback)
self.on_sort(sort_callback)
def on_insert(self, callback):
"""Register a callback for when an item is inserted into the list.
Allows the listener to detect when items are inserted into the list and
optionally cancel the insertion.
callback: callable or None
If you want to ignore the insertion event, pass None as the callback.
The callback should have a signature of callback(index, value). The
callback should return a boolean True if the insertion should be
canceled, False or None otherwise."""
self._insert_callback = callback if callable(callback) else _void
def on_del(self, callback):
"""Register a callback for item deletion.
Allows the listener to detect when items are deleted from the list and
optionally cancel the deletion.
callback: callable or None
If you want to ignore the deletion event, pass None as the callback.
The callback should have a signature of callback(index). The
callback should return a boolean True if the deletion should be
canceled, False or None otherwise."""
self._del_callback = callback if callable(callback) else _void
def on_set(self, callback):
"""Register a callback for items are set.
Allows the listener to detect when items are set and optionally cancel
the setting. Note, `set` is also called when one or more items are
added to the end of the list.
callback: callable or None
If you want to ignore the set event, pass None as the callback.
The callback should have a signature of callback(index, value). The
callback should return a boolean True if the set should be
canceled, False or None otherwise."""
self._set_callback = callback if callable(callback) else _void
def on_reverse(self, callback):
"""Register a callback for list reversal.
callback: callable or None
If you want to ignore the reverse event, pass None as the callback.
The callback should have a signature of callback(). The
callback should return a boolean True if the reverse should be
canceled, False or None otherwise."""
self._reverse_callback = callback if callable(callback) else _void
def on_sort(self, callback):
"""Register a callback for sortting of the list.
callback: callable or None
If you want to ignore the sort event, pass None as the callback.
The callback signature should match that of Python list's `.sort`
method or `callback(*pargs, **kwargs)` as a catch all. The callback
should return a boolean True if the reverse should be canceled,
False or None otherwise."""
self._sort_callback = callback if callable(callback) else _void
def append(self, x):
"""Add an item to the end of the list."""
self[len(self):] = [x]
def extend(self, L):
"""Extend the list by appending all the items in the given list."""
self[len(self):] = L
def remove(self, x):
"""Remove the first item from the list whose value is x. It is an error
if there is no such item."""
del self[self.index(x)]
def pop(self, i=None):
"""Remove the item at the given position in the list, and return it. If
no index is specified, a.pop() removes and returns the last item in the
list."""
if i is None:
i = len(self) - 1
val = self[i]
del self[i]
return val
def reverse(self):
"""Reverse the elements of the list, in place."""
if self._can_reverse():
list.reverse(self)
def insert(self, index, value):
"""Insert an item at a given position. The first argument is the index
of the element before which to insert, so a.insert(0, x) inserts at the
front of the list, and a.insert(len(a), x) is equivalent to
a.append(x)."""
if self._can_insert(index, value):
list.insert(self, index, value)
def sort(self, *pargs, **kwargs):
"""Sort the items of the list in place (the arguments can be used for
sort customization, see Python's sorted() for their explanation)."""
if self._can_sort(*pargs, **kwargs):
list.sort(self, *pargs, **kwargs)
def __delitem__(self, index):
if self._can_del(index):
list.__delitem__(self, index)
def __setitem__(self, index, value):
if self._can_set(index, value):
list.__setitem__(self, index, value)
def __setslice__(self, start, end, value):
if self._can_set(slice(start, end), value):
list.__setslice__(self, start, end, value)
def _can_insert(self, index, value):
"""Check if the item can be inserted."""
return not bool(self._insert_callback(index, value))
def _can_del(self, index):
"""Check if the item can be deleted."""
return not bool(self._del_callback(index))
def _can_set(self, index, value):
"""Check if the item can be set."""
return not bool(self._set_callback(index, value))
def _can_reverse(self):
"""Check if the list can be reversed."""
return not bool(self._reverse_callback())
def _can_sort(self, *pargs, **kwargs):
"""Check if the list can be sorted."""
return not bool(self._sort_callback(*pargs, **kwargs))

@ -1,344 +0,0 @@
"""Interact with functions using widgets."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import print_function
try: # Python >= 3.3
from inspect import signature, Parameter
except ImportError:
from IPython.utils.signatures import signature, Parameter
from inspect import getcallargs
from IPython.core.getipython import get_ipython
from . import (Widget, Text,
FloatSlider, IntSlider, Checkbox, Dropdown,
Box, Button, DOMWidget)
from IPython.display import display, clear_output
from ipython_genutils.py3compat import string_types, unicode_type
from traitlets import HasTraits, Any, Unicode
empty = Parameter.empty
def _matches(o, pattern):
"""Match a pattern of types in a sequence."""
if not len(o) == len(pattern):
return False
comps = zip(o,pattern)
return all(isinstance(obj,kind) for obj,kind in comps)
def _get_min_max_value(min, max, value=None, step=None):
"""Return min, max, value given input values with possible None."""
if value is None:
if not max > min:
raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
value = min + abs(min-max)/2
value = type(min)(value)
elif min is None and max is None:
if value == 0.0:
min, max, value = 0.0, 1.0, 0.5
elif value == 0:
min, max, value = 0, 1, 0
elif isinstance(value, (int, float)):
min, max = (-value, 3*value) if value > 0 else (3*value, -value)
else:
raise TypeError('expected a number, got: %r' % value)
else:
raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
if step is not None:
# ensure value is on a step
r = (value - min) % step
value = value - r
return min, max, value
def _widget_abbrev_single_value(o):
"""Make widgets from single values, which can be used as parameter defaults."""
if isinstance(o, string_types):
return Text(value=unicode_type(o))
elif isinstance(o, dict):
return Dropdown(options=o)
elif isinstance(o, bool):
return Checkbox(value=o)
elif isinstance(o, float):
min, max, value = _get_min_max_value(None, None, o)
return FloatSlider(value=o, min=min, max=max)
elif isinstance(o, int):
min, max, value = _get_min_max_value(None, None, o)
return IntSlider(value=o, min=min, max=max)
else:
return None
def _widget_abbrev(o):
"""Make widgets from abbreviations: single values, lists or tuples."""
float_or_int = (float, int)
if isinstance(o, (list, tuple)):
if o and all(isinstance(x, string_types) for x in o):
return Dropdown(options=[unicode_type(k) for k in o])
elif _matches(o, (float_or_int, float_or_int)):
min, max, value = _get_min_max_value(o[0], o[1])
if all(isinstance(_, int) for _ in o):
cls = IntSlider
else:
cls = FloatSlider
return cls(value=value, min=min, max=max)
elif _matches(o, (float_or_int, float_or_int, float_or_int)):
step = o[2]
if step <= 0:
raise ValueError("step must be >= 0, not %r" % step)
min, max, value = _get_min_max_value(o[0], o[1], step=step)
if all(isinstance(_, int) for _ in o):
cls = IntSlider
else:
cls = FloatSlider
return cls(value=value, min=min, max=max, step=step)
else:
return _widget_abbrev_single_value(o)
def _widget_from_abbrev(abbrev, default=empty):
"""Build a Widget instance given an abbreviation or Widget."""
if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
return abbrev
widget = _widget_abbrev(abbrev)
if default is not empty and isinstance(abbrev, (list, tuple, dict)):
# if it's not a single-value abbreviation,
# set the initial value from the default
try:
widget.value = default
except Exception:
# ignore failure to set default
pass
if widget is None:
raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
return widget
def _yield_abbreviations_for_parameter(param, kwargs):
"""Get an abbreviation for a function parameter."""
name = param.name
kind = param.kind
ann = param.annotation
default = param.default
not_found = (name, empty, empty)
if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
if name in kwargs:
value = kwargs.pop(name)
elif ann is not empty:
value = ann
elif default is not empty:
value = default
else:
yield not_found
yield (name, value, default)
elif kind == Parameter.VAR_KEYWORD:
# In this case name=kwargs and we yield the items in kwargs with their keys.
for k, v in kwargs.copy().items():
kwargs.pop(k)
yield k, v, empty
def _find_abbreviations(f, kwargs):
"""Find the abbreviations for a function and kwargs passed to interact."""
new_kwargs = []
for param in signature(f).parameters.values():
for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
if value is empty:
raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
new_kwargs.append((name, value, default))
return new_kwargs
def _widgets_from_abbreviations(seq):
"""Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
result = []
for name, abbrev, default in seq:
widget = _widget_from_abbrev(abbrev, default)
if not widget.description:
widget.description = name
widget._kwarg = name
result.append(widget)
return result
def interactive(__interact_f, **kwargs):
"""
Builds a group of interactive widgets tied to a function and places the
group into a Box container.
Returns
-------
container : a Box instance containing multiple widgets
Parameters
----------
__interact_f : function
The function to which the interactive widgets are tied. The `**kwargs`
should match the function signature.
**kwargs : various, optional
An interactive widget is created for each keyword argument that is a
valid widget abbreviation.
"""
f = __interact_f
co = kwargs.pop('clear_output', True)
manual = kwargs.pop('__manual', False)
kwargs_widgets = []
container = Box(_dom_classes=['widget-interact'])
container.result = None
container.args = []
container.kwargs = dict()
kwargs = kwargs.copy()
new_kwargs = _find_abbreviations(f, kwargs)
# Before we proceed, let's make sure that the user has passed a set of args+kwargs
# that will lead to a valid call of the function. This protects against unspecified
# and doubly-specified arguments.
getcallargs(f, **{n:v for n,v,_ in new_kwargs})
# Now build the widgets from the abbreviations.
kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
# This has to be done as an assignment, not using container.children.append,
# so that traitlets notices the update. We skip any objects (such as fixed) that
# are not DOMWidgets.
c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
# If we are only to run the function on demand, add a button to request this
if manual:
manual_button = Button(description="Run %s" % f.__name__)
c.append(manual_button)
container.children = c
# Build the callback
def call_f(name=None, old=None, new=None):
container.kwargs = {}
for widget in kwargs_widgets:
value = widget.value
container.kwargs[widget._kwarg] = value
if co:
clear_output(wait=True)
if manual:
manual_button.disabled = True
try:
container.result = f(**container.kwargs)
except Exception as e:
ip = get_ipython()
if ip is None:
container.log.warn("Exception in interact callback: %s", e, exc_info=True)
else:
ip.showtraceback()
finally:
if manual:
manual_button.disabled = False
# Wire up the widgets
# If we are doing manual running, the callback is only triggered by the button
# Otherwise, it is triggered for every trait change received
# On-demand running also suppresses running the function with the initial parameters
if manual:
manual_button.on_click(call_f)
else:
for widget in kwargs_widgets:
widget.on_trait_change(call_f, 'value')
container.on_displayed(lambda _: call_f(None, None, None))
return container
def interact(__interact_f=None, **kwargs):
"""
Displays interactive widgets which are tied to a function.
Expects the first argument to be a function. Parameters to this function are
widget abbreviations passed in as keyword arguments (`**kwargs`). Can be used
as a decorator (see examples).
Returns
-------
f : __interact_f with interactive widget attached to it.
Parameters
----------
__interact_f : function
The function to which the interactive widgets are tied. The `**kwargs`
should match the function signature. Passed to :func:`interactive()`
**kwargs : various, optional
An interactive widget is created for each keyword argument that is a
valid widget abbreviation. Passed to :func:`interactive()`
Examples
--------
Render an interactive text field that shows the greeting with the passed in
text::
# 1. Using interact as a function
def greeting(text="World"):
print "Hello {}".format(text)
interact(greeting, text="IPython Widgets")
# 2. Using interact as a decorator
@interact
def greeting(text="World"):
print "Hello {}".format(text)
# 3. Using interact as a decorator with named parameters
@interact(text="IPython Widgets")
def greeting(text="World"):
print "Hello {}".format(text)
Render an interactive slider widget and prints square of number::
# 1. Using interact as a function
def square(num=1):
print "{} squared is {}".format(num, num*num)
interact(square, num=5)
# 2. Using interact as a decorator
@interact
def square(num=2):
print "{} squared is {}".format(num, num*num)
# 3. Using interact as a decorator with named parameters
@interact(num=5)
def square(num=2):
print "{} squared is {}".format(num, num*num)
"""
# positional arg support in: https://gist.github.com/8851331
if __interact_f is not None:
# This branch handles the cases 1 and 2
# 1. interact(f, **kwargs)
# 2. @interact
# def f(*args, **kwargs):
# ...
f = __interact_f
w = interactive(f, **kwargs)
try:
f.widget = w
except AttributeError:
# some things (instancemethods) can't have attributes attached,
# so wrap in a lambda
f = lambda *args, **kwargs: __interact_f(*args, **kwargs)
f.widget = w
display(w)
return f
else:
# This branch handles the case 3
# @interact(a=30, b=40)
# def f(*args, **kwargs):
# ...
def dec(f):
return interact(f, **kwargs)
return dec
def interact_manual(__interact_f=None, **kwargs):
"""interact_manual(f, **kwargs)
As `interact()`, generates widgets for each argument, but rather than running
the function after each widget change, adds a "Run" button and waits for it
to be clicked. Useful if the function is long-running and has several
parameters to change.
"""
return interact(__interact_f, __manual=True, **kwargs)
class fixed(HasTraits):
"""A pseudo-widget whose value is fixed and never synced to the client."""
value = Any(help="Any Python object")
description = Unicode('', help="Any Python object")
def __init__(self, value, **kwargs):
super(fixed, self).__init__(value=value, **kwargs)

@ -1,693 +0,0 @@
"""Test interact and interactive."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import print_function
try:
from unittest.mock import patch
except ImportError:
from mock import patch
import nose.tools as nt
from ipython_kernel.comm import Comm
from jupyter_notebook import widgets
from jupyter_notebook.widgets import interact, interactive, Widget, interaction
from ipython_genutils.py3compat import annotate
#-----------------------------------------------------------------------------
# Utility stuff
#-----------------------------------------------------------------------------
class DummyComm(Comm):
comm_id = 'a-b-c-d'
def open(self, *args, **kwargs):
pass
def send(self, *args, **kwargs):
pass
def close(self, *args, **kwargs):
pass
_widget_attrs = {}
displayed = []
undefined = object()
def setup():
_widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
Widget._comm_default = lambda self: DummyComm()
_widget_attrs['_ipython_display_'] = Widget._ipython_display_
def raise_not_implemented(*args, **kwargs):
raise NotImplementedError()
Widget._ipython_display_ = raise_not_implemented
def teardown():
for attr, value in _widget_attrs.items():
if value is undefined:
delattr(Widget, attr)
else:
setattr(Widget, attr, value)
def f(**kwargs):
pass
def clear_display():
global displayed
displayed = []
def record_display(*args):
displayed.extend(args)
#-----------------------------------------------------------------------------
# Actual tests
#-----------------------------------------------------------------------------
def check_widget(w, **d):
"""Check a single widget against a dict"""
for attr, expected in d.items():
if attr == 'cls':
nt.assert_is(w.__class__, expected)
else:
value = getattr(w, attr)
nt.assert_equal(value, expected,
"%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
)
def check_widgets(container, **to_check):
"""Check that widgets are created as expected"""
# build a widget dictionary, so it matches
widgets = {}
for w in container.children:
widgets[w.description] = w
for key, d in to_check.items():
nt.assert_in(key, widgets)
check_widget(widgets[key], **d)
def test_single_value_string():
a = u'hello'
c = interactive(f, a=a)
w = c.children[0]
check_widget(w,
cls=widgets.Text,
description='a',
value=a,
)
def test_single_value_bool():
for a in (True, False):
c = interactive(f, a=a)
w = c.children[0]
check_widget(w,
cls=widgets.Checkbox,
description='a',
value=a,
)
def test_single_value_dict():
for d in [
dict(a=5),
dict(a=5, b='b', c=dict),
]:
c = interactive(f, d=d)
w = c.children[0]
check_widget(w,
cls=widgets.Dropdown,
description='d',
options=d,
value=next(iter(d.values())),
)
def test_single_value_float():
for a in (2.25, 1.0, -3.5):
c = interactive(f, a=a)
w = c.children[0]
check_widget(w,
cls=widgets.FloatSlider,
description='a',
value=a,
min= -a if a > 0 else 3*a,
max= 3*a if a > 0 else -a,
step=0.1,
readout=True,
)
def test_single_value_int():
for a in (1, 5, -3):
c = interactive(f, a=a)
nt.assert_equal(len(c.children), 1)
w = c.children[0]
check_widget(w,
cls=widgets.IntSlider,
description='a',
value=a,
min= -a if a > 0 else 3*a,
max= 3*a if a > 0 else -a,
step=1,
readout=True,
)
def test_list_tuple_2_int():
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,1))
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,-1))
for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
c = interactive(f, tup=(min, max), lis=[min, max])
nt.assert_equal(len(c.children), 2)
d = dict(
cls=widgets.IntSlider,
min=min,
max=max,
step=1,
readout=True,
)
check_widgets(c, tup=d, lis=d)
def test_list_tuple_3_int():
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,2,0))
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,2,-1))
for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
c = interactive(f, tup=(min, max, step), lis=[min, max, step])
nt.assert_equal(len(c.children), 2)
d = dict(
cls=widgets.IntSlider,
min=min,
max=max,
step=step,
readout=True,
)
check_widgets(c, tup=d, lis=d)
def test_list_tuple_2_float():
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1.0,1.0))
with nt.assert_raises(ValueError):
c = interactive(f, tup=(0.5,-0.5))
for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
c = interactive(f, tup=(min, max), lis=[min, max])
nt.assert_equal(len(c.children), 2)
d = dict(
cls=widgets.FloatSlider,
min=min,
max=max,
step=.1,
readout=True,
)
check_widgets(c, tup=d, lis=d)
def test_list_tuple_3_float():
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,2,0.0))
with nt.assert_raises(ValueError):
c = interactive(f, tup=(-1,-2,1.))
with nt.assert_raises(ValueError):
c = interactive(f, tup=(1,2.,-1.))
for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
c = interactive(f, tup=(min, max, step), lis=[min, max, step])
nt.assert_equal(len(c.children), 2)
d = dict(
cls=widgets.FloatSlider,
min=min,
max=max,
step=step,
readout=True,
)
check_widgets(c, tup=d, lis=d)
def test_list_tuple_str():
values = ['hello', 'there', 'guy']
first = values[0]
c = interactive(f, tup=tuple(values), lis=list(values))
nt.assert_equal(len(c.children), 2)
d = dict(
cls=widgets.Dropdown,
value=first,
options=values
)
check_widgets(c, tup=d, lis=d)
def test_list_tuple_invalid():
for bad in [
(),
(5, 'hi'),
('hi', 5),
({},),
(None,),
]:
with nt.assert_raises(ValueError):
print(bad) # because there is no custom message in assert_raises
c = interactive(f, tup=bad)
def test_defaults():
@annotate(n=10)
def f(n, f=4.5, g=1):
pass
c = interactive(f)
check_widgets(c,
n=dict(
cls=widgets.IntSlider,
value=10,
),
f=dict(
cls=widgets.FloatSlider,
value=4.5,
),
g=dict(
cls=widgets.IntSlider,
value=1,
),
)
def test_default_values():
@annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
def f(n, f=4.5, g=1, h=2, j='there'):
pass
c = interactive(f)
check_widgets(c,
n=dict(
cls=widgets.IntSlider,
value=10,
),
f=dict(
cls=widgets.FloatSlider,
value=4.5,
),
g=dict(
cls=widgets.IntSlider,
value=5,
),
h=dict(
cls=widgets.Dropdown,
options={'a': 1, 'b': 2},
value=2
),
j=dict(
cls=widgets.Dropdown,
options=['hi', 'there'],
value='there'
),
)
def test_default_out_of_bounds():
@annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
def f(f='hi', h=5, j='other'):
pass
c = interactive(f)
check_widgets(c,
f=dict(
cls=widgets.FloatSlider,
value=5.,
),
h=dict(
cls=widgets.Dropdown,
options={'a': 1},
value=1,
),
j=dict(
cls=widgets.Dropdown,
options=['hi', 'there'],
value='hi',
),
)
def test_annotations():
@annotate(n=10, f=widgets.FloatText())
def f(n, f):
pass
c = interactive(f)
check_widgets(c,
n=dict(
cls=widgets.IntSlider,
value=10,
),
f=dict(
cls=widgets.FloatText,
),
)
def test_priority():
@annotate(annotate='annotate', kwarg='annotate')
def f(kwarg='default', annotate='default', default='default'):
pass
c = interactive(f, kwarg='kwarg')
check_widgets(c,
kwarg=dict(
cls=widgets.Text,
value='kwarg',
),
annotate=dict(
cls=widgets.Text,
value='annotate',
),
)
@nt.with_setup(clear_display)
def test_decorator_kwarg():
with patch.object(interaction, 'display', record_display):
@interact(a=5)
def foo(a):
pass
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.IntSlider,
value=5,
)
@nt.with_setup(clear_display)
def test_interact_instancemethod():
class Foo(object):
def show(self, x):
print(x)
f = Foo()
with patch.object(interaction, 'display', record_display):
g = interact(f.show, x=(1,10))
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.IntSlider,
value=5,
)
@nt.with_setup(clear_display)
def test_decorator_no_call():
with patch.object(interaction, 'display', record_display):
@interact
def foo(a='default'):
pass
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.Text,
value='default',
)
@nt.with_setup(clear_display)
def test_call_interact():
def foo(a='default'):
pass
with patch.object(interaction, 'display', record_display):
ifoo = interact(foo)
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.Text,
value='default',
)
@nt.with_setup(clear_display)
def test_call_interact_kwargs():
def foo(a='default'):
pass
with patch.object(interaction, 'display', record_display):
ifoo = interact(foo, a=10)
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.IntSlider,
value=10,
)
@nt.with_setup(clear_display)
def test_call_decorated_on_trait_change():
"""test calling @interact decorated functions"""
d = {}
with patch.object(interaction, 'display', record_display):
@interact
def foo(a='default'):
d['a'] = a
return a
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.Text,
value='default',
)
# test calling the function directly
a = foo('hello')
nt.assert_equal(a, 'hello')
nt.assert_equal(d['a'], 'hello')
# test that setting trait values calls the function
w.value = 'called'
nt.assert_equal(d['a'], 'called')
@nt.with_setup(clear_display)
def test_call_decorated_kwargs_on_trait_change():
"""test calling @interact(foo=bar) decorated functions"""
d = {}
with patch.object(interaction, 'display', record_display):
@interact(a='kwarg')
def foo(a='default'):
d['a'] = a
return a
nt.assert_equal(len(displayed), 1)
w = displayed[0].children[0]
check_widget(w,
cls=widgets.Text,
value='kwarg',
)
# test calling the function directly
a = foo('hello')
nt.assert_equal(a, 'hello')
nt.assert_equal(d['a'], 'hello')
# test that setting trait values calls the function
w.value = 'called'
nt.assert_equal(d['a'], 'called')
def test_fixed():
c = interactive(f, a=widgets.fixed(5), b='text')
nt.assert_equal(len(c.children), 1)
w = c.children[0]
check_widget(w,
cls=widgets.Text,
value='text',
description='b',
)
def test_default_description():
c = interactive(f, b='text')
w = c.children[0]
check_widget(w,
cls=widgets.Text,
value='text',
description='b',
)
def test_custom_description():
d = {}
def record_kwargs(**kwargs):
d.clear()
d.update(kwargs)
c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo'))
w = c.children[0]
check_widget(w,
cls=widgets.Text,
value='text',
description='foo',
)
w.value = 'different text'
nt.assert_equal(d, {'b': 'different text'})
def test_interact_manual_button():
c = interactive(f, __manual=True)
w = c.children[0]
check_widget(w, cls=widgets.Button)
def test_interact_manual_nocall():
callcount = 0
def calltest(testarg):
callcount += 1
c = interactive(calltest, testarg=5, __manual=True)
c.children[0].value = 10
nt.assert_equal(callcount, 0)
def test_int_range_logic():
irsw = widgets.IntRangeSlider
w = irsw(value=(2, 4), min=0, max=6)
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
w.value = (4, 2)
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
w.value = (-1, 7)
check_widget(w, cls=irsw, value=(0, 6), min=0, max=6)
w.min = 3
check_widget(w, cls=irsw, value=(3, 6), min=3, max=6)
w.max = 3
check_widget(w, cls=irsw, value=(3, 3), min=3, max=3)
w.min = 0
w.max = 6
w.lower = 2
w.upper = 4
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
w.value = (0, 1) #lower non-overlapping range
check_widget(w, cls=irsw, value=(0, 1), min=0, max=6)
w.value = (5, 6) #upper non-overlapping range
check_widget(w, cls=irsw, value=(5, 6), min=0, max=6)
w.value = (-1, 4) #semi out-of-range
check_widget(w, cls=irsw, value=(0, 4), min=0, max=6)
w.lower = 2
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
w.value = (-2, -1) #wholly out of range
check_widget(w, cls=irsw, value=(0, 0), min=0, max=6)
w.value = (7, 8)
check_widget(w, cls=irsw, value=(6, 6), min=0, max=6)
with nt.assert_raises(ValueError):
w.min = 7
with nt.assert_raises(ValueError):
w.max = -1
with nt.assert_raises(ValueError):
w.lower = 5
with nt.assert_raises(ValueError):
w.upper = 1
w = irsw(min=2, max=3)
check_widget(w, min=2, max=3)
w = irsw(min=100, max=200)
check_widget(w, lower=125, upper=175, value=(125, 175))
with nt.assert_raises(ValueError):
irsw(value=(2, 4), lower=3)
with nt.assert_raises(ValueError):
irsw(value=(2, 4), upper=3)
with nt.assert_raises(ValueError):
irsw(value=(2, 4), lower=3, upper=3)
with nt.assert_raises(ValueError):
irsw(min=2, max=1)
with nt.assert_raises(ValueError):
irsw(lower=5)
with nt.assert_raises(ValueError):
irsw(upper=5)
def test_float_range_logic():
frsw = widgets.FloatRangeSlider
w = frsw(value=(.2, .4), min=0., max=.6)
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
w.value = (.4, .2)
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
w.value = (-.1, .7)
check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6)
w.min = .3
check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6)
w.max = .3
check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3)
w.min = 0.
w.max = .6
w.lower = .2
w.upper = .4
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
w.value = (0., .1) #lower non-overlapping range
check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6)
w.value = (.5, .6) #upper non-overlapping range
check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6)
w.value = (-.1, .4) #semi out-of-range
check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6)
w.lower = .2
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
w.value = (-.2, -.1) #wholly out of range
check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6)
w.value = (.7, .8)
check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6)
with nt.assert_raises(ValueError):
w.min = .7
with nt.assert_raises(ValueError):
w.max = -.1
with nt.assert_raises(ValueError):
w.lower = .5
with nt.assert_raises(ValueError):
w.upper = .1
w = frsw(min=2, max=3)
check_widget(w, min=2, max=3)
w = frsw(min=1., max=2.)
check_widget(w, lower=1.25, upper=1.75, value=(1.25, 1.75))
with nt.assert_raises(ValueError):
frsw(value=(2, 4), lower=3)
with nt.assert_raises(ValueError):
frsw(value=(2, 4), upper=3)
with nt.assert_raises(ValueError):
frsw(value=(2, 4), lower=3, upper=3)
with nt.assert_raises(ValueError):
frsw(min=.2, max=.1)
with nt.assert_raises(ValueError):
frsw(lower=5)
with nt.assert_raises(ValueError):
frsw(upper=5)
def test_multiple_selection():
smw = widgets.SelectMultiple
# degenerate multiple select
w = smw()
check_widget(w, value=tuple(), options=None, selected_labels=tuple())
# don't accept random other value when no options
with nt.assert_raises(KeyError):
w.value = (2,)
check_widget(w, value=tuple(), selected_labels=tuple())
# basic multiple select
w = smw(options=[(1, 1)], value=[1])
check_widget(w, cls=smw, value=(1,), options=[(1, 1)])
# don't accept random other value
with nt.assert_raises(KeyError):
w.value = w.value + (2,)
check_widget(w, value=(1,), selected_labels=(1,))
# change options
w.options = w.options + [(2, 2)]
check_widget(w, options=[(1, 1), (2,2)])
# change value
w.value = w.value + (2,)
check_widget(w, value=(1, 2), selected_labels=(1, 2))
# change value name
w.selected_labels = (1,)
check_widget(w, value=(1,))
# don't accept random other names when no options
with nt.assert_raises(KeyError):
w.selected_labels = (3,)
check_widget(w, value=(1,))
# don't accept selected_label (from superclass)
with nt.assert_raises(AttributeError):
w.selected_label = 3
# don't return selected_label (from superclass)
with nt.assert_raises(AttributeError):
print(w.selected_label)
# dict style
w.options = {1: 1}
check_widget(w, options={1: 1})
# updating
with nt.assert_raises(KeyError):
w.value = (2,)
check_widget(w, options={1: 1})

@ -1,39 +0,0 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import nose.tools as nt
from .. import jslink, jsdlink, ToggleButton
from .test_interaction import setup, teardown
def test_jslink_args():
with nt.assert_raises(TypeError):
jslink()
w1 = ToggleButton()
with nt.assert_raises(TypeError):
jslink((w1, 'value'))
w2 = ToggleButton()
jslink((w1, 'value'), (w2, 'value'))
with nt.assert_raises(TypeError):
jslink((w1, 'value'), (w2, 'nosuchtrait'))
with nt.assert_raises(TypeError):
jslink((w1, 'value'), (w2, 'traits'))
def test_jsdlink_args():
with nt.assert_raises(TypeError):
jsdlink()
w1 = ToggleButton()
with nt.assert_raises(TypeError):
jsdlink((w1, 'value'))
w2 = ToggleButton()
jsdlink((w1, 'value'), (w2, 'value'))
with nt.assert_raises(TypeError):
jsdlink((w1, 'value'), (w2, 'nosuchtrait'))
with nt.assert_raises(TypeError):
jsdlink((w1, 'value'), (w2, 'traits'))

@ -1,83 +0,0 @@
"""Test trait types of the widget packages."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from unittest import TestCase
from traitlets import HasTraits
from traitlets.tests.test_traitlets import TraitTestBase
from jupyter_notebook.widgets import Color, EventfulDict, EventfulList
class ColorTrait(HasTraits):
value = Color("black")
class TestColor(TraitTestBase):
obj = ColorTrait()
_good_values = ["blue", "#AA0", "#FFFFFF"]
_bad_values = ["vanilla", "blues"]
class TestEventful(TestCase):
def test_list(self):
"""Does the EventfulList work?"""
event_cache = []
class A(HasTraits):
x = EventfulList([c for c in 'abc'])
a = A()
a.x.on_events(lambda i, x: event_cache.append('insert'), \
lambda i, x: event_cache.append('set'), \
lambda i: event_cache.append('del'), \
lambda: event_cache.append('reverse'), \
lambda *p, **k: event_cache.append('sort'))
a.x.remove('c')
# ab
a.x.insert(0, 'z')
# zab
del a.x[1]
# zb
a.x.reverse()
# bz
a.x[1] = 'o'
# bo
a.x.append('a')
# boa
a.x.sort()
# abo
# Were the correct events captured?
self.assertEqual(event_cache, ['del', 'insert', 'del', 'reverse', 'set', 'set', 'sort'])
# Is the output correct?
self.assertEqual(a.x, [c for c in 'abo'])
def test_dict(self):
"""Does the EventfulDict work?"""
event_cache = []
class A(HasTraits):
x = EventfulDict({c: c for c in 'abc'})
a = A()
a.x.on_events(lambda k, v: event_cache.append('add'), \
lambda k, v: event_cache.append('set'), \
lambda k: event_cache.append('del'))
del a.x['c']
# ab
a.x['z'] = 1
# abz
a.x['z'] = 'z'
# abz
a.x.pop('a')
# bz
# Were the correct events captured?
self.assertEqual(event_cache, ['del', 'add', 'set', 'del'])
# Is the output correct?
self.assertEqual(a.x, {c: c for c in 'bz'})

@ -1,70 +0,0 @@
# encoding: utf-8
"""
Trait types for html widgets.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import re
import traitlets
from . import eventful
_color_names = ['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred ', 'indigo ', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']
_color_re = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
class Color(traitlets.Unicode):
"""A string holding a valid HTML color such as 'blue', '#060482', '#A80'"""
info_text = 'a valid HTML color'
def validate(self, obj, value):
if value.lower() in _color_names or _color_re.match(value):
return value
self.error(obj, value)
class EventfulDict(traitlets.Instance):
"""An instance of an EventfulDict."""
def __init__(self, default_value={}, **metadata):
"""Create a EventfulDict trait type from a dict.
The default value is created by doing
``eventful.EvenfulDict(default_value)``, which creates a copy of the
``default_value``.
"""
if default_value is None:
args = None
elif isinstance(default_value, dict):
args = (default_value,)
elif isinstance(default_value, SequenceTypes):
args = (default_value,)
else:
raise TypeError('default value of EventfulDict was %s' % default_value)
super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
**metadata)
class EventfulList(traitlets.Instance):
"""An instance of an EventfulList."""
def __init__(self, default_value=None, **metadata):
"""Create a EventfulList trait type from a dict.
The default value is created by doing
``eventful.EvenfulList(default_value)``, which creates a copy of the
``default_value``.
"""
if default_value is None:
args = ((),)
else:
args = (default_value,)
super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
**metadata)

@ -1,515 +0,0 @@
"""Base Widget class. Allows user to create widgets in the back-end that render
in the IPython notebook front-end.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from contextlib import contextmanager
import collections
from IPython.core.getipython import get_ipython
from ipython_kernel.comm import Comm
from traitlets.config import LoggingConfigurable
from ipython_genutils.importstring import import_item
from traitlets import Unicode, Dict, Instance, Bool, List, \
CaselessStrEnum, Tuple, CUnicode, Int, Set
from ipython_genutils.py3compat import string_types
from .trait_types import Color
def _widget_to_json(x):
if isinstance(x, dict):
return {k: _widget_to_json(v) for k, v in x.items()}
elif isinstance(x, (list, tuple)):
return [_widget_to_json(v) for v in x]
elif isinstance(x, Widget):
return "IPY_MODEL_" + x.model_id
else:
return x
def _json_to_widget(x):
if isinstance(x, dict):
return {k: _json_to_widget(v) for k, v in x.items()}
elif isinstance(x, (list, tuple)):
return [_json_to_widget(v) for v in x]
elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
return Widget.widgets[x[10:]]
else:
return x
widget_serialization = {
'from_json': _json_to_widget,
'to_json': _widget_to_json
}
class CallbackDispatcher(LoggingConfigurable):
"""A structure for registering and running callbacks"""
callbacks = List()
def __call__(self, *args, **kwargs):
"""Call all of the registered callbacks."""
value = None
for callback in self.callbacks:
try:
local_value = callback(*args, **kwargs)
except Exception as e:
ip = get_ipython()
if ip is None:
self.log.warn("Exception in callback %s: %s", callback, e, exc_info=True)
else:
ip.showtraceback()
else:
value = local_value if local_value is not None else value
return value
def register_callback(self, callback, remove=False):
"""(Un)Register a callback
Parameters
----------
callback: method handle
Method to be registered or unregistered.
remove=False: bool
Whether to unregister the callback."""
# (Un)Register the callback.
if remove and callback in self.callbacks:
self.callbacks.remove(callback)
elif not remove and callback not in self.callbacks:
self.callbacks.append(callback)
def _show_traceback(method):
"""decorator for showing tracebacks in IPython"""
def m(self, *args, **kwargs):
try:
return(method(self, *args, **kwargs))
except Exception as e:
ip = get_ipython()
if ip is None:
self.log.warn("Exception in widget method %s: %s", method, e, exc_info=True)
else:
ip.showtraceback()
return m
def register(key=None):
"""Returns a decorator registering a widget class in the widget registry.
If no key is provided, the class name is used as a key. A key is
provided for each core IPython widget so that the frontend can use
this key regardless of the language of the kernel"""
def wrap(widget):
l = key if key is not None else widget.__module__ + widget.__name__
Widget.widget_types[l] = widget
return widget
return wrap
class Widget(LoggingConfigurable):
#-------------------------------------------------------------------------
# Class attributes
#-------------------------------------------------------------------------
_widget_construction_callback = None
widgets = {}
widget_types = {}
@staticmethod
def on_widget_constructed(callback):
"""Registers a callback to be called when a widget is constructed.
The callback must have the following signature:
callback(widget)"""
Widget._widget_construction_callback = callback
@staticmethod
def _call_widget_constructed(widget):
"""Static method, called when a widget is constructed."""
if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
Widget._widget_construction_callback(widget)
@staticmethod
def handle_comm_opened(comm, msg):
"""Static method, called when a widget is constructed."""
widget_class = import_item(msg['content']['data']['widget_class'])
widget = widget_class(comm=comm)
#-------------------------------------------------------------------------
# Traits
#-------------------------------------------------------------------------
_model_module = Unicode(None, allow_none=True, help="""A requirejs module name
in which to find _model_name. If empty, look in the global registry.""")
_model_name = Unicode('WidgetModel', help="""Name of the backbone model
registered in the front-end to create and sync this widget with.""")
_view_module = Unicode(help="""A requirejs module in which to find _view_name.
If empty, look in the global registry.""", sync=True)
_view_name = Unicode(None, allow_none=True, help="""Default view registered in the front-end
to use to represent the widget.""", sync=True)
comm = Instance('ipython_kernel.comm.Comm', allow_none=True)
msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
front-end can send before receiving an idle msg from the back-end.""")
version = Int(0, sync=True, help="""Widget's version""")
keys = List()
def _keys_default(self):
return [name for name in self.traits(sync=True)]
_property_lock = Tuple((None, None))
_send_state_lock = Int(0)
_states_to_send = Set()
_display_callbacks = Instance(CallbackDispatcher, ())
_msg_callbacks = Instance(CallbackDispatcher, ())
#-------------------------------------------------------------------------
# (Con/de)structor
#-------------------------------------------------------------------------
def __init__(self, **kwargs):
"""Public constructor"""
self._model_id = kwargs.pop('model_id', None)
super(Widget, self).__init__(**kwargs)
Widget._call_widget_constructed(self)
self.open()
def __del__(self):
"""Object disposal"""
self.close()
#-------------------------------------------------------------------------
# Properties
#-------------------------------------------------------------------------
def open(self):
"""Open a comm to the frontend if one isn't already open."""
if self.comm is None:
args = dict(target_name='ipython.widget',
data={'model_name': self._model_name,
'model_module': self._model_module})
if self._model_id is not None:
args['comm_id'] = self._model_id
self.comm = Comm(**args)
def _comm_changed(self, name, new):
"""Called when the comm is changed."""
if new is None:
return
self._model_id = self.model_id
self.comm.on_msg(self._handle_msg)
Widget.widgets[self.model_id] = self
# first update
self.send_state()
@property
def model_id(self):
"""Gets the model id of this widget.
If a Comm doesn't exist yet, a Comm will be created automagically."""
return self.comm.comm_id
#-------------------------------------------------------------------------
# Methods
#-------------------------------------------------------------------------
def close(self):
"""Close method.
Closes the underlying comm.
When the comm is closed, all of the widget views are automatically
removed from the front-end."""
if self.comm is not None:
Widget.widgets.pop(self.model_id, None)
self.comm.close()
self.comm = None
def send_state(self, key=None):
"""Sends the widget state, or a piece of it, to the front-end.
Parameters
----------
key : unicode, or iterable (optional)
A single property's name or iterable of property names to sync with the front-end.
"""
state, buffer_keys, buffers = self.get_state(key=key)
msg = {"method": "update", "state": state}
if buffer_keys:
msg['buffers'] = buffer_keys
self._send(msg, buffers=buffers)
def get_state(self, key=None):
"""Gets the widget state, or a piece of it.
Parameters
----------
key : unicode or iterable (optional)
A single property's name or iterable of property names to get.
Returns
-------
state : dict of states
buffer_keys : list of strings
the values that are stored in buffers
buffers : list of binary memoryviews
values to transmit in binary
metadata : dict
metadata for each field: {key: metadata}
"""
if key is None:
keys = self.keys
elif isinstance(key, string_types):
keys = [key]
elif isinstance(key, collections.Iterable):
keys = key
else:
raise ValueError("key must be a string, an iterable of keys, or None")
state = {}
buffers = []
buffer_keys = []
for k in keys:
f = self.trait_metadata(k, 'to_json', self._trait_to_json)
value = getattr(self, k)
serialized = f(value)
if isinstance(serialized, memoryview):
buffers.append(serialized)
buffer_keys.append(k)
else:
state[k] = serialized
return state, buffer_keys, buffers
def set_state(self, sync_data):
"""Called when a state is received from the front-end."""
for name in self.keys:
if name in sync_data:
json_value = sync_data[name]
from_json = self.trait_metadata(name, 'from_json', self._trait_from_json)
with self._lock_property(name, json_value):
setattr(self, name, from_json(json_value))
def send(self, content, buffers=None):
"""Sends a custom msg to the widget model in the front-end.
Parameters
----------
content : dict
Content of the message to send.
buffers : list of binary buffers
Binary buffers to send with message
"""
self._send({"method": "custom", "content": content}, buffers=buffers)
def on_msg(self, callback, remove=False):
"""(Un)Register a custom msg receive callback.
Parameters
----------
callback: callable
callback will be passed three arguments when a message arrives::
callback(widget, content, buffers)
remove: bool
True if the callback should be unregistered."""
self._msg_callbacks.register_callback(callback, remove=remove)
def on_displayed(self, callback, remove=False):
"""(Un)Register a widget displayed callback.
Parameters
----------
callback: method handler
Must have a signature of::
callback(widget, **kwargs)
kwargs from display are passed through without modification.
remove: bool
True if the callback should be unregistered."""
self._display_callbacks.register_callback(callback, remove=remove)
def add_trait(self, traitname, trait):
"""Dynamically add a trait attribute to the Widget."""
super(Widget, self).add_trait(traitname, trait)
if trait.get_metadata('sync'):
self.keys.append(traitname)
self.send_state(traitname)
#-------------------------------------------------------------------------
# Support methods
#-------------------------------------------------------------------------
@contextmanager
def _lock_property(self, key, value):
"""Lock a property-value pair.
The value should be the JSON state of the property.
NOTE: This, in addition to the single lock for all state changes, is
flawed. In the future we may want to look into buffering state changes
back to the front-end."""
self._property_lock = (key, value)
try:
yield
finally:
self._property_lock = (None, None)
@contextmanager
def hold_sync(self):
"""Hold syncing any state until the context manager is released"""
# We increment a value so that this can be nested. Syncing will happen when
# all levels have been released.
self._send_state_lock += 1
try:
yield
finally:
self._send_state_lock -=1
if self._send_state_lock == 0:
self.send_state(self._states_to_send)
self._states_to_send.clear()
def _should_send_property(self, key, value):
"""Check the property lock (property_lock)"""
to_json = self.trait_metadata(key, 'to_json', self._trait_to_json)
if (key == self._property_lock[0]
and to_json(value) == self._property_lock[1]):
return False
elif self._send_state_lock > 0:
self._states_to_send.add(key)
return False
else:
return True
# Event handlers
@_show_traceback
def _handle_msg(self, msg):
"""Called when a msg is received from the front-end"""
data = msg['content']['data']
method = data['method']
# Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
if method == 'backbone':
if 'sync_data' in data:
# get binary buffers too
sync_data = data['sync_data']
for i,k in enumerate(data.get('buffer_keys', [])):
sync_data[k] = msg['buffers'][i]
self.set_state(sync_data) # handles all methods
# Handle a state request.
elif method == 'request_state':
self.send_state()
# Handle a custom msg from the front-end.
elif method == 'custom':
if 'content' in data:
self._handle_custom_msg(data['content'], msg['buffers'])
# Catch remainder.
else:
self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
def _handle_custom_msg(self, content, buffers):
"""Called when a custom msg is received."""
self._msg_callbacks(self, content, buffers)
def _notify_trait(self, name, old_value, new_value):
"""Called when a property has been changed."""
# Trigger default traitlet callback machinery. This allows any user
# registered validation to be processed prior to allowing the widget
# machinery to handle the state.
LoggingConfigurable._notify_trait(self, name, old_value, new_value)
# Send the state after the user registered callbacks for trait changes
# have all fired (allows for user to validate values).
if self.comm is not None and name in self.keys:
# Make sure this isn't information that the front-end just sent us.
if self._should_send_property(name, new_value):
# Send new state to front-end
self.send_state(key=name)
def _handle_displayed(self, **kwargs):
"""Called when a view has been displayed for this widget instance"""
self._display_callbacks(self, **kwargs)
def _trait_to_json(self, x):
"""Convert a trait value to json."""
return x
def _trait_from_json(self, x):
"""Convert json values to objects."""
return x
def _ipython_display_(self, **kwargs):
"""Called when `IPython.display.display` is called on the widget."""
# Show view.
if self._view_name is not None:
self._send({"method": "display"})
self._handle_displayed(**kwargs)
def _send(self, msg, buffers=None):
"""Sends a message to the model in the front-end."""
self.comm.send(data=msg, buffers=buffers)
class DOMWidget(Widget):
visible = Bool(True, allow_none=True, help="Whether the widget is visible. False collapses the empty space, while None preserves the empty space.", sync=True)
_css = Tuple(sync=True, help="CSS property list: (selector, key, value)")
_dom_classes = Tuple(sync=True, help="DOM classes applied to widget.$el.")
width = CUnicode(sync=True)
height = CUnicode(sync=True)
# A default padding of 2.5 px makes the widgets look nice when displayed inline.
padding = CUnicode(sync=True)
margin = CUnicode(sync=True)
color = Color(None, allow_none=True, sync=True)
background_color = Color(None, allow_none=True, sync=True)
border_color = Color(None, allow_none=True, sync=True)
border_width = CUnicode(sync=True)
border_radius = CUnicode(sync=True)
border_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_border-style.asp
'none',
'hidden',
'dotted',
'dashed',
'solid',
'double',
'groove',
'ridge',
'inset',
'outset',
'initial',
'inherit', ''],
default_value='', sync=True)
font_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_font-style.asp
'normal',
'italic',
'oblique',
'initial',
'inherit', ''],
default_value='', sync=True)
font_weight = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_weight.asp
'normal',
'bold',
'bolder',
'lighter',
'initial',
'inherit', ''] + list(map(str, range(100,1000,100))),
default_value='', sync=True)
font_size = CUnicode(sync=True)
font_family = Unicode(sync=True)
def __init__(self, *pargs, **kwargs):
super(DOMWidget, self).__init__(*pargs, **kwargs)
def _validate_border(name, old, new):
if new is not None and new != '':
if name != 'border_width' and not self.border_width:
self.border_width = 1
if name != 'border_style' and self.border_style == '':
self.border_style = 'solid'
self.on_trait_change(_validate_border, ['border_width', 'border_style', 'border_color'])

@ -1,80 +0,0 @@
"""Bool class.
Represents a boolean using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, register
from traitlets import Unicode, Bool, CaselessStrEnum
from .deprecated import DeprecatedClass
class _Bool(DOMWidget):
"""A base class for creating widgets that represent booleans."""
value = Bool(False, help="Bool value", sync=True)
description = Unicode('', help="Description of the boolean (label).", sync=True)
disabled = Bool(False, help="Enable or disable user changes.", sync=True)
def __init__(self, value=None, **kwargs):
if value is not None:
kwargs['value'] = value
super(_Bool, self).__init__(**kwargs)
@register('IPython.Checkbox')
class Checkbox(_Bool):
"""Displays a boolean `value` in the form of a checkbox.
Parameters
----------
value : {True,False}
value of the checkbox: True-checked, False-unchecked
description : str
description displayed next to the checkbox
"""
_view_name = Unicode('CheckboxView', sync=True)
@register('IPython.ToggleButton')
class ToggleButton(_Bool):
"""Displays a boolean `value` in the form of a toggle button.
Parameters
----------
value : {True,False}
value of the toggle button: True-pressed, False-unpressed
description : str
description displayed next to the button
tooltip: str
tooltip caption of the toggle button
icon: str
font-awesome icon name
"""
_view_name = Unicode('ToggleButtonView', sync=True)
tooltip = Unicode(help="Tooltip caption of the toggle button.", sync=True)
icon = Unicode('', help= "Font-awesome icon.", sync=True)
button_style = CaselessStrEnum(
values=['primary', 'success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the button.""")
@register('IPython.Valid')
class Valid(_Bool):
"""Displays a boolean `value` in the form of a green check (True / valid)
or a red cross (False / invalid).
Parameters
----------
value: {True,False}
value of the Valid widget
"""
readout = Unicode(help="Message displayed when the value is False", sync=True)
_view_name = Unicode('ValidView', sync=True)
# Remove in IPython 4.0
CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')

@ -1,82 +0,0 @@
"""Box class.
Represents a container that can be used to group other widgets.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, Widget, register, widget_serialization
from traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
from .deprecated import DeprecatedClass
@register('IPython.Box')
class Box(DOMWidget):
"""Displays multiple widgets in a group."""
_model_name = Unicode('BoxModel', sync=True)
_view_name = Unicode('BoxView', sync=True)
# Child widgets in the container.
# Using a tuple here to force reassignment to update the list.
# When a proper notifying-list trait exists, that is what should be used here.
children = Tuple(sync=True, **widget_serialization)
_overflow_values = ['visible', 'hidden', 'scroll', 'auto', 'initial', 'inherit', '']
overflow_x = CaselessStrEnum(
values=_overflow_values,
default_value='', sync=True, help="""Specifies what
happens to content that is too large for the rendered region.""")
overflow_y = CaselessStrEnum(
values=_overflow_values,
default_value='', sync=True, help="""Specifies what
happens to content that is too large for the rendered region.""")
box_style = CaselessStrEnum(
values=['success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the box.""")
def __init__(self, children = (), **kwargs):
kwargs['children'] = children
super(Box, self).__init__(**kwargs)
self.on_displayed(Box._fire_children_displayed)
def _fire_children_displayed(self):
for child in self.children:
child._handle_displayed()
@register('IPython.FlexBox')
class FlexBox(Box):
"""Displays multiple widgets using the flexible box model."""
_view_name = Unicode('FlexBoxView', sync=True)
orientation = CaselessStrEnum(values=['vertical', 'horizontal'], default_value='vertical', sync=True)
flex = Int(0, sync=True, help="""Specify the flexible-ness of the model.""")
def _flex_changed(self, name, old, new):
new = min(max(0, new), 2)
if self.flex != new:
self.flex = new
_locations = ['start', 'center', 'end', 'baseline', 'stretch']
pack = CaselessStrEnum(
values=_locations,
default_value='start', sync=True)
align = CaselessStrEnum(
values=_locations,
default_value='start', sync=True)
def VBox(*pargs, **kwargs):
"""Displays multiple widgets vertically using the flexible box model."""
kwargs['orientation'] = 'vertical'
return FlexBox(*pargs, **kwargs)
def HBox(*pargs, **kwargs):
"""Displays multiple widgets horizontally using the flexible box model."""
kwargs['orientation'] = 'horizontal'
return FlexBox(*pargs, **kwargs)
# Remove in IPython 4.0
ContainerWidget = DeprecatedClass(Box, 'ContainerWidget')

@ -1,73 +0,0 @@
"""Button class.
Represents a button in the frontend using a widget. Allows user to listen for
click events on the button and trigger backend code when the clicks are fired.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, CallbackDispatcher, register
from traitlets import Unicode, Bool, CaselessStrEnum
from .deprecated import DeprecatedClass
@register('IPython.Button')
class Button(DOMWidget):
"""Button widget.
This widget has an `on_click` method that allows you to listen for the
user clicking on the button. The click event itself is stateless.
Parameters
----------
description : str
description displayed next to the button
tooltip: str
tooltip caption of the toggle button
icon: str
font-awesome icon name
"""
_view_name = Unicode('ButtonView', sync=True)
# Keys
description = Unicode('', help="Button label.", sync=True)
tooltip = Unicode(help="Tooltip caption of the button.", sync=True)
disabled = Bool(False, help="Enable or disable user changes.", sync=True)
icon = Unicode('', help= "Font-awesome icon.", sync=True)
button_style = CaselessStrEnum(
values=['primary', 'success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the button.""")
def __init__(self, **kwargs):
"""Constructor"""
super(Button, self).__init__(**kwargs)
self._click_handlers = CallbackDispatcher()
self.on_msg(self._handle_button_msg)
def on_click(self, callback, remove=False):
"""Register a callback to execute when the button is clicked.
The callback will be called with one argument,
the clicked button widget instance.
Parameters
----------
remove : bool (optional)
Set to true to remove the callback from the list of callbacks."""
self._click_handlers.register_callback(callback, remove=remove)
def _handle_button_msg(self, _, content, buffers):
"""Handle a msg from the front-end.
Parameters
----------
content: dict
Content of the msg."""
if content.get('event', '') == 'click':
self._click_handlers(self)
# Remove in IPython 4.0
ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')

@ -1,287 +0,0 @@
"""Float class.
Represents an unbounded float using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, register
from .trait_types import Color
from traitlets import (Unicode, CFloat, Bool, CaselessStrEnum,
Tuple, TraitError)
from .deprecated import DeprecatedClass
class _Float(DOMWidget):
value = CFloat(0.0, help="Float value", sync=True)
disabled = Bool(False, help="Enable or disable user changes", sync=True)
description = Unicode(help="Description of the value this widget represents", sync=True)
def __init__(self, value=None, **kwargs):
if value is not None:
kwargs['value'] = value
super(_Float, self).__init__(**kwargs)
class _BoundedFloat(_Float):
max = CFloat(100.0, help="Max value", sync=True)
min = CFloat(0.0, help="Min value", sync=True)
step = CFloat(0.1, help="Minimum step to increment the value (ignored by some views)", sync=True)
def __init__(self, *pargs, **kwargs):
"""Constructor"""
super(_BoundedFloat, self).__init__(*pargs, **kwargs)
def _value_validate(self, value, trait):
"""Cap and floor value"""
if self.min > value or self.max < value:
value = min(max(value, self.min), self.max)
return value
def _min_validate(self, min, trait):
"""Enforce min <= value <= max"""
if min > self.max:
raise TraitError("Setting min > max")
if min > self.value:
self.value = min
return min
def _max_validate(self, max, trait):
"""Enforce min <= value <= max"""
if max < self.min:
raise TraitError("setting max < min")
if max < self.value:
self.value = max
return max
@register('IPython.FloatText')
class FloatText(_Float):
""" Displays a float value within a textbox. For a textbox in
which the value must be within a specific range, use BoundedFloatText.
Parameters
----------
value : float
value displayed
description : str
description displayed next to the text box
color : str Unicode color code (eg. '#C13535'), optional
color of the value displayed
"""
_view_name = Unicode('FloatTextView', sync=True)
@register('IPython.BoundedFloatText')
class BoundedFloatText(_BoundedFloat):
""" Displays a float value within a textbox. Value must be within the range specified.
For a textbox in which the value doesn't need to be within a specific range, use FloatText.
Parameters
----------
value : float
value displayed
min : float
minimal value of the range of possible values displayed
max : float
maximal value of the range of possible values displayed
description : str
description displayed next to the textbox
color : str Unicode color code (eg. '#C13535'), optional
color of the value displayed
"""
_view_name = Unicode('FloatTextView', sync=True)
@register('IPython.FloatSlider')
class FloatSlider(_BoundedFloat):
""" Slider/trackbar of floating values with the specified range.
Parameters
----------
value : float
position of the slider
min : float
minimal position of the slider
max : float
maximal position of the slider
step : float
step of the trackbar
description : str
name of the slider
orientation : {'vertical', 'horizontal}, optional
default is horizontal
readout : {True, False}, optional
default is True, display the current value of the slider next to it
slider_color : str Unicode color code (eg. '#C13535'), optional
color of the slider
color : str Unicode color code (eg. '#C13535'), optional
color of the value displayed (if readout == True)
"""
_view_name = Unicode('FloatSliderView', sync=True)
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
default_value='horizontal', help="Vertical or horizontal.", sync=True)
_range = Bool(False, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
slider_color = Color(None, allow_none=True, sync=True)
@register('IPython.FloatProgress')
class FloatProgress(_BoundedFloat):
""" Displays a progress bar.
Parameters
-----------
value : float
position within the range of the progress bar
min : float
minimal position of the slider
max : float
maximal position of the slider
step : float
step of the progress bar
description : str
name of the progress bar
bar_style: {'success', 'info', 'warning', 'danger', ''}, optional
color of the progress bar, default is '' (blue)
colors are: 'success'-green, 'info'-light blue, 'warning'-orange, 'danger'-red
"""
_view_name = Unicode('ProgressView', sync=True)
bar_style = CaselessStrEnum(
values=['success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the progess bar.""")
class _FloatRange(_Float):
value = Tuple(CFloat, CFloat, default_value=(0.0, 1.0), help="Tuple of (lower, upper) bounds", sync=True)
lower = CFloat(0.0, help="Lower bound", sync=False)
upper = CFloat(1.0, help="Upper bound", sync=False)
def __init__(self, *pargs, **kwargs):
value_given = 'value' in kwargs
lower_given = 'lower' in kwargs
upper_given = 'upper' in kwargs
if value_given and (lower_given or upper_given):
raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
if lower_given != upper_given:
raise ValueError("Must specify both 'lower' and 'upper' for range widget")
DOMWidget.__init__(self, *pargs, **kwargs)
# ensure the traits match, preferring whichever (if any) was given in kwargs
if value_given:
self.lower, self.upper = self.value
else:
self.value = (self.lower, self.upper)
self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
def _validate(self, name, old, new):
if name == 'value':
self.lower, self.upper = min(new), max(new)
elif name == 'lower':
self.value = (new, self.value[1])
elif name == 'upper':
self.value = (self.value[0], new)
class _BoundedFloatRange(_FloatRange):
step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)", sync=True)
max = CFloat(100.0, help="Max value", sync=True)
min = CFloat(0.0, help="Min value", sync=True)
def __init__(self, *pargs, **kwargs):
any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
_FloatRange.__init__(self, *pargs, **kwargs)
# ensure a minimal amount of sanity
if self.min > self.max:
raise ValueError("min must be <= max")
if any_value_given:
# if a value was given, clamp it within (min, max)
self._validate("value", None, self.value)
else:
# otherwise, set it to 25-75% to avoid the handles overlapping
self.value = (0.75*self.min + 0.25*self.max,
0.25*self.min + 0.75*self.max)
# callback already set for 'value', 'lower', 'upper'
self.on_trait_change(self._validate, ['min', 'max'])
def _validate(self, name, old, new):
if name == "min":
if new > self.max:
raise ValueError("setting min > max")
self.min = new
elif name == "max":
if new < self.min:
raise ValueError("setting max < min")
self.max = new
low, high = self.value
if name == "value":
low, high = min(new), max(new)
elif name == "upper":
if new < self.lower:
raise ValueError("setting upper < lower")
high = new
elif name == "lower":
if new > self.upper:
raise ValueError("setting lower > upper")
low = new
low = max(self.min, min(low, self.max))
high = min(self.max, max(high, self.min))
# determine the order in which we should update the
# lower, upper traits to avoid a temporary inverted overlap
lower_first = high < self.lower
self.value = (low, high)
if lower_first:
self.lower = low
self.upper = high
else:
self.upper = high
self.lower = low
@register('IPython.FloatRangeSlider')
class FloatRangeSlider(_BoundedFloatRange):
""" Slider/trackbar for displaying a floating value range (within the specified range of values).
Parameters
----------
value : float tuple
range of the slider displayed
min : float
minimal position of the slider
max : float
maximal position of the slider
step : float
step of the trackbar
description : str
name of the slider
orientation : {'vertical', 'horizontal}, optional
default is horizontal
readout : {True, False}, optional
default is True, display the current value of the slider next to it
slider_color : str Unicode color code (eg. '#C13535'), optional
color of the slider
color : str Unicode color code (eg. '#C13535'), optional
color of the value displayed (if readout == True)
"""
_view_name = Unicode('FloatSliderView', sync=True)
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
default_value='horizontal', help="Vertical or horizontal.", sync=True)
_range = Bool(True, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
slider_color = Color(None, allow_none=True, sync=True)
# Remove in IPython 4.0
FloatTextWidget = DeprecatedClass(FloatText, 'FloatTextWidget')
BoundedFloatTextWidget = DeprecatedClass(BoundedFloatText, 'BoundedFloatTextWidget')
FloatSliderWidget = DeprecatedClass(FloatSlider, 'FloatSliderWidget')
FloatProgressWidget = DeprecatedClass(FloatProgress, 'FloatProgressWidget')

@ -1,37 +0,0 @@
"""Image class.
Represents an image in the frontend using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import base64
from .widget import DOMWidget, register
from traitlets import Unicode, CUnicode, Bytes
from .deprecated import DeprecatedClass
@register('IPython.Image')
class Image(DOMWidget):
"""Displays an image as a widget.
The `value` of this widget accepts a byte string. The byte string is the raw
image data that you want the browser to display. You can explicitly define
the format of the byte string using the `format` trait (which defaults to
"png")."""
_view_name = Unicode('ImageView', sync=True)
# Define the custom state properties to sync with the front-end
format = Unicode('png', sync=True)
width = CUnicode(sync=True)
height = CUnicode(sync=True)
_b64value = Unicode(sync=True)
value = Bytes()
def _value_changed(self, name, old, new):
self._b64value = base64.b64encode(new)
# Remove in IPython 4.0
ImageWidget = DeprecatedClass(Image, 'ImageWidget')

@ -1,198 +0,0 @@
"""Int class.
Represents an unbounded int using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, register
from .trait_types import Color
from traitlets import (Unicode, CInt, Bool, CaselessStrEnum,
Tuple, TraitError)
from .deprecated import DeprecatedClass
class _Int(DOMWidget):
"""Base class used to create widgets that represent an int."""
value = CInt(0, help="Int value", sync=True)
disabled = Bool(False, help="Enable or disable user changes", sync=True)
description = Unicode(help="Description of the value this widget represents", sync=True)
def __init__(self, value=None, **kwargs):
if value is not None:
kwargs['value'] = value
super(_Int, self).__init__(**kwargs)
class _BoundedInt(_Int):
"""Base class used to create widgets that represent a int that is bounded
by a minium and maximum."""
step = CInt(1, help="Minimum step to increment the value (ignored by some views)", sync=True)
max = CInt(100, help="Max value", sync=True)
min = CInt(0, help="Min value", sync=True)
def __init__(self, *pargs, **kwargs):
"""Constructor"""
super(_BoundedInt, self).__init__(*pargs, **kwargs)
def _value_validate(self, value, trait):
"""Cap and floor value"""
if self.min > value or self.max < value:
value = min(max(value, self.min), self.max)
return value
def _min_validate(self, min, trait):
"""Enforce min <= value <= max"""
if min > self.max:
raise TraitError("Setting min > max")
if min > self.value:
self.value = min
return min
def _max_validate(self, max, trait):
"""Enforce min <= value <= max"""
if max < self.min:
raise TraitError("setting max < min")
if max < self.value:
self.value = max
return max
@register('IPython.IntText')
class IntText(_Int):
"""Textbox widget that represents a int."""
_view_name = Unicode('IntTextView', sync=True)
@register('IPython.BoundedIntText')
class BoundedIntText(_BoundedInt):
"""Textbox widget that represents a int bounded by a minimum and maximum value."""
_view_name = Unicode('IntTextView', sync=True)
@register('IPython.IntSlider')
class IntSlider(_BoundedInt):
"""Slider widget that represents a int bounded by a minimum and maximum value."""
_view_name = Unicode('IntSliderView', sync=True)
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
default_value='horizontal', help="Vertical or horizontal.", sync=True)
_range = Bool(False, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
slider_color = Color(None, allow_none=True, sync=True)
@register('IPython.IntProgress')
class IntProgress(_BoundedInt):
"""Progress bar that represents a int bounded by a minimum and maximum value."""
_view_name = Unicode('ProgressView', sync=True)
bar_style = CaselessStrEnum(
values=['success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the progess bar.""")
class _IntRange(_Int):
value = Tuple(CInt, CInt, default_value=(0, 1), help="Tuple of (lower, upper) bounds", sync=True)
lower = CInt(0, help="Lower bound", sync=False)
upper = CInt(1, help="Upper bound", sync=False)
def __init__(self, *pargs, **kwargs):
value_given = 'value' in kwargs
lower_given = 'lower' in kwargs
upper_given = 'upper' in kwargs
if value_given and (lower_given or upper_given):
raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
if lower_given != upper_given:
raise ValueError("Must specify both 'lower' and 'upper' for range widget")
super(_IntRange, self).__init__(*pargs, **kwargs)
# ensure the traits match, preferring whichever (if any) was given in kwargs
if value_given:
self.lower, self.upper = self.value
else:
self.value = (self.lower, self.upper)
self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
def _validate(self, name, old, new):
if name == 'value':
self.lower, self.upper = min(new), max(new)
elif name == 'lower':
self.value = (new, self.value[1])
elif name == 'upper':
self.value = (self.value[0], new)
class _BoundedIntRange(_IntRange):
step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
max = CInt(100, help="Max value", sync=True)
min = CInt(0, help="Min value", sync=True)
def __init__(self, *pargs, **kwargs):
any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
_IntRange.__init__(self, *pargs, **kwargs)
# ensure a minimal amount of sanity
if self.min > self.max:
raise ValueError("min must be <= max")
if any_value_given:
# if a value was given, clamp it within (min, max)
self._validate("value", None, self.value)
else:
# otherwise, set it to 25-75% to avoid the handles overlapping
self.value = (0.75*self.min + 0.25*self.max,
0.25*self.min + 0.75*self.max)
# callback already set for 'value', 'lower', 'upper'
self.on_trait_change(self._validate, ['min', 'max'])
def _validate(self, name, old, new):
if name == "min":
if new > self.max:
raise ValueError("setting min > max")
elif name == "max":
if new < self.min:
raise ValueError("setting max < min")
low, high = self.value
if name == "value":
low, high = min(new), max(new)
elif name == "upper":
if new < self.lower:
raise ValueError("setting upper < lower")
high = new
elif name == "lower":
if new > self.upper:
raise ValueError("setting lower > upper")
low = new
low = max(self.min, min(low, self.max))
high = min(self.max, max(high, self.min))
# determine the order in which we should update the
# lower, upper traits to avoid a temporary inverted overlap
lower_first = high < self.lower
self.value = (low, high)
if lower_first:
self.lower = low
self.upper = high
else:
self.upper = high
self.lower = low
@register('IPython.IntRangeSlider')
class IntRangeSlider(_BoundedIntRange):
"""Slider widget that represents a pair of ints between a minimum and maximum value."""
_view_name = Unicode('IntSliderView', sync=True)
orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
default_value='horizontal', help="Vertical or horizontal.", sync=True)
_range = Bool(True, help="Display a range selector", sync=True)
readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
slider_color = Color(None, allow_none=True, sync=True)
# Remove in IPython 4.0
IntTextWidget = DeprecatedClass(IntText, 'IntTextWidget')
BoundedIntTextWidget = DeprecatedClass(BoundedIntText, 'BoundedIntTextWidget')
IntSliderWidget = DeprecatedClass(IntSlider, 'IntSliderWidget')
IntProgressWidget = DeprecatedClass(IntProgress, 'IntProgressWidget')

@ -1,108 +0,0 @@
"""Link and DirectionalLink classes.
Propagate changes between widgets on the javascript side
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import Widget
from traitlets import Unicode, Tuple, List,Instance, TraitError
class WidgetTraitTuple(Tuple):
"""Traitlet for validating a single (Widget, 'trait_name') pair"""
def __init__(self, **kwargs):
super(WidgetTraitTuple, self).__init__(Instance(Widget), Unicode, **kwargs)
def validate_elements(self, obj, value):
value = super(WidgetTraitTuple, self).validate_elements(obj, value)
widget, trait_name = value
trait = widget.traits().get(trait_name)
trait_repr = "%s.%s" % (widget.__class__.__name__, trait_name)
# Can't raise TraitError because the parent will swallow the message
# and throw it away in a new, less informative TraitError
if trait is None:
raise TypeError("No such trait: %s" % trait_repr)
elif not trait.get_metadata('sync'):
raise TypeError("%s cannot be synced" % trait_repr)
return value
class Link(Widget):
"""Link Widget
one trait:
widgets, a list of (widget, 'trait_name') tuples which should be linked in the frontend.
"""
_model_name = Unicode('LinkModel', sync=True)
widgets = List(WidgetTraitTuple, sync=True)
def __init__(self, widgets, **kwargs):
if len(widgets) < 2:
raise TypeError("Require at least two widgets to link")
kwargs['widgets'] = widgets
super(Link, self).__init__(**kwargs)
# for compatibility with traitlet links
def unlink(self):
self.close()
def jslink(*args):
"""Link traits from different widgets together on the frontend so they remain in sync.
Parameters
----------
*args : two or more (Widget, 'trait_name') tuples that should be kept in sync.
Examples
--------
>>> c = link((widget1, 'value'), (widget2, 'value'), (widget3, 'value'))
"""
return Link(widgets=args)
class DirectionalLink(Widget):
"""A directional link
source: a (Widget, 'trait_name') tuple for the source trait
targets: one or more (Widget, 'trait_name') tuples that should be updated
when the source trait changes.
"""
_model_name = Unicode('DirectionalLinkModel', sync=True)
targets = List(WidgetTraitTuple, sync=True)
source = WidgetTraitTuple(sync=True)
# Does not quite behave like other widgets but reproduces
# the behavior of traitlets.directional_link
def __init__(self, source, targets, **kwargs):
if len(targets) < 1:
raise TypeError("Require at least two widgets to link")
kwargs['source'] = source
kwargs['targets'] = targets
super(DirectionalLink, self).__init__(**kwargs)
# for compatibility with traitlet links
def unlink(self):
self.close()
def jsdlink(source, *targets):
"""Link the trait of a source widget with traits of target widgets in the frontend.
Parameters
----------
source : a (Widget, 'trait_name') tuple for the source trait
*targets : one or more (Widget, 'trait_name') tuples that should be updated
when the source trait changes.
Examples
--------
>>> c = dlink((src_widget, 'value'), (tgt_widget1, 'value'), (tgt_widget2, 'value'))
"""
return DirectionalLink(source=source, targets=targets)

@ -1,76 +0,0 @@
"""Output class.
Represents a widget that can be used to display output within the widget area.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget
import sys
from traitlets import Unicode, List
from IPython.display import clear_output
from jupyter_client.session import Message
class Output(DOMWidget):
"""Widget used as a context manager to display output.
This widget can capture and display stdout, stderr, and rich output. To use
it, create an instance of it and display it. Then use it as a context
manager. Any output produced while in it's context will be captured and
displayed in it instead of the standard output area.
Example
from jupyter_notebook import widgets
from IPython.display import display
out = widgets.Output()
display(out)
print('prints to output area')
with out:
print('prints to output widget')"""
_view_name = Unicode('OutputView', sync=True)
def clear_output(self, *pargs, **kwargs):
with self:
clear_output(*pargs, **kwargs)
def __enter__(self):
"""Called upon entering output widget context manager."""
self._flush()
kernel = get_ipython().kernel
session = kernel.session
send = session.send
self._original_send = send
self._session = session
def send_hook(stream, msg_or_type, content=None, parent=None, ident=None,
buffers=None, track=False, header=None, metadata=None):
# Handle both prebuild messages and unbuilt messages.
if isinstance(msg_or_type, (Message, dict)):
msg_type = msg_or_type['msg_type']
msg = dict(msg_or_type)
else:
msg_type = msg_or_type
msg = session.msg(msg_type, content=content, parent=parent,
header=header, metadata=metadata)
# If this is a message type that we want to forward, forward it.
if stream is kernel.iopub_socket and msg_type in ['clear_output', 'stream', 'display_data']:
self.send(msg)
else:
send(stream, msg, ident=ident, buffers=buffers, track=track)
session.send = send_hook
def __exit__(self, exception_type, exception_value, traceback):
"""Called upon exiting output widget context manager."""
self._flush()
self._session.send = self._original_send
def _flush(self):
"""Flush stdout and stderr buffers."""
sys.stdout.flush()
sys.stderr.flush()

@ -1,238 +0,0 @@
"""Selection classes.
Represents an enumeration using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from collections import OrderedDict
from threading import Lock
from .widget import DOMWidget, register
from traitlets import (
Unicode, Bool, Any, Dict, TraitError, CaselessStrEnum, Tuple, List
)
from ipython_genutils.py3compat import unicode_type
from .deprecated import DeprecatedClass
class _Selection(DOMWidget):
"""Base class for Selection widgets
``options`` can be specified as a list or dict. If given as a list,
it will be transformed to a dict of the form ``{str(value):value}``.
When programmatically setting the value, a reverse lookup is performed
among the options to set the value of ``selected_label`` accordingly. The
reverse lookup uses the equality operator by default, but an other
predicate may be provided via the ``equals`` argument. For example, when
dealing with numpy arrays, one may set equals=np.array_equal.
"""
value = Any(help="Selected value")
selected_label = Unicode(help="The label of the selected value", sync=True)
options = Any(help="""List of (key, value) tuples or dict of values that the
user can select.
The keys of this list are the strings that will be displayed in the UI,
representing the actual Python choices.
The keys of this list are also available as _options_labels.
""")
_options_dict = Dict()
_options_labels = Tuple(sync=True)
_options_values = Tuple()
disabled = Bool(False, help="Enable or disable user changes", sync=True)
description = Unicode(help="Description of the value this widget represents", sync=True)
def __init__(self, *args, **kwargs):
self.value_lock = Lock()
self.options_lock = Lock()
self.equals = kwargs.pop('equals', lambda x, y: x == y)
self.on_trait_change(self._options_readonly_changed, ['_options_dict', '_options_labels', '_options_values', '_options'])
if 'options' in kwargs:
self.options = kwargs.pop('options')
DOMWidget.__init__(self, *args, **kwargs)
self._value_in_options()
def _make_options(self, x):
# If x is a dict, convert it to list format.
if isinstance(x, (OrderedDict, dict)):
return [(k, v) for k, v in x.items()]
# Make sure x is a list or tuple.
if not isinstance(x, (list, tuple)):
raise ValueError('x')
# If x is an ordinary list, use the option values as names.
for y in x:
if not isinstance(y, (list, tuple)) or len(y) < 2:
return [(i, i) for i in x]
# Value is already in the correct format.
return x
def _options_changed(self, name, old, new):
"""Handles when the options tuple has been changed.
Setting options implies setting option labels from the keys of the dict.
"""
if self.options_lock.acquire(False):
try:
self.options = new
options = self._make_options(new)
self._options_dict = {i[0]: i[1] for i in options}
self._options_labels = [i[0] for i in options]
self._options_values = [i[1] for i in options]
self._value_in_options()
finally:
self.options_lock.release()
def _value_in_options(self):
# ensure that the chosen value is one of the choices
if self._options_values:
if self.value not in self._options_values:
self.value = next(iter(self._options_values))
def _options_readonly_changed(self, name, old, new):
if not self.options_lock.locked():
raise TraitError("`.%s` is a read-only trait. Use the `.options` tuple instead." % name)
def _value_changed(self, name, old, new):
"""Called when value has been changed"""
if self.value_lock.acquire(False):
try:
# Reverse dictionary lookup for the value name
for k, v in self._options_dict.items():
if self.equals(new, v):
# set the selected value name
self.selected_label = k
return
# undo the change, and raise KeyError
self.value = old
raise KeyError(new)
finally:
self.value_lock.release()
def _selected_label_changed(self, name, old, new):
"""Called when the value name has been changed (typically by the frontend)."""
if self.value_lock.acquire(False):
try:
self.value = self._options_dict[new]
finally:
self.value_lock.release()
class _MultipleSelection(_Selection):
"""Base class for MultipleSelection widgets.
As with ``_Selection``, ``options`` can be specified as a list or dict. If
given as a list, it will be transformed to a dict of the form
``{str(value): value}``.
Despite their names, ``value`` (and ``selected_label``) will be tuples, even
if only a single option is selected.
"""
value = Tuple(help="Selected values")
selected_labels = Tuple(help="The labels of the selected options",
sync=True)
@property
def selected_label(self):
raise AttributeError(
"Does not support selected_label, use selected_labels")
def _value_in_options(self):
# ensure that the chosen value is one of the choices
if self.options:
old_value = self.value or []
new_value = []
for value in old_value:
if value in self._options_dict.values():
new_value.append(value)
if new_value:
self.value = new_value
else:
self.value = [next(iter(self._options_dict.values()))]
def _value_changed(self, name, old, new):
"""Called when value has been changed"""
if self.value_lock.acquire(False):
try:
self.selected_labels = [
self._options_labels[self._options_values.index(v)]
for v in new
]
except:
self.value = old
raise KeyError(new)
finally:
self.value_lock.release()
def _selected_labels_changed(self, name, old, new):
"""Called when the selected label has been changed (typically by the
frontend)."""
if self.value_lock.acquire(False):
try:
self.value = [self._options_dict[name] for name in new]
finally:
self.value_lock.release()
@register('IPython.ToggleButtons')
class ToggleButtons(_Selection):
"""Group of toggle buttons that represent an enumeration. Only one toggle
button can be toggled at any point in time."""
_view_name = Unicode('ToggleButtonsView', sync=True)
tooltips = List(Unicode(), sync=True)
icons = List(Unicode(), sync=True)
button_style = CaselessStrEnum(
values=['primary', 'success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the buttons.""")
@register('IPython.Dropdown')
class Dropdown(_Selection):
"""Allows you to select a single item from a dropdown."""
_view_name = Unicode('DropdownView', sync=True)
button_style = CaselessStrEnum(
values=['primary', 'success', 'info', 'warning', 'danger', ''],
default_value='', allow_none=True, sync=True, help="""Use a
predefined styling for the buttons.""")
@register('IPython.RadioButtons')
class RadioButtons(_Selection):
"""Group of radio buttons that represent an enumeration. Only one radio
button can be toggled at any point in time."""
_view_name = Unicode('RadioButtonsView', sync=True)
@register('IPython.Select')
class Select(_Selection):
"""Listbox that only allows one item to be selected at any given time."""
_view_name = Unicode('SelectView', sync=True)
@register('IPython.SelectMultiple')
class SelectMultiple(_MultipleSelection):
"""Listbox that allows many items to be selected at any given time.
Despite their names, inherited from ``_Selection``, the currently chosen
option values, ``value``, or their labels, ``selected_labels`` must both be
updated with a list-like object."""
_view_name = Unicode('SelectMultipleView', sync=True)
# Remove in IPython 4.0
ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
SelectWidget = DeprecatedClass(Select, 'SelectWidget')

@ -1,58 +0,0 @@
"""SelectionContainer class.
Represents a multipage container that can be used to group other widgets into
pages.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget_box import Box, register
from traitlets import Unicode, Dict, CInt
from .deprecated import DeprecatedClass
class _SelectionContainer(Box):
"""Base class used to display multiple child widgets."""
_titles = Dict(help="Titles of the pages", sync=True)
selected_index = CInt(0, sync=True)
# Public methods
def set_title(self, index, title):
"""Sets the title of a container page.
Parameters
----------
index : int
Index of the container page
title : unicode
New title"""
self._titles[index] = title
self.send_state('_titles')
def get_title(self, index):
"""Gets the title of a container pages.
Parameters
----------
index : int
Index of the container page"""
if index in self._titles:
return self._titles[index]
else:
return None
@register('IPython.Accordion')
class Accordion(_SelectionContainer):
"""Displays children each on a separate accordion page."""
_view_name = Unicode('AccordionView', sync=True)
@register('IPython.Tab')
class Tab(_SelectionContainer):
"""Displays children each on a separate accordion tab."""
_view_name = Unicode('TabView', sync=True)
# Remove in IPython 4.0
AccordionWidget = DeprecatedClass(Accordion, 'AccordionWidget')
TabWidget = DeprecatedClass(Tab, 'TabWidget')

@ -1,86 +0,0 @@
"""String class.
Represents a unicode string using a widget.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from .widget import DOMWidget, CallbackDispatcher, register
from traitlets import Unicode, Bool
from .deprecated import DeprecatedClass
class _String(DOMWidget):
"""Base class used to create widgets that represent a string."""
value = Unicode(help="String value", sync=True)
disabled = Bool(False, help="Enable or disable user changes", sync=True)
description = Unicode(help="Description of the value this widget represents", sync=True)
placeholder = Unicode("", help="Placeholder text to display when nothing has been typed", sync=True)
def __init__(self, value=None, **kwargs):
if value is not None:
kwargs['value'] = value
super(_String, self).__init__(**kwargs)
@register('IPython.HTML')
class HTML(_String):
"""Renders the string `value` as HTML."""
_view_name = Unicode('HTMLView', sync=True)
@register('IPython.Latex')
class Latex(_String):
"""Renders math inside the string `value` as Latex (requires $ $ or $$ $$
and similar latex tags)."""
_view_name = Unicode('LatexView', sync=True)
@register('IPython.Textarea')
class Textarea(_String):
"""Multiline text area widget."""
_view_name = Unicode('TextareaView', sync=True)
def scroll_to_bottom(self):
self.send({"method": "scroll_to_bottom"})
@register('IPython.Text')
class Text(_String):
"""Single line textbox widget."""
_view_name = Unicode('TextView', sync=True)
def __init__(self, *args, **kwargs):
super(Text, self).__init__(*args, **kwargs)
self._submission_callbacks = CallbackDispatcher()
self.on_msg(self._handle_string_msg)
def _handle_string_msg(self, _, content, buffers):
"""Handle a msg from the front-end.
Parameters
----------
content: dict
Content of the msg."""
if content.get('event', '') == 'submit':
self._submission_callbacks(self)
def on_submit(self, callback, remove=False):
"""(Un)Register a callback to handle text submission.
Triggered when the user clicks enter.
Parameters
----------
callback: callable
Will be called with exactly one argument: the Widget instance
remove: bool (optional)
Whether to unregister the callback"""
self._submission_callbacks.register_callback(callback, remove=remove)
# Remove in IPython 4.0
HTMLWidget = DeprecatedClass(HTML, 'HTMLWidget')
LatexWidget = DeprecatedClass(Latex, 'LatexWidget')
TextareaWidget = DeprecatedClass(Textarea, 'TextareaWidget')
TextWidget = DeprecatedClass(Text, 'TextWidget')
Loading…
Cancel
Save