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:
provide a state-full implicit API that rhymes / was inspired by MATLAB
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.