The value
traitlet is synchronised with the frontend, and will be used to display in the notebook cell a string set by the backend. The other traitlets (_view_name
, _view_module
and _view_module_version
) are used by the Jupyter Widget framework to make your widget work, but you don't have to worry about this for the time being!
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)
value = Unicode('Hello World!').tag(sync=True)
The IPython widget framework front end relies heavily on Backbone.js. 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.
To access the model associated 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 an on
method, which allows you to listen to events triggered by the model (like value changes).
You first need to import the @jupyter-widgets/base
module. To import modules, use the define
method of require.js (as seen below).
%%javascript
define('hello', ["@jupyter-widgets/base"], function(widgets) {
});
Next, define your widget view class. Inherit from the DOMWidgetView
by using the .extend
method.
%%javascript
require.undef('hello');
define('hello', ["@jupyter-widgets/base"], function(widgets) {
// Define the HelloView
var HelloView = widgets.DOMWidgetView.extend({
});
return {
HelloView: HelloView
}
});
Lastly, override the base render
method of the view to define custom rendering logic. A handle to the widget's default DOM element can be acquired via this.el
. The el
property is the DOM element associated with the view. Thanks to the call to model.get
, the view will display the value of the back end upon display. However, it will not update itself to a new value when the value changes.
%%javascript
require.undef('hello');
define('hello', ["@jupyter-widgets/base"], function(widgets) {
var HelloView = widgets.DOMWidgetView.extend({
render: function() {
this.el.textContent = this.model.get('value');
},
});
return {
HelloView : HelloView
};
});
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).
%%javascript
require.undef('hello');
define('hello', ["@jupyter-widgets/base"], function(widgets) {
var HelloView = widgets.DOMWidgetView.extend({
render: function() {
this.value_changed();
this.model.on('change:value', this.value_changed, this);
},
value_changed: function() {
this.el.textContent = this.model.get('value');
},
});
return {
HelloView : HelloView
};
});
w = HelloWidget()
w
Failed to display Jupyter Widget of type HelloWidget
.
If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean that the widgets JavaScript is still loading. If this message persists, it likely means that the widgets JavaScript library is either not installed or not enabled. See the Jupyter Widgets Documentation for setup instructions.
If you're reading this message in another frontend (for example, a static rendering on GitHub or NBViewer), it may mean that your frontend doesn't currently support widgets.
w.value = 'my value'
The example above dumps 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.
Now the widget consists in a form element, and a title. Both display the value
string set in the backend. But now you can change the string from the form, and it'll update both the title and the backend value. The synchronisation from the JS frontend to the Python backend is done using an EventListener
which listens to changes in the form element, and triggers a callback. The callback changes the model value, and syncs with the backend.
%%javascript
require.undef('hello');
define('hello', ["@jupyter-widgets/base"], function(widgets) {
var HelloView = widgets.DOMWidgetView.extend({
callback:function(inputEvent, formElement){
this.model.set({'value':formElement[0].value}) // update the JS model with the current view value
this.touch() // sync the JS model with the Python backend
},
render: function() {
this.model.on('change:value', this.value_changed, this);
let view = this;
// standard HTML DOM change from JS
let f = document.createElement("form");
let i = document.createElement("input"); // input element, text
i.setAttribute('type',"text");
f.appendChild(i);
this.el.appendChild(f);
let title = document.createElement("h3");
this.el.appendChild(title);
// initializing the form and the title values
i.setAttribute('value', this.model.get('value'));
title.textContent = this.model.get('value');
// Listening to changes in the frontend input
f.addEventListener("input", (inputEvent => view.callback(inputEvent, f)), false);
// handle to access the DOM elements directly
this.input = i;
this.title = title;
},
value_changed: function() {
// access to the 'input' DOM element
this.input.setAttribute('value', this.model.get('value'))
// access to the 'h3' DOM element
this.title.textContent = this.model.get('value')
}
});
return {
HelloView : HelloView
};
});
w2 = HelloWidget()
w2
# you can change the value in the form below, and it will be automatically updated in the Python kernel
Failed to display Jupyter Widget of type HelloWidget
.
If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean that the widgets JavaScript is still loading. If this message persists, it likely means that the widgets JavaScript library is either not installed or not enabled. See the Jupyter Widgets Documentation for setup instructions.
If you're reading this message in another frontend (for example, a static rendering on GitHub or NBViewer), it may mean that your frontend doesn't currently support widgets.
w2.value
'Hello World!'
w2.value = 'Sync from Python to JS'