'Matplotlib animation : adding / removing legend item slows down animations
The code below dynamically add some lines and their legend in a matlplot lib animated plot. I have an issue with the legend adding : it significantly reduces the frame rate.
Let's describe the issue. The root question was how to update legend in order to see the newly added plot legend? I've tried several options :
1- Add line, try to add legend calling ax.legend()
lines.append(ax.plot(x, np.sin(x), label='line {}'.format(len(lines)))[0])
legend = ax.legend(loc='upper left')
Result : legend not updated. Frame rate does not change significantly when the lines are added. It's not what I want, I cannot see the legends of added lines
2- Add line and return legend in animation update function
lines.append(ax.plot(x, np.sin(x), label='line {}'.format(len(lines)))[0])
legend = ax.legend(loc='upper left')
# Forcing legend redraw by returning it in update function updates it
ret = *lines, legend,
Result : legend updated. Frame rate significantly drops when the lines are added. If I try to only update legend once (by returning *lines, legend, once after new plot added), I can see 1 frame with the updated legend, then we cannot see anymore the legend in the next frames.
The code :
import numpy as np
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from threading import Thread, Lock
VIZ_UPDATE_FS = 50
ADD_LEGEND_IN_BLIT = True
fig, ax = plt.subplots()
f = 10
x = np.arange(0, 2*np.pi, 0.01)
lines = []
lines.append(ax.plot(x, np.sin(x), label='line {}'.format(len(lines)))[0])
legend = ax.legend(loc='upper left')
t_fps_start = time.time()
fps_nb_frames = 0
def animate(i):
global fps_nb_frames, t_fps_start
fps_nb_frames += 1
with add_line_mutex:
for j, line in enumerate(lines):
line.set_ydata(np.sin(x + i/((j + 1)*10.0))) # update the data
# reset FPS counter to get precise fps measurements
if not t_fps_start or (time.time() - t_fps_start) > 10:
t_fps_start = time.time()
fps_nb_frames = 0
print('reset FPS counter')
# Measure FPS after 5s an until 1 min
if time.time() - t_fps_start > 3:
print('VIZ fps = {:.1f}'.format(fps_nb_frames/(time.time() - t_fps_start)))
if ADD_LEGEND_IN_BLIT or i%100 == 0:
ret = *lines, legend,
else:
ret = *lines,
return ret
def add_line():
global legend
t_0 = time.time()
while not stop_th_run and len(lines) < 15:
if int(time.time() - t_0) > 0.2:
t_0 = time.time()
with add_line_mutex:
lines.append(ax.plot(x, np.sin(x), label='line {}'.format(len(lines)))[0])
legend = ax.legend(loc='upper left')
time.sleep(0.1)
ani = animation.FuncAnimation(fig, animate, np.arange(1, 200),
interval=1/VIZ_UPDATE_FS*1000, blit=True)
add_line_th = Thread(target=add_line)
add_line_mutex = Lock()
stop_th_run = False
add_line_th.start()
plt.show()
stop_th_run = True
while add_line_th.is_alive():
time.sleep(0.1)
How can we add dynamically a line and its corresponding legend keeping a decent framerate?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
