Using Templating and Shared Options in Highcharts for Python

Using Templating and Shared Options in Highcharts for Python logo

When building analytical applications – or robust dashboards – your visualization needs are likely to become more complicated. In practice, you will want all of your visualizations to a) be visually consistent, and b) to be implemented with the least amount of code/effort. Which is why it is a good thing that Highcharts Core and Highcharts for Python have such strong support for templating and the sharing of configuration across individual charts.

Templating with Highcharts for Python

The Highcharts for Python (and underlying Highcharts Core) APIs are very robust, which means that there are lots of small “fiddly” options which can be customized, configured, and tweaked to support the look and behavior of data that you want. Tweaking your configuration to get what you need can be a lot of effort, and it can produce configuration sets that are quite extensive. And if elements of it need to be re-used, repeating those long sets of configurations can rapidly clutter up your code and make it unreadable.

Templates to the rescue!

A best practice when working with Highcharts for Python is to store your core (or repeatable) chart configurations in .js files, and then to instantiate your Chart options from those template files. This pattern can actually work for any Highcharts for Python object, whether that’s your chart, your chart’s .options, or even a particular series type.

To see how this works in practice, let’s imagine you have your project files organized as follows:

my_repository/
| — docs/
| — my_project/
| —— project_resources/
| ——— image_files/
| ——— data_files/
| ————— data-file-01.csv
| ————— data-file-02.csv
| ————— data-file-03.csv
| ——— highcharts_config/
| ————— shared_options.js
| ————— bar-template.js
| ————— line-template.js
| ————— packed-bubble-template.js
| —— some_package/
| ———— __init__.py
| ———— package_module.py
| ———— another_module.py
| —— __init__.py
| —— __version__.py
| —— some_module.py
| — tests/
| — .gitignore
| — requirements.txt

In the example above, you can see the highcharts_config/ folder which contains a series of .js (JavaScript) files. As some of the file names suggest, this folder contains templates for charts that will have a bar series (bar-template.js), a line series (line-template.js), and a packed-bubble series (packed-bubble-template.js).

Note
While this example assumes one template per series type, you can actually have a template that applies to all series types, or you can have templates that combine series types, or really any combination of templates.

Let’s take a look at the bar-template.js file. You’ll find that it contains a pretty basic JavaScript object literal definition of a set of Highcharts .options:

{
  xAxis: {
    categories: [
      'Jan',
      'Feb',
      'Mar',
      'Apr',
      'May',
      'Jun',
      'Jul',
      'Aug',
      'Sep',
      'Oct',
      'Nov',
      'Dec'
    ]
  },
  legend: {
    align: 'right',
    backgroundColor: '#FFF',
    floating: true,
    padding: 14,
    shadow: true,
    title: {
      text: "Chart Legend Goes Here"
    },
    verticalAlign: 'top',
    x: -8,
    y: 36
  },
  plotOptions: {
    bar: {
      borderRadius: 8,
      negativeColor: '#FF0000'
    },
    series: {
      allowPointSelect: true
    }
  },
  title: {
    text: "My Chart Title"
  }
}

As you can see, this set of options is not doing anything fancy. It is just setting some x-axis category labeling, configuring the position and title of the legend, and setting some basic styling properties for a bar series. But if you want all of your bar charts to adhere to this template, you can now apply this template in a couple of different ways:

from highcharts_core.chart import Chart
from highcharts_core.options import HighchartsOptions
from highcharts_core.options.series import BarSeries
# Create a HighchartsOptions instance configured from your template.
bar_template_options = HighchartsOptions.from_js_literal(
  '../project_resources/highcharts_config/bar-template.js'
)
# Create a new Chart instance from your bar_template_options
my_chart = Chart.from_options(bar_template_options,
  chart_kwargs = {
    'container': 'target_div',
    'variable_name': 'myChart'
  })
# Create a second chart instance by copying the first one’ s options.
my_other_chart = my_chart.copy()
# Add your data series(from the“ my - csv - file.csv” CSV file) to the chart.
my_chart.add_series(BarSeries.from_csv('my-csv-file.csv',
  property_column_map = {
    'x': 0,
    'y': 3,
    'id': 'id'
  }))
# Add another data series(from a different CSV file) to the chart.
my_other_chart.add_series(BarSeries.from_csv('my-other-csv-file.csv',
  property_column_map = {
    'x': 0,
    'y': 3,
    'id': 'id'
  }))

