mpl-gui Documentation#

Motivation#

This project is a prototype space for overhauling the GUI event loop management tools that Matplotlib provides in pyplot.

The pyplot module current serves two critical, but unrelated functions:

  1. provide a state-full implicit API that rhymes / was inspired by MATLAB

  2. provide the management of interaction between Matplotlib and the GUI event loop, including keeping Figures alive

While it can be very convenient when working at the prompt, the state-full API can lead to brittle code that depends on the global state in confusing ways, particularly when used in library code. On the other hand, matplotlib.pyplot does a very good job of hiding from the user the fact that they are developing a GUI application and handling, along with IPython, many of the details involved in running a GUI application in parallel with Python.

Examples#

If you want to be sure that this code does not secretly depend on pyplot run

import sys
sys.modules['matplotlib.pyplot'] = None

which will prevent pyplot from being imported!

showing#

The core of the API is show

import mpl_gui as mg
from matplotlib.figure import Figure

fig1 = Figure(label='A Label!')

fig2 = Figure()

mg.show([fig1, fig2])

which will show both figures and block until they are closed. As part of the “showing” process, the correct GUI objects will be created, put on the screen, and the event loop for the host GUI framework is run.

blocking (or not)#

Similar to plt.ion and plt.ioff, we provide mg.ion() and mg.ioff() which have identical semantics. Thus

import mpl_gui as mg
from matplotlib.figure import Figure

mg.ion()
print(mg.is_interactive())
fig = Figure()

mg.show([fig])  # will not block

mg.ioff()
print(mg.is_interactive())
mg.show([fig])  # will block!

As with plt.show, you can explicitly control the blocking behavior of mg.show via the block keyword argument

import mpl_gui as mg
from matplotlib.figure import Figure

fig = Figure(label='control blocking')

mg.show([fig], block=False)  # will never block
mg.show([fig], block=True)   # will always block

The interactive state is shared Matplotlib and can also be controlled with matplotlib.interactive and queried via matplotlib.is_interactive.

Figure and Axes Creation#

In analogy with matplotlib.pyplot we also provide figure, subplots and subplot_mosaic

import mpl_gui as mg
fig1 = mg.figure()
fig2, axs = mg.subplots(2, 2)
fig3, axd = mg.subplot_mosaic('AA\nBC')

mg.show([fig1, fig2, fig3])

If mpl_gui is in “interactive mode”, mpl_gui.figure, mpl_gui.subplots and mpl_gui.subplot_mosaic will automatically put the new Figure in a window on the screen (but not run the event loop).

FigureRegistry#

In the above examples it is the responsibility of the user to keep track of the Figure instances that are created. If the user does not keep a hard reference to the fig object, either directly or indirectly through its children, then it will be garbage collected like any other Python object. While this can be advantageous in some cases (such as scripts or functions that create many transient figures). It loses the convenience of matplotlib.pyplot keeping track of the instances for you. To this end we also have provided FigureRegistry

import mpl_gui as mg

fr = mg.FigureRegistry()

fr.figure()
fr.subplots(2, 2)
fr.subplot_mosaic('AA\nBC')

fr.show_all()     # will show all three figures
fr.show()         # alias for pyplot compatibility

fr.close_all()    # will close all three figures
fr.close('all')   # alias for pyplot compatibility

Thus, if you are only using this restricted set of the pyplot API then you can change

import matplotlib.pyplot as plt

to

import mpl_gui as mg
plt = mg.FigureRegistry()

and have a (mostly) drop-in replacement.

Additionally, there is a FigureRegistry.by_label accessory that returns a dictionary mapping the Figures’ labels to each Figure

import mpl_gui as mg

fr = mg.FigureRegistry()

figA = fr.figure(label='A')
figB = fr.subplots(2, 2, label='B')

fr.by_label['A'] is figA
fr.by_label['B'] is figB

FigureContext#

A very common use case is to make several figures and then show them all together at the end. To facilitate this we provide a sub-class of FigureRegistry that can be used as a context manager that (locally) keeps track of the created figures and shows them on exit

import mpl_gui as mg

with mg.FigureContext() as fc:
    fc.subplot_mosaic('AA\nBC')
    fc.figure()
    fc.subplots(2, 2)

This will create 3 figures and block on __exit__. The blocking behavior depends on mg.is_interacitve() (and follow the behavior of mg.show or can explicitly controlled via the block keyword argument).

Selecting the GUI toolkit#

mpl_gui makes use of Matplotlib backends for actually providing the GUI bindings. Analagous to matplotlib.use and matplotlib.pyplot.switch_backend mpl_gui provides mpl_gui.select_gui_toolkit to select which GUI toolkit is used. select_gui_toolkit has the same fall-back behavior as pyplot and stores its state in rcParams["backend"]. mpl_gui will consistently co-exist with matplotlib.pyplot managed Figures in the same process.