Summary
TimedAnimation resets the event timer at the end of its _step() method:
|
self.event_source.interval = self._interval |
As far as I can tell, this is by design. The original commit included this comment:
|
def _loop_delay(self, *args): |
|
# Reset the interval and change callbacks after the delay. |
|
self.event_source.remove_callback(self._loop_delay) |
|
self.event_source.interval = self._interval |
|
self.event_source.add_callback(self._step) |
To me, this was suprising and it took me a while to get to the bottom of it. From the documentation I took that the event loop calls the animation target at exactly the interval specified. However, the current implementation means that it actually calls it at interval + time_in_callback. In case the callback does some heavy lifting, like processing data or communicating with an instrument, this can add significant overhead to the real interval between frames.
A MWE highlighting the effects of resetting the interval:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import animation
def update(frame):
if len(frame) < 2:
return []
diff = np.diff(frame)
avg.set_text(avg_txt.format(diff.mean()*1e3))
cur.set_text(cur_txt.format(diff[-1]*1e3))
return [avg, cur]
def frame():
times = deque(maxlen=100)
while True:
time.sleep(0.1)
times.append(time.perf_counter())
yield times
fig, ax = plt.subplots(figsize=(1,1))
ax.axis('off')
avg_txt = 'avg: {:.3g} ms'
cur_txt = 'cur: {:.3g} ms'
avg = ax.text(0.5, 0.75, avg_txt.format(0), va='center', ha='center')
cur = ax.text(0.5, 0.25, cur_txt.format(0), va='center', ha='center')
anim = animation.FuncAnimation(fig, update, frame, repeat=False,
cache_frame_data=False, interval=200)
With the current behavior, this results in an effective interval of 300 ms. Commenting out the line resetting the interval results in the effective interval I would expect from interval=200, 200 ms.
Proposed fix
There might be reasonable usecases for resetting the interval, but I cannot see any right now. Hence, I would propose to leave the timer running uninterrupted, i.e., dropping
|
self.event_source.interval = self._interval |
Summary
TimedAnimationresets the event timer at the end of its_step()method:matplotlib/lib/matplotlib/animation.py
Line 1446 in f7b3def
As far as I can tell, this is by design. The original commit included this comment:
matplotlib/lib/matplotlib/animation.py
Lines 314 to 318 in 70e401f
To me, this was suprising and it took me a while to get to the bottom of it. From the documentation I took that the event loop calls the animation target at exactly the
intervalspecified. However, the current implementation means that it actually calls it atinterval + time_in_callback. In case the callback does some heavy lifting, like processing data or communicating with an instrument, this can add significant overhead to the real interval between frames.A MWE highlighting the effects of resetting the interval:
With the current behavior, this results in an effective interval of 300 ms. Commenting out the line resetting the interval results in the effective interval I would expect from
interval=200, 200 ms.Proposed fix
There might be reasonable usecases for resetting the interval, but I cannot see any right now. Hence, I would propose to leave the timer running uninterrupted, i.e., dropping
matplotlib/lib/matplotlib/animation.py
Line 1446 in f7b3def