DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • What Is Ant, Really?
  • Automate Sign-In To Any Website Using Selenium Web-Driver
  • Stop Poisoning Your Models: How I Built a CV Dataset Quality Toolkit I Can Reuse Forever
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes

Trending

  • 11 Agentic Testing Tools to Know in 2026
  • Scaling Cloud Data Automation: A Practical Guide to Open Table Formats
  • Zone-Free Angular: Unlocking High-Performance Change Detection With Signals and Modern Reactivity
  • Improving DAG Failure Detection in Airflow Using AI Techniques
  1. DZone
  2. Coding
  3. JavaScript
  4. Creating Custom Widgets Displayed in the Jupyter Notebook

Creating Custom Widgets Displayed in the Jupyter Notebook

Widgets can be used to build interactive GUIs for your notebooks, synchronize information between Python and JavaScript, and display HTML elements in the Jupyter notebook.

By 
Jirapongse Phuriphanvichai user avatar
Jirapongse Phuriphanvichai
·
Feb. 04, 21 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
5.5K Views

Join the DZone community and get the full member experience.

Join For Free

Widgets are eventful python objects that have a representation in the browser such as sliders, Google charts, and Plotly charts. Widgets can be used to build interactive GUIs for your notebooks, synchronize information between Python and JavaScript, and display HTML elements in the Jupyter notebook. Jupyter notebook allows us to develop custom widgets to be presented in the Jupyter notebook.

In addition to Python and web technologies (HTML, CSS, and JavaScript) knowledge, there are three main things that we need to know before developing custom widgets.

1. Built-in Magic Commands

Magic commands are supported by the IPython kernel. They are prefixed by the % or %% characters. These magic commands are designed to solve common problems in data analysis and control the behavior of IPython itself. The magic command used to create custom widgets are:

  • %%html is used to render and define HTML templates and cascading style sheets for widgets
  • %%javascript is used to run the cell block of JavaScript code for widgets. Typically, it is used to create JavaScript modules and views for the widget front-end components

2. Traitlets

Traitlets is a framework that allows attributes in Python classes to support type checking, default values, and change events (observer pattern). Widgets use Traitlets to notify changes of the Python classes’ properties to JavaScript. Then JavaScript will update the HTML elements according to the changes.

3. Backbone.js

Backbone.js is a lightweight JavaScript library designed for developing single-page web applications. It is based on the MVC (model–view–controller) framework. The IPython widget framework relies heavily on Backbone.js for the front-end components. 

In the next section, I will use this knowledge including CSS bootstrap and jQuery to create a Quote widget displaying real-time financial data in the Jupyter notebook.

Quote Widget

Quote Widget is a widget that displays real-time financial data in the Jupyter notebook. 

BDMS.BK Quote

There are three steps to implement this quote widget. All code is written in the Jupyter notebook (QuoteWidget.ipynb). 

1. Python Widget Class

The first step is defining a Python class inheriting from the DOMWidget base class. The DOMWidget class defined in the ipywidgets package represents a widget that is displayed on the page as an HTML DOM element. 

Python
 




xxxxxxxxxx
1
11


 
1
import ipywidgets as widgets
2
from traitlets import Unicode, Dict
3
 
          
4
class QuoteWidget(widgets.DOMWidget):
5
    _view_name = Unicode('QuoteView').tag(sync=True)
6
    _view_module = Unicode('Quote').tag(sync=True)
7
    _view_module_version = Unicode('0.1.0').tag(sync=True)
8
    payload = Dict().tag(sync=True)
9
    status = Unicode("").tag(sync=True)
10
    name = Unicode("Quote1").tag(sync=True)
11
 
          


The QuoteWidget class contains several traitlets properties. The _view_module, _view_module_version, and _view_name properties define the Backbone.js view class for the model. The payload, status, and name are the properties of the model. The name property contains the unique name of the quote widget. It represents a unique ID in the HTML DOM element. The status property contains a text representing the status of the quote widget. The payload property contains real-time financial data in the field list format.

JSON
 




xxxxxxxxxx
1


 
1
{'BID': 21.2, 'BIDSIZE': 445800, 'QUOTIM': '08:20:06', 'SEQNUM': 3693049}



 The data in the quote widget will be updated when the value of the payload property has been changed. The sync=True keyword argument tells the widget framework to synchronize the value to the Backbone.js front end. 

