我们一直在尝试为水箱中不同时间间隔的参数绘制动画图表。它绘制时没有错误,但不随时间变化。
动画代码如下:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
T= [0*60,5*60,8*60,10*60,12*60] #minutes #Time correspondeing to I (inflow) below. The (*60) is to convert the time from hours to minutes
I= [0.08,0.06,0.05,0.00,0.00] #cubic meter/sec #Inflow values corresponding to the above T (time)
h0= 11.0 #meters #Initial water head value
dt= 10.0 #minutes #time step
k= 0.01*60 #sqm/min #Flow coefficient
R= 8.0 #meters #Sphere radius
Tmax= 12.0*60 #minutes #Total estimation time
n= int(Tmax/dt)+1
ts = np.linspace(0,10,101)
h_th=[h0]
t_th=[0]
Area_th= [np.pi*(R**2 - (abs(R - h0))**2)]
Q_th= [k*np.sqrt(h0)]
inflow_th=[I[0]*60]
for i in range (1,n): #looping through the steps
t_th.append(i*dt)
h_th.append(h_th[i-1]+(dt*(inflow_th[i-1]-Q_th[i-1]))/(Area_th[i-1]))
Area_th.append(np.pi*(R**2 - (abs(R - h_th[i]))**2))
Q_th.append(k*(np.sqrt(h_th[i])))
inflow_th.append((np.interp(i*dt,T,I))*60)
fig=plt.figure(1,figsize=(12,5))
i=0
ax1=plt.subplot(4,1,1)
pl1,=plt.plot(t_th[0:i+1],h_th[0:i+1],'r-',linewidth=3,label='water level')
plt.ylabel('Head Level (m)')
plt.legend(loc='best')
ax1.set_ylim(-1,1)
ax2=plt.subplot(4,1,2)
pl2,=plt.plot(t_th[0:i+1],inflow_th[0:i+1]*60,'b--',linewidth=3,label='inflow')
plt.ylabel('Inflow in (m3/min)')
plt.legend(loc='best')
ax3=plt.subplot(4,1,3)
ax3.set_xlim(ts[0], ts[100])
ax3.set_ylim(-5,5)
pl3,=plt.plot(t_th[0:i+1],Q_th[0:i+1],'g-',linewidth=3,label='outflow')
plt.ylabel('Outflow (m3/min)')
plt.xlabel('Time (min)')
plt.legend(loc='best')
ax4=plt.subplot(4,1,4)
ax4.set_xlim(ts[0], ts[100])
pl4,=plt.plot(ts[0:i+1],h_th[0:i+1],'y-',linewidth=3,label='overflow')
plt.ylabel('Overflow')
plt.xlabel('Time (min)')
plt.legend(loc='best')
#plt.legend(loc='best')
def myAnimation(i):
if i<1: return [] # No need to update frame 0, we already drawn it
pl1.set_data(t_th[0:i+1], h_th[0:i+1])
ax1.set_xlim(t_th[0],t_th[i])
pl2.set_data(t_th[0:i+1], inflow_th[0:i+1])
ax2.set_xlim(ts[0],ts[i])
ax2.set_ylim(min(inflow_th[0:i+1]), max(inflow_th[0:i+1]))
pl3.set_data(t_th[0:i+1], Q_th[0:i+1])
pl4.set_data(t_th[0:i+1], h_th[0:i+1])
ax4.set_ylim(min(h_th[0:i+1]), max(h_th[0:i+1]))
return [pl1, pl2, pl3, pl4]
theAnim = animation.FuncAnimation(fig, myAnimation, frames=100, interval=100, repeat=False)
plt.show()
时间间隔甚至暂停间隔都已重新定义。除了动画之外,一切正常。
最佳答案
最小示例
因此,例如,这里是一个最小的可重现示例,其外观尽可能与您的代码相同。
import numpy as np
import matplotlib.pyplot as plt
ts = np.linspace(0,10,101)
h=np.sin(ts)
inflow=np.cos(ts)
Q=np.tan(ts)
h_th=np.exp(ts)
plt.figure(1,figsize=(12,5))
plt.ion()
plt.show()
for i in range(100):
plt.clf()
plt.subplot(4,1,1)
plt.plot(ts[0:i+1],h[0:i+1],'r-',linewidth=3,label='water level')
plt.ylabel('Head Level (m)')
plt.legend(loc='best')
plt.subplot(4,1,2)
plt.plot(ts[0:i+1],inflow[0:i+1]*60,'b--',linewidth=3,label='inflow')
plt.ylabel('Inflow in (m3/min)')
plt.legend(loc='best')
plt.subplot(4,1,3)
plt.plot(ts[0:i+1],Q[0:i+1],'g-',linewidth=3,label='outflow')
plt.ylabel('Outflow (m3/min)')
plt.xlabel('Time (min)')
plt.legend(loc='best')
plt.subplot(4,1,4)
plt.plot(ts[0:i+1],h_th[0:i+1],'y-',linewidth=3,label='overflow')
plt.ylabel('Overflow')
plt.xlabel('Time (min)')
plt.legend(loc='best')
#plt.legend(loc='best') # I don't think you need it twice
plt.pause(0.1) # Reduced to 0.1, because I am not patient.
# plt.show() #Not really needed, but works also with it
请注意,它可以在我的计算机上运行。与您的代码的唯一区别(除了注释的轻微更改外,但这对动画工作或不工作没有任何改变;当然,创建一个最小的示例,但我假设您的完整代码包含计算 Q
所需的内容) , inflow
,...,并且不知何故,您迭代 i
,否则您不会期望任何动画。很难确定,因为即使您的完整代码也不包含此类内容,只有静态图,和一些评论部分尝试动画它,与我已经提到的相同问题,例如,没有定义 i
),与您的代码的唯一其他区别,所以,是我取消注释了初始 plt.ion()
和plt.show()
。但我认为,既然您输入了该代码(并对其进行了注释),您就已经尝试过了。
但是,我的最小示例确实生成了动画。
更新数据
现在,这还不是正确的方法。 因为在这里,您在每次迭代时都会重建整个图。包括图例、标签等等,太重了。
你应该做的就是只绘制一次。然后只更新数据。
import numpy as np
import matplotlib.pyplot as plt
ts = np.linspace(0,10,101)
h=np.sin(ts)
inflow=np.cos(ts)
Q=np.tan(ts)
h_th=np.exp(ts)
plt.figure(1,figsize=(12,5))
plt.ion()
plt.show()
i=0
plt.clf()
ax1=plt.subplot(4,1,1)
pl1,=plt.plot(ts[0:i+1],h[0:i+1],'r-',linewidth=3,label='water level')
plt.ylabel('Head Level (m)')
plt.legend(loc='best')
ax1.set_ylim(-1,1)
ax2=plt.subplot(4,1,2)
pl2,=plt.plot(ts[0:i+1],inflow[0:i+1]*60,'b--',linewidth=3,label='inflow')
plt.ylabel('Inflow in (m3/min)')
plt.legend(loc='best')
ax3=plt.subplot(4,1,3)
ax3.set_xlim(ts[0], ts[100])
ax3.set_ylim(-5,5)
pl3,=plt.plot(ts[0:i+1],Q[0:i+1],'g-',linewidth=3,label='outflow')
plt.ylabel('Outflow (m3/min)')
plt.xlabel('Time (min)')
plt.legend(loc='best')
ax4=plt.subplot(4,1,4)
ax4.set_xlim(ts[0], ts[100])
pl4,=plt.plot(ts[0:i+1],h_th[0:i+1],'y-',linewidth=3,label='overflow')
plt.ylabel('Overflow')
plt.xlabel('Time (min)')
plt.legend(loc='best')
#plt.legend(loc='best')
for i in range(100):
pl1.set_data(ts[0:i+1], h[0:i+1])
ax1.set_xlim(ts[0],ts[i])
pl2.set_data(ts[0:i+1], inflow[0:i+1])
ax2.set_xlim(ts[0],ts[i])
ax2.set_ylim(min(inflow[0:i+1]), max(inflow[0:i+1]))
pl3.set_data(ts[0:i+1], Q[0:i+1])
pl4.set_data(ts[0:i+1], h_th[0:i+1])
ax4.set_ylim(min(h_th[0:i+1]), max(h_th[0:i+1]))
plt.pause(0.1)
x 和 y 限制具有不同的样式(固定或非固定)。
与之前相比的变化是,我只绘制了一次。并在变量中保留两件事:我需要调用的轴( ax1, ax2, ax3, ax4
) set_xlim
和set_ylim
。而“艺术家”即plot
的返回。我需要更新数据。
因此无需每次都重新创建布局、图例、轴、样式、子图……。只是为了更新数据。
动画
这也是重新发明轮子。不过我有时也会这样做。例如,当我想将其集成到另一个“主循环”(用于 GUI 或其他东西)中时。
有一个专门的动画模块。这和我做的一模一样。但更有效(仅更新发生变化的像素,与主循环交互而无需显式暂停,等等)。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
ts = np.linspace(0,10,101)
h=np.sin(ts)
inflow=np.cos(ts)
Q=np.tan(ts)
h_th=np.exp(ts)
fig=plt.figure(1,figsize=(12,5))
i=0
ax1=plt.subplot(4,1,1)
pl1,=plt.plot(ts[0:i+1],h[0:i+1],'r-',linewidth=3,label='water level')
plt.ylabel('Head Level (m)')
plt.legend(loc='best')
ax1.set_ylim(-1,1)
ax2=plt.subplot(4,1,2)
pl2,=plt.plot(ts[0:i+1],inflow[0:i+1]*60,'b--',linewidth=3,label='inflow')
plt.ylabel('Inflow in (m3/min)')
plt.legend(loc='best')
ax3=plt.subplot(4,1,3)
ax3.set_xlim(ts[0], ts[100])
ax3.set_ylim(-5,5)
pl3,=plt.plot(ts[0:i+1],Q[0:i+1],'g-',linewidth=3,label='outflow')
plt.ylabel('Outflow (m3/min)')
plt.xlabel('Time (min)')
plt.legend(loc='best')
ax4=plt.subplot(4,1,4)
ax4.set_xlim(ts[0], ts[100])
pl4,=plt.plot(ts[0:i+1],h_th[0:i+1],'y-',linewidth=3,label='overflow')
plt.ylabel('Overflow')
plt.xlabel('Time (min)')
plt.legend(loc='best')
#plt.legend(loc='best')
def myAnimation(i):
if i<1: return [] # No need to update frame 0, we already drawn it
pl1.set_data(ts[0:i+1], h[0:i+1])
ax1.set_xlim(ts[0],ts[i])
pl2.set_data(ts[0:i+1], inflow[0:i+1])
ax2.set_xlim(ts[0],ts[i])
ax2.set_ylim(min(inflow[0:i+1]), max(inflow[0:i+1]))
pl3.set_data(ts[0:i+1], Q[0:i+1])
pl4.set_data(ts[0:i+1], h_th[0:i+1])
ax4.set_ylim(min(h_th[0:i+1]), max(h_th[0:i+1]))
return [pl1, pl2, pl3, pl4]
theAnim = animation.FuncAnimation(fig, myAnimation, frames=100, interval=100, repeat=False)
plt.show()
这里的主要变化是我导入了 animation
,将数字保存在变量 fig
中,并创建一个回调来更新数据(循环是 animation
的工作)。
请注意,该回调需要返回“艺术家”已更改内容的数组。在这里,所有的,即[pl1, pl2, pl3, pl4]
.
另请注意,即使我从不使用该变量(至少目前如此),我也需要保存 FuncAnimation
的结果调用变量theAnim
。因为如果我不这样做,垃圾收集器会在一段时间后删除该返回值。这种破坏也意味着动画本身的破坏。
创建 gif(或 mp4,...)
你还没有问过这个问题。但是代码中失败的尝试和注释表明,一旦动画正常工作,您希望能够将结果保存在电影中(或 gif,我将在此处执行此操作,以便能够在此处包含结果)
与 animation
模块,这非常简单。
只需添加到代码的最后
theAnim.save("anim.gif")
(如果同时保留 .save
和 plt.show
,则只有在关闭绘图窗口后动画才会保存到文件中。如果注释 plt.show()
行,则不会打开任何窗口,但创建包含动画的文件)
关于python - python 中的 Animate 显示静态图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74886493/