Matplotlib:动画¶
日期 | 2012-05-13(上次修改),2006-01-22(创建) |
---|
注意: 此食谱条目中的一些 matplotlib 代码可能已过时或已弃用。例如,下面提到的 anim.py 文件不再存在于 matplotlib 中。matplotlib 中动画的示例位于 http://matplotlib.sourceforge.net/examples/animation/index.html,主要动画 API 文档位于 http://matplotlib.sourceforge.net/api/animation_api.html.
matplotlib 支持动画绘图,并提供了一些演示。在考虑是否使用 matplotlib 进行动画时,一个重要的问题是您需要什么样的速度。matplotlib 不是最快的绘图库,对于某些动画应用程序来说可能太慢。尽管如此,它对于大多数(如果不是全部)来说足够快,本教程旨在向您展示如何使其对您来说足够快。特别是“动画选定绘图元素”部分向您展示了如何从 matplotlib 动画中获得最高速度。
性能¶
matplotlib 支持 5 种不同的图形用户界面(GTK、WX、Qt、Tkinter、FLTK),对于其中一些 GUI,有各种方法可以绘制到画布上。例如,对于 GTK,您可以使用本机 GDK 绘制、antigrain 或 cairo。GUI 工具包与某些绘制方法相结合构成一个 后端。例如,使用 antigrain 绘制工具包绘制到 GTK 画布被称为 GTKAgg 后端。这很重要,因为不同的后端具有不同的性能特征,并且差异可能很大。
在考虑性能时,典型的衡量指标是每秒帧数。电视是每秒 30 帧,对于许多应用程序来说,如果你能获得每秒 10 帧或更多帧,动画就足够流畅,以至于“看起来不错”。显示器通常以每秒 75-80 帧的速度刷新,因此这是性能的上限。任何更快的速度可能都是浪费 CPU 周期。
以下是动画脚本 anim.py 的一些数字,该脚本只是在各种后端上更新正弦波,在 3GHz Pentium IV 的 Linux 上运行。要分析不同后端下的脚本,可以使用下面描述的“GUI 中立”动画技术,然后使用标志运行它,例如
> python anim.py -dWX
> python anim.py -dGTKAgg
以下是结果。请注意,这些结果应该谨慎解读,因为某些 GUI 可能会在单独的线程中调用绘图操作并在完成之前返回,或者在绘图操作排队时丢弃绘图操作。最重要的评估是定性的。
Backend Frames/second
GTK 43
GTKAgg 36
TkAgg 20
WX 11
WXAgg 27
pylab 中的 GUI 中立动画¶
pylab 接口支持不依赖于特定 GUI 工具包的动画。这并不推荐用于生产环境,但通常是制作快速简便的临时动画的好方法。导入 pylab 后,需要使用 [http://matplotlib.sf.net/matplotlib.pylab.html#-ion ion] 命令打开交互模式。然后,您可以随时使用 [http://matplotlib.sf.net/matplotlib.pylab.html#-draw draw] 命令强制绘制。在交互模式下,在每个 pylab 命令之后都会发出新的绘制命令,您还可以暂时关闭此行为,以用于您不希望更新的一组绘图命令,使用 [http://matplotlib.sf.net/matplotlib.pylab.html#-ioff ioff] 命令。这在 交互式 页面中有更详细的描述。
以下是用于生成上面表格中跨后端的分析数字的 anim.py 脚本
from pylab import *
import time
ion()
tstart = time.time() # for profiling
x = arange(0,2*pi,0.01) # x-array
line, = plot(x,sin(x))
for i in arange(1,200):
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
print 'FPS:' , 200/(time.time()-tstart)
请注意使用 [http://matplotlib.sf.net/matplotlib.pylab.html#-plot plot]
line, = plot(x,sin(x))
调用创建线条的技术,然后使用 set_ydata 方法设置其数据并调用 draw
line.set_ydata(sin(x+i/10.0)) # update the data
draw() # redraw the canvas
这比清除坐标轴和/或为每个绘图命令创建新对象要快得多。
要为 pcolor 图表制作动画,请使用
p = pcolor(XI,YI,C[0])
for i in range(1,len(C)):
p.set_array(C[i,0:-1,0:-1].ravel())
p.autoscale()
draw()
这假设 C 是一个 3D 数组,其中第一个维度是时间,并且 XI、YI 和 C[i,:,:] 具有相同的形状。如果 C[i,:,:] 比一行一列小,只需使用 C.ravel()。
使用 GUI 定时器或空闲处理程序¶
如果您正在编写生产代码或任何半严肃的代码,建议您使用特定于您的工具包的 GUI 事件处理来进行动画,因为这将使您对动画的控制比 matplotlib 通过 GUI 中立的 pylab 接口提供的控制更多。如何做到这一点取决于您的工具包,但在 matplotlib 示例目录中有一些针对多个后端的示例,例如,anim_tk.py 用于 Tkinter,dynamic_image_gtkagg.py 用于 GTKAgg 和 dynamic_image_wxagg.py 用于 WXAgg。
基本思想是创建您的图形和一个更新图形的回调函数。然后将该回调传递给 GUI 空闲处理程序或计时器。在 GTK 中的一个简单示例如下所示
def callback(*args):
line.set_ydata(get_next_plot())
canvas.draw() # or simply "draw" in pylab
gtk.idle_add(callback)
在 WX 或 WXAgg 中的一个简单示例如下所示
def callback(*args):
line.set_ydata(get_next_plot())
canvas.draw()
wx.WakeUpIdle() # ensure that the idle event keeps firing
wx.EVT_IDLE(wx.GetApp(), callback)
为选定的绘图元素制作动画¶
上面介绍的方法的一个限制是,每次调用 draw 时都会重新绘制所有图形元素,但我们只更新一个元素。通常,我们想要做的是绘制一个背景,并在其上动画一个或两个元素。从 matplotlib-0.87 开始,GTKAgg、!TkAgg、WXAgg 和 FLTKAgg 支持此处讨论的方法。
基本思想是设置要为其制作动画的 Artist 的“animated”属性(从 Figure 到 Axes 到 Line2D 到 Text 的所有图形元素都派生自基类 Artist)。然后,当调用标准画布绘制操作时,将绘制除动画元素之外的所有元素。然后,您可以使用该方法将矩形区域(例如轴边界框)复制到像素缓冲区中。在动画中,您使用 恢复背景,然后调用 将动画元素绘制到干净的背景上,并调用 将更新的轴矩形快速绘制到图形上。当我以产生 GTKAgg 上方 36 FPS 的相同环境运行下面的示例时,我使用下面的技术测量了 327 FPS。请参阅上面提到的关于性能数字的注意事项。总之,从数量上和质量上来说,它都要快得多。
import sys
import gtk, gobject
import pylab as p
import matplotlib.numerix as nx
import time
ax = p.subplot(111)
canvas = ax.figure.canvas
# for profiling
tstart = time.time()
# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = p.plot(x, nx.sin(x), animated=True)
# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)
def update_line(*args):
# restore the clean slate background
canvas.restore_region(background)
# update the data
line.set_ydata(nx.sin(x+update_line.cnt/10.0))
# just draw the animated artist
ax.draw_artist(line)
# just redraw the axes rectangle
canvas.blit(ax.bbox)
if update_line.cnt==50:
# print the timing info and quit
print 'FPS:' , update_line.cnt/(time.time()-tstart)
sys.exit()
update_line.cnt += 1
return True
update_line.cnt = 0
gobject.idle_add(update_line)
p.show()
示例:光标¶
matplotlib 0.83.2 引入了一个光标类,它可以利用这些快速绘制方法来实现无延迟光标。该类在构造函数中接受一个参数。对于支持新 API 的后端(GTKAgg),请设置
from matplotlib.widgets import Cursor
import pylab
fig = pylab.figure(figsize=(8,6))
ax = fig.add_axes([0.075, 0.25, 0.9, 0.725], axisbg='#FFFFCC')
x,y = 4*(pylab.rand(2,100)-.5)
ax.plot(x,y,'o')
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
# set useblit = True on gtkagg for enhanced performance
cursor = Cursor(ax, useblit=True, color='red', linewidth=2 )
pylab.show()
“快速绘制”动画方法¶
如上所述,只有 GTKAgg 支持以上方法来实现对选定演员的动画。以下是需要的
图形画布方法¶
* `` - 将 ax.bbox 中的区域复制到像素缓冲区,并以您选择的类型返回它。 bbox 是来自
transforms
模块
的 matplotlib BBox 实例。 `` 不被 matplotlib 前端使用,但它会存储它并将其传递回后端,在 `` 方法中。您可能希望不仅存储像素缓冲区,还要存储来自背景对象的画布的矩形区域。
* `` - 将上面复制的区域恢复到画布。
* `` - 将 bbox 区域内的像素缓冲区传输到画布。
对于 *Agg 后端,无需实现前两个,因为 Agg 会完成所有工作(定义它们)。因此,您只需要能够从选定矩形中 blit agg 缓冲区。对于使用字符串方法将 agg 像素缓冲区传输到各自画布的后端,在 agg 中定义一个方法可能会使这更容易。如果您正在处理此问题并需要帮助,请联系 matplotlib-devel 列表。
一旦所有/大多数后端都实现了这些方法,matplotlib 前端就可以完成管理背景/恢复/blit 操作的所有工作,用户级动画代码可以看起来像
line, = plot(something, animated=True)
draw()
def callback(*args):
line.set_ydata(somedata)
ax.draw_animated()
其余部分将自动发生。由于某些后端目前'''没有'''实现所需的方法,因此我将其提供给用户自行管理,但在轴绘制代码中不假设它们。
章节作者:AndrewStraw,Unknown[83],Unknown[84],Unknown[85],Unknown[86],KristjanOnu,WarrenWeckesser