Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait until matplotlib animation ends?

Consider the following code directly taken from the Matplotlib documentation:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time # optional for testing only
import cv2 # optional for testing only

fig = plt.figure()   

def f(x, y):
    return np.sin(x) + np.cos(y)

x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

im = plt.imshow(f(x, y), animated=True)    

def updatefig(*args):
    global x, y
    x += np.pi / 15.
    y += np.pi / 20.
    im.set_array(f(x, y))
    return im,

ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show()

This work fine on my system. Now, try to append the following piece of code to the above code:

while True: 
  #I have tried any of these 3 commands, without success:  
    pass
    #time.sleep(1)
    #cv2.waitKey(10)

What happens is that the program freezes. Apparently, the "Animation" class of Matplotlib runs the animation in a separate thread. So I have the 2 following questions:

1) If the process runs in a separate thread, why is it disturbed by the subsequent loop ?

2) How to say to python to wait until the animation has ended ?

like image 895
MikeTeX Avatar asked Dec 23 '15 09:12

MikeTeX


People also ask

What is interval in Matplotlib animation?

interval: It is an optional integer value that represents the delay between each frame in milliseconds. Its default is 100. repeat_delay: It is an optional integer value that adds a delay in milliseconds before repeating the animation.

How do I stop func animation?

Alternatively you may stop the animation with anim. event_source. stop() . In order to have access to the animation inside the animating function one may use a class and make the animation a class variable.

What is the PLT pause?

The pause() function in pyplot module of matplotlib library is used to pause for interval seconds. Parameters: This method does not accepts any parameters.


1 Answers

For me, copying into ipython works as expected (animation plays first then the infinite loop) but when running the script it freezes.

1) I'm not sure exactly how cpython handles multi-threading, especially when combined with a particular matplotlib backend but it seems to be failing here. One possibility is to be explicit about how you want to use threads, by using

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import multiprocessing as mp


fig = plt.figure()   

def f(x, y):
    return np.sin(x) + np.cos(y)

x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

im = plt.imshow(f(x, y), animated=True)    

def updatefig(*args):
    global x, y
    x += np.pi / 15.
    y += np.pi / 20.
    im.set_array(f(x, y))
    return im,

#A function to set thread number 0 to animate and the rest to loop
def worker(num):
    if num == 0:
        ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
        plt.show()
    else:
        while True: 
            print("in loop")
            pass

    return


# Create two threads
jobs = []
for i in range(2):
    p = mp.Process(target=worker, args=(i,))
    jobs.append(p)
    p.start()

Which defines two threads and sets one to work on animation, one to loop.

2) To fix this, as suggested by @Mitesh Shah, you can use plt.show(block=True). For me, the script then behaves as expected with animation and then loop. Full code:

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

fig = plt.figure()   

def f(x, y):
    return np.sin(x) + np.cos(y)

x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

im = plt.imshow(f(x, y), animated=True)    

def updatefig(*args):
    global x, y
    x += np.pi / 15.
    y += np.pi / 20.
    im.set_array(f(x, y))
    return im,

ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True)
plt.show(block=True)

while True: 
    print("in loop")
    pass

UPDATE: Alternative is to simply use interactive mode,

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

fig = plt.figure()   
plt.ion()
plt.show()

def f(x, y):
    return np.sin(x) + np.cos(y)

def updatefig(*args):
    global x, y


x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
im = plt.imshow(f(x, y))    

for i in range(500):

    x += np.pi / 15.
    y += np.pi / 20.
    im.set_array(f(x, y))
    plt.draw()
    plt.pause(0.0000001)
like image 113
Ed Smith Avatar answered Sep 29 '22 10:09

Ed Smith