Why does matplotlib.figure.Figure behave as much as matplotlib.pyplot.figure

One programmer warned me about an issue where matplotlib.pyplot and Tkinter do not behave well together as shown in this question Tkinter / Matplotlib conflict calls infinite mainloop

We modified our code to prevent the possible problems mentioned in the linked question as follows:

Old

import matplotlib.pyplot as plt
self.fig = plt.figure(figsize=(8,6))
if os.path.isfile('./UI.png'):
    image = plt.imread('./UI.png')
    plt.axis('off')
    plt.tight_layout()
    im = plt.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master = master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH,expand=YES)
self.canvas.draw()

      

Intermediate (UI.png not showing)

import matplotlib.pyplot as plt
import matplotlib
self.fig = matplotlib.figure.Figure(figsize=(8, 6))
if os.path.isfile('./UI.png'):
    image = matplotlib.image.imread('./UI.png')
    plt.axis('off')
    plt.tight_layout()
    plt.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
self.canvas.draw()

      

The modified code no longer displays the "background" image, and I basically just tried randomly (since I lost the difference between the two) to display the shape again. The changes included moving from tight_layout

to set_tight_layout

to avoid the warning as pointed out at https://github.com/matplotlib/matplotlib/issues/1852 . The resulting code looks like this:

Potential fix

import matplotlib.pyplot as plt
import matplotlib
self.fig = matplotlib.figure.Figure(figsize=(8, 6))
background_image = self.fig.add_subplot(111)
if os.path.isfile('./UI.png'):
    image = matplotlib.image.imread('./UI.png')
    background_image.axis('off')
    #self.fig.tight_layout() # This throws a warning and falls back to Agg renderer, 'avoided' by using the line below this one.
    self.fig.set_tight_layout(True)
    background_image.imshow(image)
# The Canvas
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, root)
self.canvas.get_tk_widget().pack(fill=BOTH, expand=YES)
self.canvas.draw()

      

So the question is, why do we now use a subplot (using matplotlib.figure.Figure), and before that we did not use (using matplotlib.pyplot)?

PS: I am sorry if this is a stupid question, but almost everything I can find on this question seems to use a variant matplotlib.pyplot

. So I am having trouble finding good documentation for the option matplotlib.figure.Figure

.

+3


source to share


1 answer


TL; DR

So the question is, why do we now use a subplot (using matplotlib.figure.Figure), and before that we did not use (using matplotlib.pyplot)?

subplot

creates an object Axes

. You had one, but the API pyplot

"hid" it under your covers so that you don't realize it. You are now trying to use objects directly, so they will have to handle them themselves.

More detailed reason

The reason you see this behavior has to do with how it works matplotlib.pyplot

. To quote the tutorial :

matplotlib.pyplot

is a set of command-style functions that make matplotlib work like MATLAB .... matplotlib.pyplot

is discreet as it keeps track of the current digit and plot area and the plotting functions are directed to the current axes

The key bit is what pyplot

is consistent. It keeps track of under-the-hood state and hides the object model from you to some extent. It does some implicit things as well. So - if you just call, for example, plt.axis()

under the covers calls and this in turn calls , which returns a new figure , because you don't have to customize the digit with . This is true for most calls - if it doesn't have a curly object in its own state , it will create it. pyplot

plt.gca()

gcf()

pyplot

plt.some_function()

pyplot

So, in your intermediate example, you created your own object Figure

- gave it a name self.fig

(I'm not sure what the structure of your class is, so I don't know which self

is, but I'm assuming it's your object tk.Frame

or something similar).

Dotted line

pyplot

knows nothing aboutself.fig

. So, in your intermediate code, you are calling an imshow()

object Figure

in a state pyplot

, but displaying a different number ( self.fig

) on your canvas .



The problem is not that you need to use subplot

as such, but you need to change the background image to the correct object Figure

. The way you have used subplot

the fix in your potential code will do it, although I suggest an alternative below which it is possible to make the intent clearer.

How to fix

Edit

plt.axis('off')
plt.tight_layout()
plt.imshow(image)

      

to

self.fig.set_tight_layout(True)
ax = self.fig.gca() # You could use subplot here to get an Axes object instead
ax.axis('off')
ax.imshow(image)

      

Main reason note: pyplot

API versus direct use of objects

This is a bit of an opinion, but might help. I usually use an interface pyplot

when I need to get prototypes quickly and want to use one of the fairly standard cases. This is often enough.

As soon as I need to do more complicated things, I'm starting to use the object model directly - keeping their own objects with the name Figure

and Axes

etc.

Mixing the two is possible, but often confusing. You found this with your intermediate solution. So I recommend doing this or that.

+2


source







All Articles