Life Codecs @ NamingCrisis.net

Ruminations. Reflections. Refractions. Code.

Mar 11, 2019 - Software dev matplotlib python

Matplotlib: Interactive and Non-Interactive Use

I’ve been experimenting with matplotlib recently, both interactively in an ipython shell as well as non-interactively as a chart image generator to be served through the web.

This post shares some tips that took some searching on how matplotlib operates in different interaction contexts.

General Concept: Backends

matplotlib has the concept of backends — essentially the target canvas or surface it can render a figure to.

A backend implementation takes matplotlib’s internal representations of high-level drawing objects such as lines and axes and converts these to a form suitable for that backend.

For example, an image backend — effectively what we use when we save a figure to a file or stream — will know about different image formats, and how to convert drawing objects into pixels in that format.

The key point here is that interactive and non-interactive modes require a backend implementation suitable specifically to each of them.

The following ipython sample session illustrates common backend API calls which we will need in later sections:

In [1]: import matplotlib as mpl                                                 

In [2]: mpl.get_backend()                                                        

Out[2]: 'Qt5Agg'

In [3]:  


In [3]: %matplotlib --list                                                       

Available matplotlib backends: ['tk', 'gtk', 'gtk3', 'wx', 'qt4', 'qt5', 'qt', 
'osx', 'nbagg', 'notebook', 'agg', 'svg', 'pdf', 'ps', 'inline', 'ipympl', 
'widget']


In [7]: mpl.use('pdf')


In [8]: mpl.get_backend()

Out[8]: 'pdf'


Note that simply doing mpl.use('qt5') fails. The suffix agg needs to be added in some cases — but obviously not all, as per the pdf example above (go figure) — like so:

mpl.use('qt5agg')

It is not case-sensitive, so Qt5Agg and qt5agg work just the same. The exception that shows actually lists the proper names:

ValueError: Unrecognized backend string 'gtk': valid strings are ['GTK3Agg', 
'GTK3Cairo', 'MacOSX', 'nbAgg', 'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo', 
'TkAgg', 'TkCairo', 'WebAgg', 'WX', 'WXAgg', 'WXCairo', 'agg', 'cairo', 
'pdf', 'pgf', 'ps', 'svg', 'template']  


Furthermore, even though ipython lists those various backends, it may still fail on calling use(...), and require you to install additional components.

Basically, ipython is aware of a bunch of possible backends, and attempts to dynamically load the one requested by matplotlib.use(...).

Switching backends can happen prior to any plotting command, e.g. matplotlib.pyplot.plot(..).

In [61]: mpl.use('agg')                                                          

In [62]: plt.bar(x, y)                                                           

Out[62]: <BarContainer object of 25 artists>

In [63]: plt.show()                                                              

/home/xxx/apps/anaconda3/lib/python3.6/site-packages/matplotlib/figure.py:445
: UserWarning: Matplotlib is currently using agg, which is a non-GUI 
backend, so cannot show the figure.
  % get_backend())

In [64]: mpl.use('qt5agg')                                                       

In [66]: plt.bar(x, y)                                                           

Out[66]: <BarContainer object of 25 artists>

In [67]: plt.show()                                                              


Interactive Mode

In an interactive shell such as ipython, we want an interactive backend such as Qt5Agg or TkAgg.

TkAgg in particular, was what worked for me on Mac with an Anaconda distribution without installing anything further.

These are image renderer and viewer implementations with controls for interactive use.

When exploring in interactive mode, I was confused why matplotlib.pyplot.show() was only taking effect once: calling show() then closing the chart window, and calling show() again does nothing.

Essentially, one needs to reissue a plotting command such as pyplot.bar(...) or pyplot.plot(...) to force a draw, and then call show() again.

Of course, simply starting afresh with a new figure also works, since a new figure is a fresh draw.

plt.figure()
plt.show()

plt.figure()
plt.show()

Non-Interactive Mode

A common use case for non-interactive mode is to produce and serve graphics over the web. The server code will run completely headless and not require any GUI toolkits to be installed where it is deployed.

We will often get user inputs, plot the figure in-memory, then stream it through, encoded as an image to be rendered on the browser, or saved to disk, etc.

The correct backend to use for this is agg.

Essentially, as part of bootstrapping our application, webapp or otherwise, we need to run the following prior to any plotting API calls being made:

import matplotlib
matplotlib.use('Agg') # case-insensitive


In most frameworks, such as Flask, this can be done in the web module’s __init__.py where we create the application object.

Of course, this configuration appropriately stops show() from working, issuing a UserWarning:

In [61]: mpl.use('agg')                                                      
                                        

In [62]: plt.bar(x, y)                                                       
                                        
Out[62]: <BarContainer object of 25 artists>

In [63]: plt.show()                                                          
                                        
/home/xxx/apps/anaconda3/lib/python3.6/site-packages/matplotlib/figure.py:445
: UserWarning: Matplotlib is currently using agg, which is a non-GUI 
backend, so cannot show the figure.
  % get_backend())


Happy Hacking!