So what’s happening in the code above? First, we import the relevant classes that we need. Easy enough. Next, we instantiate a set of HighchartsOptions from our bar-template.js file. Next, using that set of options we create a chart instance using the Chart.from_options() class method.

If we wanted to, we could do this again and again for however many charts needed to apply our bar chart template. However, we then use another method of applying a template: the .copy() method. This method takes the properties from the first chart instance we created and copies it to a new variable named my_other_chart. No need to reload the template file or anything else.

Then, with our two chart objects created we simply add a BarSeries populated with data from two (different) CSV files. And that’s it! Two richly-configured charts with just four lines of code.

Tip
Templating is particularly powerful when used in combination with Shared Options (see below). The best practice is to use Shared Options for all of the universal/global settings that apply to all of your charts, and then to use templating for those settings that apply to subsets of your charts. For example, you may have one set of global options configured using Shared Options, and then you may have two different types of Bar Chart templates that you use for different classes of data.

Using Shared Options with Highcharts for Python

Shared Options in Highcharts for Python are particularly useful when you want to apply global settings to all of the Highcharts visualizations rendered at the same time. While you could do this using templating as described above, you’ll ultimately produce less code if you use shared options. Then you can make any subset-specific adjustments using templates, and finally you can override the global Shared Options on any of your chart instances if you need to.

So how do Shared Options work in Highcharts for Python? Very similarly to templates. If you recall the file structure outlined above, let’s now look at the file project_resources/highcharts_config/shared_options.js which…you guessed it! Contains the shared option configuration. This might look something like this:

{
  chart: {
    backgroundColor: {
      linearGradient: {
        x1: 0,
        x2: 0,
        y1: 1,
        y2: 1
      },
      stops: [
        [0, 'rgb(255, 255, 255)'],
        [1, 'rgb(240, 240, 255)']
      ]
    },
    borderWidth: 2,
    plotBackgroundColor: 'rgba(255, 255, 255, .9)',
    plotBorderWidth: 1
  },
  caption: {
    align: 'center',
    floating: true,
    margin: 20,
    verticalAlign: 'top'
  },
  credits: {
    enabled: true,
    href: 'https://www.somewhere.com',
    style: {
      color: '#cccccc',
      fontSize: '8px'
    },
    text: 'Highcharts for Python'
  }
}

As you can see, these global options configure the chart formatting (applying a snazzy gradient background), the chart border, the chart caption styling, and the credits shown for the chart. With this file, we can now create a SharedOptions instance:

from highcharts_core.global_options.shared_options
import SharedOptions
# Create a new SharedOptions instance
shared_options = SharedOptions.from_js_literal(
  '../project_resources/highcharts_config/shared_options.js'
)

Now with that SharedOptions instance, we can simply convert it to JS code and deliver it to your front-end (the same as you do with your chart). The process is pretty straightforward:

from highcharts_core.global_options.shared_options import SharedOptions
# Create your client-side JavaScript code
js_code_snippet = shared_options.to_js_literal()

The JS code snippet will contain a string of JavaScript that is very similar to the original shared_options.js file, with one notable difference: the new JS literal file will execute the JavaScript Highcharts.setOptions() method, which configures the global settings that will apply to all charts rendered on the page. The result in this case would be:

Highcharts.setOptions({
  caption: {
    align: 'center',
    floating: true,
    margin: 20,
    verticalAlign: 'top'
  },
  chart: {
    backgroundColor: {
      linearGradient: {
        x1: 0,
        x2: 0,
        y1: 1,
        y2: 1
      },
      stops: [
        [0, 'rgb(255, 255, 255)'],
        [1, 'rgb(240, 240, 255)']
      ]
    },
    borderWidth: 2,
    plotBackgroundColor: 'rgba(255, 255, 255, .9)',
    plotBorderWidth: 1
  },
  credits: {
    enabled: true,
    href: 'https://www.somewhere.com',
    style: {
      color: '#cccccc',
      fontSize: '8px'
    },
    text: 'Highcharts for Python'
  }
});

Which when included in your web-based front-end, will apply those settings to all charts rendered in the same browser context.

Tip
You can also apply shared options when programmatically exporting charts using the .download_chart() method. Just pass your SharedOptions instance to the method in an argument named global_options.

More Resources

The above tutorial is just a really simple example of how you can create rich visualizations with just a handful of method calls using Highcharts for Python. But the library offers so much more! We recommend you take a look at the following additional resources which you may find useful: