6

In Matplotlib, I can update the visual effect of the plot in a figure by calling the Figure object, say fig and calling the method show (fig.show()). But I also can do that by calling the canvas object in the figure and then calling method draw (fig.canvas.draw()).

Example:

import matplotlib.pyplot as plt

fig, axes = plt.subplots()
axes.plot([1,2,3],[2,3,4])
fig.show()
axes.plot([1,2,3],[-2,23,14])
fig.show()

and

import matplotlib.pyplot as plt

fig, axes = plt.subplots()
axes.plot([1,2,3],[2,3,4])
fig.show()
axes.plot([1,2,3],[-2,23,14])
fig.canvas.draw()

I suspected the fig.show also performs self.canvas.draw() and some other things. But when I checked the code, I see only that it may call self.canvas.draw_idle().

What is the difference between fig.show, fig.canvas.draw, fig.canvas.draw_idle??

Arief
  • 199
  • 1
  • 12

1 Answers1

4

When I call fig.show() in a python script that is run from the command line (in Anaconda on Windows), it briefly appears and disappears from my screen. This is different from plt.show() which shows all open figures and the Python script hangs until I close them to continue running. (I know you are not discussing plt.show() above but wanted to mention it for clairty and completeness.)

fig.show() calls plt.canvas.draw_idle(), which "schedules a rendering the next time the GUI window is going to re-paint the screen" and thus calls fig.canvas.draw() under certain conditions. In fact, that same link continues to note that "Even if multiple calls to draw_idle occur before control returns to the GUI event loop, the figure will only be rendered once". To be sure that the screen is updated as soon as possible, the documentation suggests that fig.canvas.draw_idle should be followed by fig.canvas.flush_events().

By contrast, fig.canvas.draw() renders the figure even if no output is produced. Looking at the documentation, this function renders the figure and "walk[s] the artist tree even if not output is produced because this will trigger deferred work (like computing limits auto-limits and tick values)". So you can generate the figure in memory and not actually show or print it. The only instance I have seen where it is useful is when you need details about the formatting of the figure before the final rendering and printing/saving.

Based on that info, I did a timing experiment:

import matplotlib.pyplot as plt
import datetime

N = 10**4

fig, axes = plt.subplots()
axes.plot([1,2,3],[2,3,4])

a = datetime.datetime.now()
for i in range(N):
    fig.show()
b = datetime.datetime.now()
print("fig.show:               ", b-a)
a = datetime.datetime.now()

for i in range(N):
    fig.canvas.draw_idle()
b = datetime.datetime.now()
print("fig.canvas.draw_idle:   ", b-a)

a = datetime.datetime.now()
for i in range(N):
    fig.canvas.draw()
b = datetime.datetime.now()
print("fig.canvas.draw:        ", b-a)


for i in range(N):
    fig.canvas.draw_idle()
    fig.canvas.flush_events()
b = datetime.datetime.now()
print("draw_idle, flush_events:", b-a)

# fig.show:                0:00:00.280800
# fig.canvas.draw_idle:    0:00:00.015600
# fig.canvas.draw:         0:03:43.450512
# draw_idle, flush_events: 0:08:29.103440

Based on these timings, you can see why the documentation recommends using fig.canvas.draw_idle() over fig.canvas.draw() in most cases.

ramzeek
  • 2,226
  • 12
  • 23
  • You should test if the screen is actually redrawn every time, which I am sure, it is not unless you use fig.canvas.draw()! – Martin R Jul 14 '23 at 17:13