【问题标题】:matplotlib exit after animation动画后matplotlib退出
【发布时间】:2013-07-03 11:39:14
【问题描述】:

我正在用 Python 编写一个程序,使用 matplotlib(除其他外)运行动画,显示时间相关薛定谔方程的数值解。

一切正常,但一旦动画完成运行,我希望它所在的窗口自行关闭。我这样做的方式(如下所示)有效,但抛出了我似乎无法捕捉到的异常。它可以很好地满足我的需要,但是错误看起来很混乱。

我有另一种方法,它可以在不引发错误的情况下工作,但需要用户手动关闭窗口(我的目的不可接受)。有人可以指出我做错了什么,或者提出更好的选择吗?

我的代码相关部分的简化版本如下:

from matplotlib import animation as ani
from matplotlib import pyplot as plt

multiplier = 0
def get_data():         # some dummy data to animate
    x = range(-10, 11)
    global multiplier
    y = [multiplier * i for i in x]
    multiplier += 0.005
    return x, y

class Schrodinger_Solver(object):
    def __init__(self, xlim = (-10, 10), ylim = (-10, 10), num_frames = 200):

        self.num_frames = num_frames
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, xlim = xlim, ylim = ylim)
        self.p_line, = self.ax.plot([], [])

        self.ani = ani.FuncAnimation(self.fig, self.animate_frame,
                                     init_func = self.init_func,
                                     interval = 1, frames = self.num_frames,
                                     repeat = False, blit = True)

        plt.show()

    def animate_frame(self, framenum):
        data = get_data()
        self.p_line.set_data(data[0], data[1])

        if framenum == self.num_frames - 1:
            plt.close()
        # closes the window when the last frame is reached,
        # but exception is thrown. Comment out to avoid the error,
        # but then the window needs manual closing

        return self.p_line,

    def init_func(self):
        self.p_line.set_data([], [])
        return self.p_line,

Schrodinger_Solver()

我在 Windows 7 上使用 matplotlib 1.1.0 运行 Python 2.7.2

提前致谢

编辑: 异常和回溯如下:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
    return self.func(*args)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 495, in callit
    func(*args)
  File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 116, in _on_timer
    TimerBase._on_timer(self)
  File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 1092, in _on_timer
    ret = func(*args, **kwargs)
  File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 315, in _step
    still_going = Animation._step(self, *args)
  File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 177, in _step
    self._draw_next_frame(framedata, self._blit)
  File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 197, in _draw_next_frame
    self._post_draw(framedata, blit)
  File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 220, in _post_draw
    self._blit_draw(self._drawn_artists, self._blit_cache)
  File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 240, in _blit_draw
    ax.figure.canvas.blit(ax.bbox)
  File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 244, in blit
    tkagg.blit(self._tkphoto, self.renderer._renderer, bbox=bbox, colormode=2)
  File "C:\Python27\lib\site-packages\matplotlib\backends\tkagg.py", line 19, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
TclError: this isn't a Tk application

Traceback (most recent call last):
  File "C:\Python27\quicktest.py", line 44, in <module>
    Schrodinger_Solver()
  File "C:\Python27\quicktest.py", line 26, in __init__
    plt.show()
  File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 139, in show
    _show(*args, **kw)
  File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 109, in __call__
    self.mainloop()
  File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 69, in mainloop
    Tk.mainloop()
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 325, in mainloop
    _default_root.tk.mainloop(n)
AttributeError: 'NoneType' object has no attribute 'tk'

我可以通过一个小改动来捕获第二个异常,AttributeError:

try: plt.show()
except AttributeError: pass

但无论我尝试什么,第一部分 TclError 仍然存在

【问题讨论】:

  • 有什么例外?还请包括回溯!

标签: python animation matplotlib


【解决方案1】:

几分钟前我遇到了同样的问题...原因是FuncAnimation 中的interval-value 非常低。您的代码尝试每 1 毫秒更新一次图形 - 非常快! (可能不需要 1000 fps)。我试过interval=200,错误消失了……

HTH

【讨论】:

  • 为什么这是个问题呢?在出现错误之前我可以使用多低的间隔值?允许有一些任意限制似乎很奇怪。我正在尝试尽可能快地绘制实时计算的结果(手动确保在 animate 函数返回之前至少经过 0.1 秒),所以我不想插入人为的延迟.
【解决方案2】:

尝试:

if framenum == self.num_frames - 1:
   exit()

它对我有用...

【讨论】:

  • 这通过完全关闭程序来工作。我需要一种单独关闭窗口的方法的原因是我希望在关闭动画窗口后发生其他事情(其他动画、计算、先前计算的动画之间的比较)。关闭程序违背了这个目的。自动化这个过程的关键是我可以让我的电脑带着一大堆计算来处理,并在我回来时完成它。如果我必须在每个动画之后重新开始,那么我还不如坚持我所拥有的。
  • 要了解我想要什么,如果将以下内容添加到代码底部,那么我想要的是第一个动画运行,然后关闭,然后运行第二个动画:multiplier = 0; Schrodinger_Solver()
【解决方案3】:

我面临着完全相同的问题,我设法通过创建另一个 Animation 类来解决这个问题。本质上,我做了两个改变:

  1. 编写一个覆盖_stop_step 方法的自定义类。
  2. 在更新函数中出现StopIteration 错误,而不是使用plt.close。异常会被捕获并且不会破坏脚本。

这是代码。

from matplotlib import animation as ani
from matplotlib import pyplot as plt

class FuncAnimationDisposable(ani.FuncAnimation):
    def __init__(self, fig, func, **kwargs):
        super().__init__(fig, func, **kwargs)
        
    def _step(self, *args):
        still_going = ani.Animation._step(self, *args)
        if not still_going and self.repeat:
            super()._init_draw()
            self.frame_seq = self.new_frame_seq()
            self.event_source.interval = self._repeat_delay
            return True
        elif (not still_going) and (not self.repeat):
            plt.close()  # this code stopped the window
            return False
        else:
            self.event_source.interval = self._interval
            return still_going
        
    def _stop(self, *args):
        # On stop we disconnect all of our events.
        if self._blit:
            self._fig.canvas.mpl_disconnect(self._resize_id)
        self._fig.canvas.mpl_disconnect(self._close_id)

        

multiplier = 0
def get_data():         # some dummy data to animate
    x = range(-10, 11)
    global multiplier
    y = [multiplier * i for i in x]
    multiplier += 0.005
    return x, y

class Schrodinger_Solver(object):
    def __init__(self, xlim = (-10, 10), ylim = (-10, 10), num_frames = 200):

        self.num_frames = num_frames
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, xlim = xlim, ylim = ylim)
        self.p_line, = self.ax.plot([], [])

        self.ani = FuncAnimationDisposable(self.fig, self.animate_frame,
                                     init_func = self.init_func,
                                     interval = 1, frames = self.num_frames,
                                     repeat = False, blit = True)

        plt.show()

    def animate_frame(self, framenum):
        data = get_data()
        self.p_line.set_data(data[0], data[1])

        if framenum == self.num_frames - 1:
            raise StopIteration  # instead of plt.close()
            
        return self.p_line,

    def init_func(self):
        self.p_line.set_data([], [])
        return self.p_line,

Schrodinger_Solver()
Schrodinger_Solver()

print(multiplier)

(代码 sn-p 使用 Python 3.7 和 matplotlib 3.4.2 测试。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 2021-02-01
    • 2018-03-24
    • 1970-01-01
    相关资源
    最近更新 更多