Creating Charts

Your First Chart

To begin, we'll use the Highchart's documentation Your First Chart example.

In JustPy this example would look like this:

import justpy as jp

# Example from https://www.highcharts.com/docs/getting-started/your-first-chart
my_chart_def = """
{
        chart: {
            type: 'bar'
        },
        title: {
            text: 'Fruit Consumption'
        },
        xAxis: {
            categories: ['Apples', 'Bananas', 'Oranges']
        },
        yAxis: {
            title: {
                text: 'Fruit eaten'
            }
        },
        series: [{
            name: 'Jane',
            data: [1, 0, 4]
        }, {
            name: 'John',
            data: [5, 7, 3]
        }]
}
"""

def chart_test():
    wp = jp.WebPage()
    my_chart = jp.HighCharts(a=wp, classes='m-2 p-2 border', style='width: 600px')
    my_chart.options = my_chart_def
    return wp

jp.justpy(chart_test)

Run the program above as explained here.

We create a chart using the JustPy HighCharts class. Like other JustPy components, it recognizes keyword arguments like classes, style and a.

In this example the variable my_chart_def defines the chart's options (settings). It is a string which can be converted directly to a valid JavaScript object. This JavaScript object is not however a valid Python dictionary because the keys of the object (for example chart and title) do not have single quotes or double quotes around them and therefore are not valid Python keys.

When my_chart_def is assigned to my_chart.options the code in HighCharts (a Python class) checks if a string is being assigned, and if that is the case, it is converted to a Python dictionary that also allows dot notation.

If a standard Python dictionary is assigned to the options attribute, it is also converted to a dictionary that allows dot notation access. Don't worry if this is not completely clear at this stage. There are plenty of examples coming that will make things clearer.

Try for example, changing the text of the chart title in my_chart_def and running the program again.

Chart Types

Let's do something a little more complex. Change chart_test in the above example to the following:

def chart_test():
    wp = jp.WebPage()
    for chart_type in ['bar', 'column', 'line', 'spline']:
        my_chart = jp.HighCharts(a=wp, classes='m-2 p-2 border w-1/2', options=my_chart_def)
        my_chart.options.chart.type = chart_type
        my_chart.options.title.text = f'Chart of Type {chart_type.capitalize()}'
        my_chart.options.subtitle.text = f'Subtitle {chart_type.capitalize()}'
    return wp

Four charts are created and put on the page, each one of a different type. We accomplish this by iterating over a list with four different chart types that Highcharts supports. When we create each chart, we load the same options (via a keyword argument) to all of them.

Info

The reference for the Highcharts chart options can be found at https://api.highcharts.com/highcharts/

However, we then proceed to change the options. Using the dot notation, we assign different values to options.chart.type, options.title.text and options.subtitle.text.

Note

Notice that options.subtitle was not specified in my_chart_def. The addict library Dict structure creates sub dictionaries automatically when required.

Using Highcharts Online Examples

There are many examples of Highcharts charts online and particularly in the Highcharts documentation. The structure of the options attribute of HighCharts (the Python class defined by JustPy) matches exactly the structure of the JavaScript object that defines the settings of a Highchart JavaScript chart. This is very convenient and allows you to use the numerous JavaScript examples of Highcharts charts as a starting point for your own charts.

The Highcharts library is a JavaScript library and the the chart examples are all in JavaScript. That is why JustPy's HighCharts supports converting strings that represent simple Javascript objects to Python dictionaries. Let's look at a concrete example from the Highcharts documentation.

Please go to https://api.highcharts.com/highcharts/chart.type and follow the link under "Try it" labeled "Bar". In the Highcharts documentation and in other discussions of Highcharts it is common to link to a JavaScript jsfiddle or codepen working example. In this case, a jsfiddle window opens. Look at the JavaScript window in the lower left of the page. The second argument to Highcharts.chart is the JavaScript object that defines the chart. Let's copy this object and assign it to a Python string and try replicating this chart.

If you simply copy the object like I did below, you will get an error. Run the program and see for yourself there is an issue.

import justpy as jp

my_chart_def = """
{
    chart: {
        type: 'bar'
    },
    xAxis: {
        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    },
    legend: {
        layout: 'vertical',
        floating: true,
        backgroundColor: '#FFFFFF',
        align: 'right',
        verticalAlign: 'top',
        y: 60,
        x: -60
    },
    tooltip: {
        formatter: function () {
            return '<b>' + this.series.name + '</b><br/>' +
                this.x + ': ' + this.y;
        }
    },
    series: [{
        data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
    }]
}
"""

def chart_test():
    wp = jp.WebPage()
    my_chart = jp.HighCharts(a=wp, classes='m-2 p-2 border w-1/2', options=my_chart_def)
    return wp

jp.justpy(chart_test)

The problem is that the value of the tooltip.formatter key is a JavaScript function definition. JustPy does not support this. We will soon see how to define tooltip formatters in JustPy, but for now, please remove the whole tooltip section and run the program. Your program should look like this:

import justpy as jp

my_chart_def = """
{
    chart: {
        type: 'bar'
    },
    xAxis: {
        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    },
    legend: {
        layout: 'vertical',
        floating: true,
        backgroundColor: '#FFFFFF',
        align: 'right',
        verticalAlign: 'top',
        y: 60,
        x: -60
    },
    series: [{
        data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
    }]
}
"""

def chart_test():
    wp = jp.WebPage()
    my_chart = jp.HighCharts(a=wp, classes='m-2 p-2 border w-1/2', options=my_chart_def)
    my_chart.options.series[0].name = 'Tourists in \'000'
    my_chart.options.title.text = 'Tourists in Middle Earth'
    return wp

jp.justpy(chart_test)

This time, there is no error. When you copy a JavaScript object, make sure that it does not include function definitions or uses JavaScript language functions to compute values. These will cause errors.

Warning

JustPy can only parse JavaScript objects that look like a Python dictionary except for the missing quotes around the dictionary keys. Also, mixing single and double quotes in the JavaScript object may cause problems. Use only single quotes as is the norm with most Highcharts examples.

Tip

It is very likely someone has already created a chart like that one you need and put it online. Use that chart as a stepping stone to create yours. Eventually, you may need to make use of the excellent Highcharts docs

Chart Series

Notice that we have added two lines to chart_test above. The first of these lines gives the series a name and makes the legend more informative.

Note

The Dicts that describe the series of a chart are held in a list so the first series is options.series[0]

Here is an example of how you create a chart with multiple series.

Warning

numpy needs to be installed for this example to work

import justpy as jp
import numpy as np


def sine_test():
    wp = jp.WebPage()
    chart = jp.HighCharts(a=wp, classes='border m-2 p-2 w-3/4')
    o = chart.options
    o.title.text = 'Sines Galore'
    x = np.linspace(-np.pi, np.pi, 201)
    for frequency in range(1,11):
        y = np.sin(frequency * x)
        s = jp.Dict()
        s.name = f'F{frequency}'
        s.data = list(zip(x, y))
        o.series.append(s)
    return wp


jp.justpy(sine_test)

This example uses numpy to generate sines with different frequencies and adds them as separate series to a chart (numpy needs to be installed for this example to work).

This and other examples will use the Python standard library function zip. Using zip, it is very simple to create lists of x, y pairs which is one data format Highcharts supports for its series (it supports other formats also such as dictionaries).

Try moving the mouse over different areas of the chart to see its interactive features. For example, when you hover over a series name in the legend, it highlights the series. When you click the series name in the legend, it is disabled.

Sharing Charts

In many cases, having created a chart, you would like to share it with others. One way is to deploy JustPy and have people access the charts via a URL.

import justpy as jp

# https://github.com/elimintz/elimintz.github.io/tree/master/charts
# Try https://127.0.0.1:8000/ + any one of the entries in charts
# For example: http://127.0.0.1:8000/bubbles
charts = ['org', 'bubbles', 'item', 'timeline', 'states', 'browsers', 'wheel']

@jp.SetRoute('/{chart_name}')
async def chart_test(request):
    wp = jp.WebPage()
    chart_name = request.path_params.get('chart_name', 'item')   # Default chart is item
    if chart_name not in charts:
        chart_name = 'item'
    chart_options = await jp.get(f'https://elimintz.github.io/charts/{chart_name}', 'text')
    my_chart = jp.HighCharts(a=wp, classes='m-2 p-2 border w-3/4', options=chart_options)
    return wp

jp.justpy(chart_test)

The program above fetches chart definitions from a static page server (a github repository in this case) based on the URL. Take a look at the different charts for a small taste of what Highcharts can do (these are all taken from the Highcharts website where there are many more examples). You could deploy the program above and have others accessing the charts via the appropriate URLs. The chart definitions can be found here.

Another option is to send people the code snippet above to run by themselves. Of course, they would need to have the appropriate environment installed on their computer.

The advantage of reading the chart definition remotely is that you can change these remote files and additional files without needing to resend or restart your program.