2. HTML Template

The next step is using the %%html built-in magic command to define an HTML template and styles of the quote widget. The template is defined inside the script tag with the text/template type. The ID of the template is quote-template. This template will be loaded by the Backbone.js front end. It uses the Bootstrap CSS framework to create a layout of the widget. 

HTML
 




xxxxxxxxxx
1
33


 
1
%%html
2
<style>
3
    .update {
4
        color: yellow;
5
    }
6
    .arrow-up {
7
        vertical-align: top;
8
    }
9
    .arrow-down {
10
        vertical-align: middle;
11
    }
12
    .quote_label {
13
        color: DeepSkyBlue;
14
    }
15
    …
16
</style>
17
<script type="text/template" id="quote-template">
18
    <div class="container quote_box">
19
        <div class="row quote_title">
20
            <div>
21
                <span data-field="X_RIC_NAME"></span> Quote
22
            </div>
23
        </div>
24
        <div class="row quote_header">
25
            <div class="col-xs-2" data-field="X_RIC_NAME">&nbsp;</div>
26
            <div class="col-xs-3" data-field="DSPLY_NAME">&nbsp;</div>
27
            <div class="col-xs-1" data-field="RDN_EXCHD2">&nbsp;</div>
28
            <div class="col-xs-1" data-field="CURRENCY">&nbsp;</div>
29
            <div class="col-xs-2 text-center">GMT</div>
30
            <div class="col-xs-2" data-field="TRADE_DATE">&nbsp;</div>
31
        </div>
32
…
33
 
          


The data-field attribute is used to define where the value of the field will be displayed and the data-animated attribute indicates the style of the element will be changed when the value has been updated.

HTML
 




xxxxxxxxxx
1


 
1
            <div class="col-xs-1 text-center" data-field="BIDSIZE" data-animated></div>
2
            <div class="col-xs-1 text-center" data-field="BID" data-animated></div>
3
            <div class="col-xs-1 text-center" data-field="ASK" data-animated></div>
4
            <div class="col-xs-1 text-center" data-field="ASKSIZE" data-animated></div>
5
            <div class="col-xs-1 text-center" data-field="QUOTIM" data-animated></div>
6
 
          


3. Backbone.js Front End

The last step is using the %%javascript built-in magic command to define a Backbone.js front end. This is the main component used to control and display the widget. In the IPython widget framework, Backbone.js is used to create a front end’s module and view linked to the Python class defined in the first step. The values of _view_name (QuoteView) and _view_module (Quote) properties in the first step are used to define the front end module and view. 

First, it uses require.js to define the Quote module which depends on the @jupyter-widgets/base module. Then, it uses the extend method to create the QuoteView class by inheriting from the DOMWidgetView class.

JavaScript
 




xxxxxxxxxx
1
14


 
1
%%javascript
2
require.undef('Quote');
3
 
          
4
define('Quote', ["@jupyter-widgets/base"], function(widgets) {
5
 
          
6
    var QuoteView = widgets.DOMWidgetView.extend({
7
 
          
8
    });
9
 
          
10
    return {
11
        QuoteView: QuoteView
12
    }
13
});
14
 
          



Next, it uses underscore.js to load the HTML template created in the second step, and then override the base render method of the view to add the template into the HTML DOM document. It uses the name property defined in the first step as a unique ID of the DOM element.

JavaScript
 




