How do I use the "object-oriented interface" without having to rely on pyplot?

I’ve used matplotlib since 2011 and came from MATLAB so I know that it began its life as python replacement for MATLAB plotting functions.

In the intervening years, the object-oriented interface has evolved and I’ve seen it recommended over pyplot in more than one place, for example here: https://matplotlib.org/stable/tutorials/introductory/lifecycle.html

It says:

A note on the Object-Oriented API vs. Pyplot

Matplotlib has two interfaces. The first is an object-oriented (OO) interface. In this case, we utilize an instance of axes.Axes in order to render visualizations on an instance of figure.Figure.

The second is based on MATLAB and uses a state-based interface. This is encapsulated in the pyplot module. See the pyplot tutorials for a more in-depth look at the pyplot interface.

Most of the terms are straightforward but the main thing to remember is that:

  • The Figure is the final image that may contain 1 or more Axes.
  • The Axes represent an individual plot (don’t confuse this with the word “axis”, which refers to the x/y axis of a plot).

We call methods that do the plotting directly from the Axes, which gives us much more flexibility and power in customizing our plot.

Note: In general, try to use the object-oriented interface over the pyplot interface.

Ok! I find working with objects natural so I prefer that. Sold!

But then the tutorial goes on to start with:

import numpy as np
import matplotlib.pyplot as plt

and

fig, ax = plt.subplots()

So what is it now? Are we using pyplot or Axes and Figure? I have yet to find an ostensibly object-oriented example that does not rely on pyplot.

This mixing of paradigms in apparent contravention of the recommendations is, at the very least, confusing.

I have tried to work without relying on pyplot. Working in a jupyter notebook, I tried creating a figure object like this:

import matplotlib as mpl

fig = mpl.figure.Figure()
ax = mpl.axis.Axes(fig, [0.1, 0.1, 0.7, 0.7])

This results in no visible output (using the inline backend). If I try and make the figure appear:

fig.show()

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-73d731ead0b0> in <module>
----> 1 fig.show()

/usr/lib/python3.8/site-packages/matplotlib/figure.py in show(self, warn)
    405         """
    406         if self.canvas.manager is None:
--> 407             raise AttributeError(
    408                 "Figure.show works only for figures managed by pyplot, "
    409                 "normally created by pyplot.figure()")

AttributeError: Figure.show works only for figures managed by pyplot, normally created by pyplot.figure()

tl; dr - how can I routinely create empty Axes and display them in a jupyter notebook without having to use pyplot?

1 Like

You don’t. pyplot is certainly the preferred way to create a figure. After that you need not use pyplot, because once you have a figure you can create all the other objects. However, pyplot still has some useful convenience functions.

The point of the recommendation is not to slavishly disregard the pyplot module, but to not use its state-based machinery where it tries to guess what axes you are working with based on the axes you drew on previously. Hence ax.plot is preferred to plt.plot because the former is explicit.

Its possible we could move plt.figure, plt.subplots somewhere else, but the practical advantages of doing so are minimal.

Okay. It would be helpful to have that exactly this:

The point of the recommendation is not to slavishly disregard the pyplot module, but to not use its state-based machinery where it tries to guess what axes you are working with based on the axes you drew on previously. Hence ax.plot is preferred to plt.plot because the former is explicit.

included in the documentation. (Is there a repo for the website documentation on github? If so I can create a pull request.)

You wrote that pyplot is the preferred way to create a figure. But is it possible to create the figure using matplotlib.figure.Figure() (I assume it must be) and how would that work?

1 Like

You can, but its not trivial. You need to choose a backend, and then call new_figure_manager and then use that manager to access the figure.

@sfbosch
Stephen- Thank you. This is the exact question I’ve been meaning to ask for a while now.

I definitely prefer the oo interface, and imho we should encourage people to use it more. Along those lines I think we should also continue to develop it to be more truly object oriented. What do I mean by that?

Generally I have been able to limit my use of pyplot to two things only:

  • calling pyplot.figure() to instantiate a Figure object, and
  • calling pyplot.show() to display the plot.

Once I create a Figure I use methods on Figure (ex: add_axes()) to create Axes, after which all of my calls are typically methods on the Figure or Axes objects.

A couple of things that I would like to see improved however:

I understand Jody’s ( @jklymak ) comment that the pyplot.figure() does a lot more for you than can Figure.figure(). But, because pyplot is the core of the “functional” non-object oriented interface, it would be nice to at least give the illusion of a separate module for instantiation and showing of the Figure. This will help users avoid the temptation to call other methods from pyplot. So for example, perhaps we have from matplotlib import oop and then one calls:

  • fig = oop.figure() to instantiate the Figure, and
  • oop.show(fig) to display the figure.

The oop module will pass .figure() to pyplot.figure() without giving the user any direct access to any of the other methods within pyplot.

Notice also that a Figure object must be passed into show. This at least gives the illusion of a more object oriented interface (as opposed to .show() being somehow magically aware of the “current” figure).

I realize that these suggestions are really a matter of style and don’t actually solve the issue of there being effectively a “singleton” object under the entire matplotlib hood. (The concepts of “current figure” and “current axis” for example, as well as rcparams). But I do believe that if we honestly want to encourage people to more and more use an object oriented model, then this type of approach will definitely help. To take this a step further, perhaps when oop.figure() is called, each instantiated Figure object gets its own rcparams and its own figure manager. I’m getting a little beyond my own limits in my knowledge of matplotlib here, so comments and corrections are more than welcome. That said, coming from an object orient background, I do appreciate matplotlib’s oo interface, and I understand that (for historical reasons) it is not completely object oriented, but I do believe we can make it more and more so over time.

@tacaswell has mostly implemented that, but when it will make it into the code base is a difficult question.

For reference the implementation is at GitHub - tacaswell/mpl-gui: Prototype for mpl-gui module (I will work on getting it on pypi as soon as I finish this post).

@DanielGoldfarb I would really appreciate any feedback you have on the proposed API!

1 Like

That took longer than I expected… “I’ll just test tk one more time…” lead to “lets re-write a the show blocking logic and how we handle the backend module/class distinction” :person_facepalming:

The code is now live at

and can be installed with pip install mpl-gui. See mpl-gui Documentation — mpl-gui 0.post1+g17841e5 documentation for docs.

1 Like

@tacaswell Thanks. Will try to take a look sometime this week.