Consider the following code directly taken from the Matplotlib documentation:
从Matplotlib文档中直接获取以下代码:
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:
程序冻结了。显然,Matplotlib的“动画”类在一个单独的线程中运行动画。所以我有两个问题:
1) If the process runs in a separate thread, why is it disturbed by the subsequent loop ?
1)如果进程在一个单独的线程中运行,为什么会受到后续循环的干扰?
2) How to say to python to wait until the animation has ended ?
2)如何对python说等待动画结束?
3 个解决方案
#1
3
For me, copying into ipython works as expected (animation plays first then the infinite loop) but when running the script it freezes.
对我来说,复制到ipython中就像预期的一样(先是动画播放,然后是无限循环),但是当运行脚本时,它会冻结。
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
1)我不确定cpython如何处理多线程,特别是当与一个特定的matplotlib后端结合时,但它似乎在这里失败了。一种可能是明确地说明如何使用线程。
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:
如@Mitesh Shah所建议的,你可以使用plt.show(block=True)来解决这个问题。对于我来说,脚本的行为与动画和循环一样。完整的代码:
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)
#2
2
We can run the animation function in a separate thread. Then start that thread. Once a new thread is created, the execution will continue.
We then use p.join()
to wait for our previously created thread to finish execution. As soon as the execution finished (or terminates for some reason) the code will continue further.
我们可以在一个单独的线程中运行动画功能。然后启动该线程。一旦创建了新线程,执行将继续。然后我们使用.join()来等待我们之前创建的线程完成执行。一旦执行完成(或由于某种原因终止),代码将会继续下去。
Also matplotlib works differently in Interactive Python shells vs. system command line shells, the below code should work for both these scenarios:
同样,在交互式Python shell和系统命令行shell中,matplotlib的工作方式不同,下面的代码应该适用于这两种情况:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from multiprocessing import Process
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 plot_graph(*args):
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()
p = Process(target=plot_graph)
p.start()
# Code here computes while the animation is running
for i in range(10):
time.sleep(1)
print('Something')
p.join()
print("Animation is over")
# Code here to be computed after animation is over
I hope this helped! You can find more information here: Is there a way to detach matplotlib plots so that the computation can continue?
我希望这帮助!您可以在这里找到更多的信息:是否有方法可以分离matplotlib块,以便继续计算?
Cheers! :)
干杯!:)
#3
2
Thanks to the help of Ed Smith and MiteshNinja, I have finally succeeded in finding a robust method that works not only with the Ipython console, but also with the Python console and the command line. Furthermore, it allows total control on the animation process. Code is self explanatory.
多亏了Ed Smith和MiteshNinja的帮助,我终于找到了一种健壮的方法,不仅可以使用Ipython控制台,还可以使用Python控制台和命令行。此外,它还允许对动画过程进行完全控制。代码是自说明的。
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from multiprocessing import Process
import time # optional for testing only
import matplotlib.animation as animation
# A. First we define some useful tools:
def wait_fig():
# Block the execution of the code until the figure is closed.
# This works even with multiprocessing.
if matplotlib.pyplot.isinteractive():
matplotlib.pyplot.ioff() # this is necessary in mutliprocessing
matplotlib.pyplot.show(block=True)
matplotlib.pyplot.ion() # restitute the interractive state
else:
matplotlib.pyplot.show(block=True)
return
def wait_anim(anim_flag, refresh_rate = 0.1):
#This will be used in synergy with the animation class in the example
#below, whenever the user want the figure to close automatically just
#after the animation has ended.
#Note: this function uses the controversial event_loop of Matplotlib, but
#I see no other way to obtain the desired result.
while anim_flag[0]: #next code extracted from plt.pause(...)
backend = plt.rcParams['backend']
if backend in plt._interactive_bk:
figManager = plt._pylab_helpers.Gcf.get_active()
if figManager is not None:
figManager.canvas.start_event_loop(refresh_rate)
def draw_fig(fig = None):
#Draw the artists of a figure immediately.
#Note: if you are using this function inside a loop, it should be less time
#consuming to set the interactive mode "on" using matplotlib.pyplot.ion()
#before the loop, event if restituting the previous state after the loop.
if matplotlib.pyplot.isinteractive():
if fig is None:
matplotlib.pyplot.draw()
else:
fig.canvas.draw()
else:
matplotlib.pyplot.ion()
if fig is None:
matplotlib.pyplot.draw()
else:
fig.canvas.draw()
matplotlib.pyplot.ioff() # restitute the interactive state
matplotlib.pyplot.show(block=False)
return
def pause_anim(t): #This is taken from plt.pause(...), but without unnecessary
#stuff. Note that the time module should be previously imported.
#Again, this use the controversial event_loop of Matplotlib.
backend = matplotlib.pyplot.rcParams['backend']
if backend in matplotlib.pyplot._interactive_bk:
figManager = matplotlib.pyplot._pylab_helpers.Gcf.get_active()
if figManager is not None:
figManager.canvas.start_event_loop(t)
return
else: time.sleep(t)
#--------------------------
# B. Now come the particular functions that will do the job.
def f(x, y):
return np.sin(x) + np.cos(y)
def plot_graph():
fig = plt.figure()
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
im = fig.gca().imshow(f(x, y))
draw_fig(fig)
n_frames = 50
#==============================================
#First method - direct animation: This use the start_event_loop, so is
#somewhat controversial according to the Matplotlib doc.
#Uncomment and put the "Second method" below into comments to test.
'''for i in range(n_frames): # n_frames iterations
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
draw_fig(fig)
pause_anim(0.015) # plt.pause(0.015) can also be used, but is slower
wait_fig() # simply suppress this command if you want the figure to close
# automatically just after the animation has ended
'''
#================================================
#Second method: this uses the Matplotlib prefered animation class.
#Put the "first method" above in comments to test it.
def updatefig(i, fig, im, x, y, anim_flag, n_frames):
x = x + i * np.pi / 15.
y = y + i * np.pi / 20.
im.set_array(f(x, y))
if i == n_frames-1:
anim_flag[0] = False
anim_flag = [True]
animation.FuncAnimation(fig, updatefig, repeat = False, frames = n_frames,
interval=50, fargs = (fig, im, x, y, anim_flag, n_frames), blit=False)
#Unfortunately, blit=True seems to causes problems
wait_fig()
#wait_anim(anim_flag) #replace the previous command by this one if you want the
#figure to close automatically just after the animation
#has ended
#================================================
return
#--------------------------
# C. Using multiprocessing to obtain the desired effects. I believe this
# method also works with the "threading" module, but I haven't test that.
def main(): # it is important that ALL the code be typed inside
# this function, otherwise the program will do weird
# things with the Ipython or even the Python console.
# Outside of this condition, type nothing but import
# clauses and function/class definitions.
if __name__ != '__main__': return
p = Process(target=plot_graph)
p.start()
print('hello', flush = True) #just to have something printed here
p.join() # suppress this command if you want the animation be executed in
# parallel with the subsequent code
for i in range(3): # This allows to see if execution takes place after the
#process above, as should be the case because of p.join().
print('world', flush = True)
time.sleep(1)
main()
#1
3
For me, copying into ipython works as expected (animation plays first then the infinite loop) but when running the script it freezes.
对我来说,复制到ipython中就像预期的一样(先是动画播放,然后是无限循环),但是当运行脚本时,它会冻结。
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
1)我不确定cpython如何处理多线程,特别是当与一个特定的matplotlib后端结合时,但它似乎在这里失败了。一种可能是明确地说明如何使用线程。
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:
如@Mitesh Shah所建议的,你可以使用plt.show(block=True)来解决这个问题。对于我来说,脚本的行为与动画和循环一样。完整的代码:
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)
#2
2
We can run the animation function in a separate thread. Then start that thread. Once a new thread is created, the execution will continue.
We then use p.join()
to wait for our previously created thread to finish execution. As soon as the execution finished (or terminates for some reason) the code will continue further.
我们可以在一个单独的线程中运行动画功能。然后启动该线程。一旦创建了新线程,执行将继续。然后我们使用.join()来等待我们之前创建的线程完成执行。一旦执行完成(或由于某种原因终止),代码将会继续下去。
Also matplotlib works differently in Interactive Python shells vs. system command line shells, the below code should work for both these scenarios:
同样,在交互式Python shell和系统命令行shell中,matplotlib的工作方式不同,下面的代码应该适用于这两种情况:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from multiprocessing import Process
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 plot_graph(*args):
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()
p = Process(target=plot_graph)
p.start()
# Code here computes while the animation is running
for i in range(10):
time.sleep(1)
print('Something')
p.join()
print("Animation is over")
# Code here to be computed after animation is over
I hope this helped! You can find more information here: Is there a way to detach matplotlib plots so that the computation can continue?
我希望这帮助!您可以在这里找到更多的信息:是否有方法可以分离matplotlib块,以便继续计算?
Cheers! :)
干杯!:)
#3
2
Thanks to the help of Ed Smith and MiteshNinja, I have finally succeeded in finding a robust method that works not only with the Ipython console, but also with the Python console and the command line. Furthermore, it allows total control on the animation process. Code is self explanatory.
多亏了Ed Smith和MiteshNinja的帮助,我终于找到了一种健壮的方法,不仅可以使用Ipython控制台,还可以使用Python控制台和命令行。此外,它还允许对动画过程进行完全控制。代码是自说明的。
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from multiprocessing import Process
import time # optional for testing only
import matplotlib.animation as animation
# A. First we define some useful tools:
def wait_fig():
# Block the execution of the code until the figure is closed.
# This works even with multiprocessing.
if matplotlib.pyplot.isinteractive():
matplotlib.pyplot.ioff() # this is necessary in mutliprocessing
matplotlib.pyplot.show(block=True)
matplotlib.pyplot.ion() # restitute the interractive state
else:
matplotlib.pyplot.show(block=True)
return
def wait_anim(anim_flag, refresh_rate = 0.1):
#This will be used in synergy with the animation class in the example
#below, whenever the user want the figure to close automatically just
#after the animation has ended.
#Note: this function uses the controversial event_loop of Matplotlib, but
#I see no other way to obtain the desired result.
while anim_flag[0]: #next code extracted from plt.pause(...)
backend = plt.rcParams['backend']
if backend in plt._interactive_bk:
figManager = plt._pylab_helpers.Gcf.get_active()
if figManager is not None:
figManager.canvas.start_event_loop(refresh_rate)
def draw_fig(fig = None):
#Draw the artists of a figure immediately.
#Note: if you are using this function inside a loop, it should be less time
#consuming to set the interactive mode "on" using matplotlib.pyplot.ion()
#before the loop, event if restituting the previous state after the loop.
if matplotlib.pyplot.isinteractive():
if fig is None:
matplotlib.pyplot.draw()
else:
fig.canvas.draw()
else:
matplotlib.pyplot.ion()
if fig is None:
matplotlib.pyplot.draw()
else:
fig.canvas.draw()
matplotlib.pyplot.ioff() # restitute the interactive state
matplotlib.pyplot.show(block=False)
return
def pause_anim(t): #This is taken from plt.pause(...), but without unnecessary
#stuff. Note that the time module should be previously imported.
#Again, this use the controversial event_loop of Matplotlib.
backend = matplotlib.pyplot.rcParams['backend']
if backend in matplotlib.pyplot._interactive_bk:
figManager = matplotlib.pyplot._pylab_helpers.Gcf.get_active()
if figManager is not None:
figManager.canvas.start_event_loop(t)
return
else: time.sleep(t)
#--------------------------
# B. Now come the particular functions that will do the job.
def f(x, y):
return np.sin(x) + np.cos(y)
def plot_graph():
fig = plt.figure()
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
im = fig.gca().imshow(f(x, y))
draw_fig(fig)
n_frames = 50
#==============================================
#First method - direct animation: This use the start_event_loop, so is
#somewhat controversial according to the Matplotlib doc.
#Uncomment and put the "Second method" below into comments to test.
'''for i in range(n_frames): # n_frames iterations
x += np.pi / 15.
y += np.pi / 20.
im.set_array(f(x, y))
draw_fig(fig)
pause_anim(0.015) # plt.pause(0.015) can also be used, but is slower
wait_fig() # simply suppress this command if you want the figure to close
# automatically just after the animation has ended
'''
#================================================
#Second method: this uses the Matplotlib prefered animation class.
#Put the "first method" above in comments to test it.
def updatefig(i, fig, im, x, y, anim_flag, n_frames):
x = x + i * np.pi / 15.
y = y + i * np.pi / 20.
im.set_array(f(x, y))
if i == n_frames-1:
anim_flag[0] = False
anim_flag = [True]
animation.FuncAnimation(fig, updatefig, repeat = False, frames = n_frames,
interval=50, fargs = (fig, im, x, y, anim_flag, n_frames), blit=False)
#Unfortunately, blit=True seems to causes problems
wait_fig()
#wait_anim(anim_flag) #replace the previous command by this one if you want the
#figure to close automatically just after the animation
#has ended
#================================================
return
#--------------------------
# C. Using multiprocessing to obtain the desired effects. I believe this
# method also works with the "threading" module, but I haven't test that.
def main(): # it is important that ALL the code be typed inside
# this function, otherwise the program will do weird
# things with the Ipython or even the Python console.
# Outside of this condition, type nothing but import
# clauses and function/class definitions.
if __name__ != '__main__': return
p = Process(target=plot_graph)
p.start()
print('hello', flush = True) #just to have something printed here
p.join() # suppress this command if you want the animation be executed in
# parallel with the subsequent code
for i in range(3): # This allows to see if execution takes place after the
#process above, as should be the case because of p.join().
print('world', flush = True)
time.sleep(1)
main()