12

我正在用 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 仍然存在

4

3 回答 3

5

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

高温高压

于 2014-05-09T23:29:51.943 回答
0

尝试:

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

这个对我有用...

于 2013-07-03T18:01:53.990 回答
0

我面临着完全相同的问题,我设法通过创建另一个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)

(代码片段使用 Python 3.7 和 matplotlib 3.4.2 进行了测试。)

于 2021-09-01T13:36:55.630 回答