Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[MNT]: TimedAnimation event loop interval #28647

Open
@thangleiter

Description

@thangleiter

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions