简体   繁体   English

优雅地停止(而不是暂停)python(matplotlib)中的动画图

[英]Gracefully stopping (not pausing) an animated plot in python (matplotlib)

I am running an animated scatter in a process. 我正在运行一个动画散点图。 Everything is working fine, except that an exception is throw when I want to exit everything. 一切工作正常,但当我要退出所有内容时会抛出异常。

import multiprocessing as mp
import time
from collections import deque

def start_colored_scores(nb_channels):
    q = mp.Queue()
    process = mp.Process(target=colored_scores,args=(q,nb_channels,4000))
    process.start()
    return process,q

def colored_scores(q,nb_channels,size):
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    fig, axes = plt.subplots(nrows=nb_channels,ncols=1,sharex=True,sharey=True)
    plt.axis([-1.0,1.0,-1.0,1.0])
    scats = [axe.scatter([0], [0], c="white", s=size) for axe in axes]
    def animate(i):
        scores = q.get()
        if scores is None : # this is the external signal saying things should stop
            plt.close()
            return [axe.scatter([0], [0], c="white", s=size) for axe in axes]
        scats = []
        for score,axe in zip(scores,axes):
            score = max(min(1,1-score),0)
            scats.append(axe.scatter([0], [0], c=(1-score,0,score), s=size))
        return scats
    ani = animation.FuncAnimation(fig, animate, interval=1, blit=True)
    plt.show()

For example, this is working fine: 例如,这工作正常:

_,q = start_colored_scores(2)
x = 0
right = 1
time_start = time.time()
while time.time()-time_start < 5:
    if right==1 and x>1.0:
        x = 1.0
        right = -1
    if right==-1 and x<0.0:
        x = 0.0
        right = 1
    x+=right*0.02
    q.put([x,1-x])
    time.sleep(0.02)
q.put(None) # indicating I do not need plotting anymore

print "this is printed ... exception in the process ?"

The behavior is as I expect : scatters are displayed and animated for 5 seconds, then the program continues. 行为符合我的预期:分散显示并设置动画5秒钟,然后程序继续运行。 The only issue is that an exception is thrown (I guess in the process) saying : 唯一的问题是抛出了一个异常(我猜是在过程中):

AttributeError: 'NoneType' object has no attribute 'tk'

Is there a way to do the exact same thing but avoiding the exception ? 有什么办法可以做完全相同的事情,但要避免出现异常? Or to catch this exception somewhere ? 还是在某处捕获此异常?

You can catch that exception pretty easily: 您可以轻松捕获该异常:

def colored_scores(q,nb_channels,size):
    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    fig, axes = plt.subplots(nrows=nb_channels,ncols=1,sharex=True,sharey=True)
    plt.axis([-1.0,1.0,-1.0,1.0])
    scats = [axe.scatter([0], [0], c="white", s=size) for axe in axes]
    def animate(i):
        scores = q.get()
        if scores is None : # this is the external signal saying things should stop
            plt.close()
            return [axe.scatter([0], [0], c="white", s=size) for axe in axes]
        scats = []
        for score,axe in zip(scores,axes):
            score = max(min(1,1-score),0)
            scats.append(axe.scatter([0], [0], c=(1-score,0,score), s=size))
        return scats
    ani = animation.FuncAnimation(fig, animate, interval=1, blit=True)
    try:
        plt.show()
    except AttributeError: # This will supress the exception
        pass

However, once you get catch that one, you get a new one (at least on my system): 但是,一旦找到了那个,就得到了一个新的(至少在我的系统上):

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in __call__
    return self.func(*args)
  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 536, in callit
    func(*args)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer
    TimerBase._on_timer(self)
  File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer
    ret = func(*args, **kwargs)
  File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 876, in _step
    still_going = Animation._step(self, *args)
  File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 735, in _step
    self._draw_next_frame(framedata, self._blit)
  File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 755, in _draw_next_frame
    self._post_draw(framedata, blit)
  File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 778, in _post_draw
    self._blit_draw(self._drawn_artists, self._blit_cache)
  File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 798, in _blit_draw
    ax.figure.canvas.blit(ax.bbox)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 353, in blit
    tkagg.blit(self._tkphoto, self.renderer._renderer, bbox=bbox, colormode=2)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 20, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
TclError: this isn't a Tk application

I can't find any way to supress that one. 我找不到任何办法抑制那个。 What you could do, is just terminate the subprocess, rather than try to send it a signal to shutdown: 您可以做的只是终止子进程,而不是尝试向其发送关闭信号:

proc,q = start_colored_scores(2)
x = 0
right = 1
time_start = time.time()
while time.time()-time_start < 5:
    if right==1 and x>1.0:
        x = 1.0
        right = -1
    if right==-1 and x<0.0:
        x = 0.0
        right = 1
    x+=right*0.02
    q.put([x,1-x])
    time.sleep(0.02)
#q.put(None) # indicating I do not need plotting anymore
proc.terminate()

This is not as graceful as sending something through the queue (and doesn't allow for any additional clean-up in the sub-process, assuming you want it), but doesn't throw any exceptions. 这不像通过队列发送内容那样优雅(并且假设您愿意,它不允许在子流程中进行任何其他清理),但是不会引发任何异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM