2

Faced with the fact that MatPlotlib when using self.frame.canvas.draw() I got only 12 FPS on a simple chart. I found a good article about acceleration speed MatPlotlib: https://bastibe.de/2013-05-30-speeding-up-matplotlib.html But part of the examples is working code, but the example with the fastest code (500 FPS) is not working. Attempts to read the documentation MatPlotlib have not yet led to success in understanding where the error is in the code: «AttributeError: draw_artist can only be used after an initial draw which caches the renderer». Where is the error in the code?

import matplotlib.pyplot as plt
import numpy as np
import time

fig, ax = plt.subplots()
line, = ax.plot(np.random.randn(100))
plt.show(block=False)

tstart = time.time()
num_plots = 0
while time.time()-tstart < 5:
    line.set_ydata(np.random.randn(100))
    ax.draw_artist(ax.patch)
    ax.draw_artist(line)
    fig.canvas.update()
    fig.canvas.flush_events()
    num_plots += 1
print(num_plots/5)
paradoxarium
  • 177
  • 1
  • 12
  • Try `plt.draw()` immediately after `plt.show()` – William Miller Jan 12 '20 at 08:39
  • I also tried this variant, but the error remains the same. – paradoxarium Jan 12 '20 at 08:46
  • Which line gives the error? Also by the way `block=False` is deprecated, you should use the approach in [this answer](https://stackoverflow.com/questions/28269157/plotting-in-a-non-blocking-way-with-matplotlib/33050617#33050617) instead – William Miller Jan 12 '20 at 08:53
  • If delete «block=False» - the chart is drawn once and stops. After closing the chart, the code is executed further, where everything rests on the same error. I looked at the answer on the link: pauses do not help either. – paradoxarium Jan 12 '20 at 09:17
  • Which line exactly is giving the error? – William Miller Jan 12 '20 at 09:19
  • 1. After deleting «block=False» chart stops. 2. After close window with chart, the code is executed next and 3. Regardless of whether there is plt.draw() or not, the code breaks on the fig.canvas.update() line with an error: AttributeError: 'FigureCanvasTkAgg' object has no attribute 'update' – paradoxarium Jan 12 '20 at 09:31
  • The link you gave is using the qt backend and `update` is a qt method, are you using the Qt5agg backend? Also note the call to `plt.ion()` in the link I gave. – William Miller Jan 12 '20 at 09:35
  • In code don't used Qt and I don't know why error in FigureCanvasTkAgg. The main question: if we add new functions and methods, will the code not lose the speed for which this code was written? Just as I mentioned earlier, even before the error, the code stops after only one frame is drawn, and you need to close the window - which is already wrong, regardless of what errors will happen next. Is your error reproducing or is your code working fine? – paradoxarium Jan 12 '20 at 09:49
  • You need to use the Qt4agg or Qt5agg backend to use `fig.canvas.update()` . Adding additional setup outside of the loop will not cause a loss in the replotting speed, which is the point of the article. Have you tried putting `plt.ion()` before `plt.show()` as the linked answer does? That is part of the current standard approach to non-blocking `plt.show()`. – William Miller Jan 12 '20 at 09:58
  • I add: «import matplotlib matplotlib.use('Qt5Agg')» - chart draw once and need close window. The code is executed further, but the window is already a window with the schedule closed and there is nothing. Add plt.ion() after plt.show() Returns the same error with which it all started: «AttributeError: draw_artist can only be used after an initial draw which caches the renderer» on «ax.draw_artist(ax.patch)» – paradoxarium Jan 12 '20 at 10:04
  • @WilliamMiller `block=False` is not deprecated! Whether or not plt.ion() is used does not change the error OP gets. – ImportanceOfBeingErnest Jan 12 '20 at 12:40
  • @ImportanceOfBeingErnest My mistake, should have vetted my source better.... – William Miller Jan 12 '20 at 15:41
  • @paradoxarium As usual Ernest is here to save the day with the correct answer, apologies for misleading you – William Miller Jan 12 '20 at 15:45
  • 1
    Everything is ok) The main thing is that there is a solution to the problem. – paradoxarium Jan 12 '20 at 16:35

1 Answers1

2

If using the Qt5Agg backend, you can indeed just do what the error suggest, namely draw the canvas once before starting the loop.

import time
import numpy as np
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
line, = ax.plot(np.random.randn(100))

fig.canvas.draw()
plt.show(block=False)


tstart = time.time()
num_plots = 0
while time.time()-tstart < 5:
    line.set_ydata(np.random.randn(100))
    ax.draw_artist(ax.patch)
    ax.draw_artist(line)
    fig.canvas.update()
    fig.canvas.flush_events()
    num_plots += 1
print(num_plots/5)

However, I would actually use blit instead of PyQt's update, such that it would work with any backend,

import time
import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
line, = ax.plot(np.random.randn(100))

fig.canvas.draw()
plt.show(block=False)


tstart = time.time()
num_plots = 0
while time.time()-tstart < 5:
    line.set_ydata(np.random.randn(100))
    ax.draw_artist(ax.patch)
    ax.draw_artist(line)
    fig.canvas.blit(ax.bbox)
    fig.canvas.flush_events()
    num_plots += 1
print(num_plots/5)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    Super! Thanks to your help, a bug has been fixed and graphics rendering has been accelerated by almost 10 times. According to your recommendation: the use of “blit” is faster than “Qt5Agg” by 1.3..1.4 times. – paradoxarium Jan 12 '20 at 16:31