Matplotlib动画应以双倍速度运行

2024-04-20 12:04:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我编写了一个简单的动画示例,并以每秒30帧的速度运行,但它的运行速度似乎比我预期的要快得多。我给它计时,果然,它是以双倍的速度运行的。似乎有两个计时器运行的实例稍微有点偏移。下面是一个复制这种行为的简短示例。但是,错误只发生在70%左右的时间。在使用动画包的过程中是否有什么地方我做错了,或者我是否应该深入研究matplotlib代码并尝试在GitHub上提交一个问题?你知道吗

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

class TestAnimation(object):
    def __init__(self, fps=30):
        # Set up animation
        self.render_dt = 1.0/fps
        self.t = time.time()
        self.fig = plt.figure(3)
        self.line, =  plt.plot([0, 1, 2], [0, 2, 1])

        # Run animation
        frames = np.arange(100)
        print "interval = %f ms" % (self.render_dt * 1000)
        self.started = False
        self.ani = animation.FuncAnimation(self.fig, self.update, frames,
                                           interval=self.render_dt * 1000, blit=True)
        # raw_input("Push enter to begin animation")
        self.started = True

    def show(self):
        plt.show()

    def update(self, idx):
        # if idx > 0 and not self.started:
        #    raise Exception("Animation has not yet been started!")
        dt = time.time() - self.t
        self.t = time.time()
        print "dt = %.3f, idx = %d, t = %f" % (dt, idx, time.time())
        self.line.set_data([0, 1, 2], [0, 2, float(idx)/50])
        return [self.line]

anim = TestAnimation()
anim.show()

Tags: importselftimematplotlibdefasshowline
1条回答
网友
1楼 · 发布于 2024-04-20 12:04:56

我终于弄明白发生了什么事。结果表明,只有在使用TkAgg后端时才会出现问题,并且问题是由回调期间重新启动TkTimer引起的。你知道吗

Tk使用单次回调,这意味着Matplotlib的TkTimer类必须在每次使用Tk.after()函数关闭计时器时创建一个新的Tk回调。当TkTimer停止时,它使用Tk.after_cancel()函数取消Tk回调。但是,直到TkTimer回调结束时才创建新的Tk回调,这意味着尝试在TkTimer回调中停止TkTimer将失败。然后,启动计时器会导致创建第二个Tk回调,从而导致动画以双倍的速度运行。下面是一个简短的代码片段,再现了这个问题:

import matplotlib
matplotlib.use('TKAgg')

import matplotlib.pyplot as plt
import time


class TestAnimation(object):
    def __init__(self, fps=10):
        # Set up animation
        self.fig = plt.figure()
        self.axes = plt.subplot(111)
        self.fig.show()
        self.fig.canvas.draw()

        self.render_dt = 1.0 / fps
        self.t = time.time()
        self.count = 0
        self.line, = plt.plot([0, 1, 2], [0, 2, 1], animated=True)

        # Save background
        self.background = self.fig.canvas.copy_from_bbox(self.axes.bbox)

        # Run animation
        self.event_source = self.fig.canvas.new_timer()
        self.event_source.interval = self.render_dt * 1000
        print("Create new TkTimer with interval %f ms" % self.event_source.interval)
        self.event_source.add_callback(self.update)
        self.event_source.start()

    def show(self):
        plt.show()

    def update(self):
        dt = time.time() - self.t
        self.t = time.time()
        idx = self.count % 100
        if idx == 10:
            print("TkTimer Start, Stop")
            self.event_source.stop()
            self.event_source.start()
        print "dt = %.3f, idx = %d, t = %f" % (dt, idx, time.time())

        self.fig.canvas.restore_region(self.background)
        self.line.set_data([0, 1, 2], [0, 2, float(idx) / 50])
        self.axes.draw_artist(self.line)
        self.fig.canvas.blit(self.axes.bbox)
        self.count += 1

下面的输出显示计时器以10 Hz的预期速度运行,直到在回调期间计时器重新启动。当计时器重新启动时,每10秒钟触发两次回调,而不是仅触发1次。你知道吗

Create new TkTimer with interval 100.000000 ms
dt = 0.177, idx = 0, t = 1534262220.665633
dt = 0.103, idx = 1, t = 1534262220.769119
dt = 0.104, idx = 2, t = 1534262220.873422
dt = 0.105, idx = 3, t = 1534262220.978350
dt = 0.105, idx = 4, t = 1534262221.083121
dt = 0.112, idx = 5, t = 1534262221.195082
dt = 0.105, idx = 6, t = 1534262221.299866
dt = 0.105, idx = 7, t = 1534262221.404975
dt = 0.105, idx = 8, t = 1534262221.509763
dt = 0.105, idx = 9, t = 1534262221.614287
TkTimer Start, Stop
dt = 0.104, idx = 10, t = 1534262221.718872
dt = 0.100, idx = 11, t = 1534262221.819030
dt = 0.005, idx = 12, t = 1534262221.823720
dt = 0.100, idx = 13, t = 1534262221.923745
dt = 0.005, idx = 14, t = 1534262221.928303
dt = 0.100, idx = 15, t = 1534262222.028263
dt = 0.005, idx = 16, t = 1534262222.032788
dt = 0.100, idx = 17, t = 1534262222.132509
dt = 0.004, idx = 18, t = 1534262222.136965
dt = 0.100, idx = 19, t = 1534262222.236865
dt = 0.005, idx = 20, t = 1534262222.241497
dt = 0.100, idx = 21, t = 1534262222.341490
dt = 0.005, idx = 22, t = 1534262222.346316
dt = 0.100, idx = 23, t = 1534262222.446195
dt = 0.005, idx = 24, t = 1534262222.450940

通过查看animation类,可以看出为什么TkTimer会在回调中重新启动。在animation类中的第一个TkTimer回调中,它绘制了一些东西,这些东西将控制Tk。作为绘图的结果,Tk触发resize\u事件和draw\u事件。动画类在调整大小时暂停TkTimer,并在调整完成后重新启动。不管出于什么原因,有时这些回调发生在TkTimer回调内部,从而导致这些问题的发生。你知道吗

相关问题 更多 >