Custom widget#

The widget framework is based on the Comms framework, which enables the kernel to send and receive JSON to the front end. In order to create a custom widget, the widget must be defined both in the browser and in the Python kernel.

Python kernel#

DOMWidget#

To define a widget, it must inherit from the Widget or DOMWidget base class. If the widget is to be displayed in the Jupyter notebook, your widget should inherit from DOMWidget. The DOMWidget class itself inherits from the Widget class.

_view_name#

By adopting DOMWidget, the widget framework is not informed which front-end widget should be linked to the back-end widget.

Instead, you have to specify this yourself using one of the following attributes:

  • _view_name

  • _view_module

  • _view_module_version

and if applicable

  • _model_name

  • _model_module

[1]:
import ipywidgets as widgets

from traitlets import Unicode, validate


class HelloWidget(widgets.DOMWidget):
    _view_name = Unicode("HelloView").tag(sync=True)
    _view_module = Unicode("hello").tag(sync=True)
    _view_module_version = Unicode("0.1.0").tag(sync=True)

sync=True-Traitlets#

Traitlets is a framework with which Python classes can have attributes with type checking, dynamically calculated default values and callbacks when changed. The sync=True keyword argument tells the widget framework to synchronise the value with the browser; without it, the browser would not learn anything about _view_name or _view_module.

Frontend (JavaScript)#

Models and Views#

The front end of the IPython widget framework depends heavily on Backbone.js. Backbone.js is an Model View Controller (MVC) framework that automatically synchronises widgets defined in the backend with generic Backbone.js models in the frontend: the previously defined _view_name characteristic is used by the widget framework to display the corresponding Backbone.js-View and link it to the model.

Import @jupyter-widgets/base#

First you have to use the @jupyter-widgets/base module with the define method of RequireJS.

[2]:
%%javascript
define('hello', ["@jupyter-widgets/base"], function(widgets) {

});

Define view#

Next we define the widget view class and we inherit from DOMWidgetView with the .extend method.

[3]:
%%javascript
require.undef('hello');

define('hello', ["@jupyter-widgets/base"], function(widgets) {
    // Define the HelloView
    var HelloView = widgets.DOMWidgetView.extend({
    });
    return {
        HelloView: HelloView
    }
});

render method#

Finally, we still have to override the basic render method to define a custom rendering logic. A handle to the standard DOM element of the widget can be called with this.el. The el property is the DOM element associated with the view.

[4]:
%%javascript
require.undef('hello');

define('hello', ["@jupyter-widgets/base"], function(widgets) {
    var HelloView = widgets.DOMWidgetView.extend({
        // Render the view.
        render: function() {
            this.el.textContent = 'Hello World!';
        },
    });
    return {
        HelloView: HelloView
    };
});

Test#

The widget can now be displayed like any other widget with

[5]:
HelloWidget()

Stateful widget#

There’s not much you can do with the example above. To change this, you have to make the widget stateful. Instead of a static Hello World! Message, a string specified by the backend should be displayed. To do this, a new traitlet is first added. Use the name of value here to stay consistent with the rest of the widget framework and to allow your widget to be used with interaction.

Create Jupyter widgets from a template#

A Cookiecutter is available with widget-cookiecutter. It contains an implementation for a placeholder widget Hello World. It also makes it easier for you to pack and distribute your Jupyter widgets.