xxxxxxxxxx
1
10


 
1
            var QuoteView = widgets.DOMWidgetView.extend({
2
                template: _.template($("#quote-template").html()),
3
                render: function(){
4
                    var name = this.model.get('name');
5
                    this.model.on('change:payload',this.payload_changed, this);
6
                    this.model.on('change:status',this.status_changed, this);
7
                    this.div = $('<div/>',{id: name}).append(this.template);
8
                    $(this.el).append(this.div);
9
                },
10
 
          



In the render function, the model.on method is called to register functions (payload_changed, and status_changed) to update the view’s values when the payload and status properties change.

The status_changed function is called when a value of the status property changes. 

JavaScript
 




xxxxxxxxxx
1


 
1
                status_changed: function(){                    
2
                    var status_text = this.model.get('status');
3
                    var name = this.model.get('name');
4
                    var statusField = $("#"+name+" [data-field=status_text]");                    
5
                    statusField.text(status_text);                    
6
                },
7
 
          



This function uses jQuery to select an HTML element in the widget that has the “status_text” in the data-field attribute, and then update the text attribute of the selected HTML element to the value of the status property.

HTML
 




xxxxxxxxxx
1


 
1
<div class="col-xs-11" data-field="status_text"></div>



The payload_changed function is called when a value of the payload property changes. 

JavaScript
 




xxxxxxxxxx
1
23


 
1
                payload_changed: function(){                   
2
                    var payload = this.model.get('payload');                   
3
                    var name = this.model.get('name');
4
                    Object.getOwnPropertyNames(payload).forEach(
5
                        (value, index, array)=>
6
                        {                            
7
                            var updatedField = $("#"+name+" [data-field="+value+"]");                            
8
                            check_ripple(name, updatedField);                            
9
                            if(updatedField.data('value')!=undefined){
10
                                var selvalue = updatedField.data('value');                                
11
                                updatedField.removeClass().addClass(selvalue[payload[value]]);                                
12
                            }else{
13
                                updatedField.text(payload[value]);
14
                            }                            
15
                            if(updatedField.data('animated')!=undefined){                            
16
                                updatedField.addClass('update');
17
                                setTimeout(function() { 
18
                                    updatedField.removeClass('update');
19
                                }, 1000);
20
                            }                            
21
                        });            
22
                }
23
 
          



This function iterates all fields in the payload. For each field, it does the following tasks:

  • Use jQuery to select an HTML element in the widget that has the updated field’s name in the data-field attribute and then update the text attribute of the selected HTML element to the field’s value:
HTML
 




xxxxxxxxxx
1


 
1
<div class="col-xs-3" data-field="DSPLY_NAME"> </div>



  • Handle the ripple fields via the data-ripple attribute where the previous value of the updated field is moved to another field when the field has been updated:
HTML
 




xxxxxxxxxx
1


 
1
<span data-field="TRDPRC_1" class="quote_trade_price1" data-ripple="TRDPRC_2" data-animated></span>



  • Change the style of the updated HTML element for 1 second if it contains the data-animated attribute. It is used to notify users that the field’s value has been updated:
HTML
 




xxxxxxxxxx
1


 
1
<div class="col-xs-1 text-center" data-field="BID" data-animated></div>



Quote Widget Usage

The Quote Widget is implemented in the QuoteWidget.ipynb file. To use the widget, please follow these steps:

1. Use the %run built-in magic command to run the QuoteWidget.ipynb file:

Python
 




xxxxxxxxxx
1


 
1
%run ./QuoteWidget.ipynb



2. Create a QuoteWidget and set a unique name:

Python
 




xxxxxxxxxx
1


 
1
quote = QuoteWidget()
2
quote.name = "Quote1"
3
quote



Created QuoteWidget

3. Set the payload and status properties to update the widget:

Python
 




xxxxxxxxxx
1


 
1
quote.payload = {'DSPLY_NAME': 'BANGKOK DUSIT ME', 'RDN_EXCHD2': 'SET', 'CURRENCY': 'THB', 'NETCHNG_1': -0.1, 'PCTCHNG': -0.47, 'TRDVOL_1': 1000, 'TRADE_DATE': '2021-01-18', 'TRDTIM_1': '08:19:41', 'TRDPRC_1': 21.3, 'TRDPRC_2': 21.3, 'TRDPRC_3': 21.2, 'TRDPRC_4': 21.3, 'TRDPRC_5': 21.3, 'PRCTCK_1': 1, 'BID': 21.2, 'BIDSIZE': 585900, 'ASK': 21.3, 'ASKSIZE': 936900, 'OPEN_PRC': 21.4, 'ACVOL_1': 17884500, 'VWAP': 21.2084, 'VWAP_VOL': 17884500, 'HIGH_1': 21.5, '52WK_HIGH': 26.5, 'TURNOVER': 379301.91, 'LOW_1': 21.1, '52WK_LOW': 15.6, 'PERATIO': 47.58, 'HST_CLOSE': 21.4, 'ADJUST_CLS': 21.4, 'EARNINGS': 0.4498, 'QUOTIM': '08:19:55', 'SEQNUM': 3690282, 'X_RIC_NAME': 'BDMS.BK'}
2
 
          
3
quote.status = "Open/Ok"
4
 
          



Updated Widget

Moreover, the Quote Widget can be used with any APIs that support real-time financial data, such as Refinitiv Eikon Data API, Refinitiv WebSocket API, and Refinitv Data Platform. The sample code (EDAPIQuoteWidget.ipynb) for Eikon Data API is available on GitHub.  To use EDAPIQuoteWidget, please follow these steps:

1. Use the %run built-in magic command to run the EDAPIQuoteWidget.ipynb file:

Python
 




xxxxxxxxxx
1


 
1
%run ./EDAPIQuoteWidget.ipynb



2. Import the Eikon Data API package and set the application key:

Python
 




xxxxxxxxxx
1


 
1
import eikon as ek
2
ek.set_app_key('<application key>')



3. Load data dictionary files used to expand enumerated strings for the enumeration fields:

Python
 




xxxxxxxxxx
1


 
1
dict = RDMFieldDictionary("RDMFieldDictionary", "enumtype.def")



4. Create an EDAPIQuoteWidget with the Eikon Data API, widget name, and data dictionary, and then call the widget method to display the widget:

Python
 




xxxxxxxxxx
1


 
1
q = EDAPIQuoteWidget(ek, "quote1", dict)
2
q.widget()



Called widget method

5. Call the open method with the instrument name to subscribe to the Real-Time service. The retrieved real-time data will be displayed on the widget:

Python
 




xxxxxxxxxx
1


 
1
q.open("AV.L")



Retrieved real-time data

Summary

This article demonstrates how to develop custom widgets displayed on the Jupyter Notebook. The widget framework uses Traitlets to notify changes of the Python classes’ attributes to JavaScript and uses Backbone.js for the front end javascript parts. Then, it shows steps to create a quote widget for displaying financial real-time on the Jupyter Notebook. Finally, it presents how to use the quote widget with Refinitiv Eikon Data API. All widget and example files are available on GitHub. This article is also available on the Refinitiv Developer Community.

References

1.    Backbonejs.org. n.d. Backbone.Js. [online] Available at: <https://backbonejs.org/> [Accessed 20 January 2021].

2.    Ipywidgets.readthedocs.io. 2017. Building A Custom Widget - Email Widget — Jupyter Widgets 7.6.2 Documentation. [online] Available at: <https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html> [Accessed 20 January 2021].

3.    Ipython.readthedocs.io. n.d. Built-In Magic Commands — Ipython 7.19.0 Documentation. [online] Available at: <https://ipython.readthedocs.io/en/stable/interactive/magics.html> [Accessed 20 January 2021].

4.    Developers.refinitiv.com. n.d. Eikon Data API | Refinitiv Developers. [online] Available at: <https://developers.refinitiv.com/en/api-catalog/eikon/eikon-data-api> [Accessed 20 January 2021].

5.    Jquery.com. n.d. Jquery. [online] Available at: <https://jquery.com/> [Accessed 20 January 2021].

6.    Mark Otto, a., n.d. Bootstrap. [online] Getbootstrap.com. Available at: <https://getbootstrap.com/> [Accessed 20 January 2021].

7.    Developers.refinitiv.com. n.d. Refinitiv Data Platform Libraries | Refinitiv Developers. [online] Available at: <https://developers.refinitiv.com/en/api-catalog/refinitiv-data-platform/refinitiv-data-platform-libraries> [Accessed 20 January 2021].

8.    Developers.refinitiv.com. n.d. Refinitiv Websocket API | Refinitiv Developers. [online] Available at: <https://developers.refinitiv.com/en/api-catalog/elektron/refinitiv-websocket-api> [Accessed 20 January 2021].

9.    Rossant, C., 2018. Ipython Interactive Computing And Visualization Cookbook, Second Edition. 2nd ed. Birmingham: Packt Publishing.

10.    Traitlets.readthedocs.io. 2015. Traitlets — Traitlets 5.0.5 Documentation. [online] Available at: <https://traitlets.readthedocs.io/en/stable/> [Accessed 20 January 2021].


jupyter notebook HTML Data dictionary Python (language) Property (programming) JavaScript library Element Attribute (computing) Backbone.js Command (computing)

Published at DZone with permission of Jirapongse Phuriphanvichai. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • What Is Ant, Really?
  • Automate Sign-In To Any Website Using Selenium Web-Driver
  • Stop Poisoning Your Models: How I Built a CV Dataset Quality Toolkit I Can Reuse Forever
  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook