从下面显示的工作Matplotlib动画代码开始,我的目标是在PyQT4 GUI中嵌入这个动画(这只是一个在屏幕上移动的圆圈)。
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import animation
fig,ax = plt.subplots()
ax.set_aspect('equal','box')
circle = Circle((0,0), 1.0)
ax.add_artist(circle)
ax.set_xlim([0,10])
ax.set_ylim([-2,2])
def animate(i):
circle.center=(i,0)
return circle,
anim = animation.FuncAnimation(fig,animate,frames=10,interval=100,repeat=False,blit=True)
plt.show()
我可以使用以下代码完成此操作,但有一个障碍:我无法工作。
import sys
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.patches import Circle
from matplotlib import animation
class Window(QtGui.QDialog): #or QtGui.QWidget ???
def __init__(self):
super(Window, self).__init__()
self.fig = Figure(figsize=(5,4),dpi=100)
self.canvas = FigureCanvas(self.fig)
self.ax = self.fig.add_subplot(111) # create an axis
self.ax.hold(False) # discards the old graph
self.ax.set_aspect('equal','box')
self.circle = Circle((0,0), 1.0)
self.ax.add_artist(self.circle)
self.ax.set_xlim([0,10])
self.ax.set_ylim([-2,2])
self.button = QtGui.QPushButton('Animate')
self.button.clicked.connect(self.animate)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def animate(self):
self.anim = animation.FuncAnimation(self.fig,self.animate_loop,frames=10,interval=100,repeat=False,blit=False)
self.canvas.draw()
def animate_loop(self,i):
self.circle.center=(i,0)
return self.circle,
def main():
app = QtGui.QApplication(sys.argv)
ex = Window()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
当我设置blit=True
时,按下Animate按钮后,我收到以下错误:
的 a.figure.canvas.restore_region(bg_cache [A])
KeyError:位于0x00000000095F1D30的matplotlib.axes._subplots.AxesSubplot对象
在搜索此错误时,我发现很多关于blits如何在Mac上不起作用的帖子,但我使用的是Windows 7.我尝试用self.canvas.draw()
替换self.canvas.update()
,但这不起作用。
答案 0 :(得分:4)
在查看动画模块的源代码之后,我意识到Animation类中存在错误(字典bg_cache为空,当第一次访问它时打开blitting)。
这是在matplotlib的git版本中修复的;但是,在最新的稳定版本1.5.1中,该错误仍然存在。您可以修复matplotlib代码本身的错误,也可以创建FuncAnimation的子类。我选择了这种方式,因为它在更新matplotlib之后仍然可以工作。
from matplotlib import animation
class MyFuncAnimation(animation.FuncAnimation):
"""
Unfortunately, it seems that the _blit_clear method of the Animation
class contains an error in several matplotlib verions
That's why, I fork it here and insert the latest git version of
the function.
"""
def _blit_clear(self, artists, bg_cache):
# Get a list of the axes that need clearing from the artists that
# have been drawn. Grab the appropriate saved background from the
# cache and restore.
axes = set(a.axes for a in artists)
for a in axes:
if a in bg_cache: # this is the previously missing line
a.figure.canvas.restore_region(bg_cache[a])
然后,简单地使用MyFuncAnimation而不是animation.FuncAnimation。
我花了一些时间来弄明白,但我希望它可以帮助任何人。
答案 1 :(得分:1)
一段时间后,我设法通过直接使用底层函数重新创建动画,而不是使用动画包装器:
import sys
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.patches import Circle
from matplotlib import animation
from time import sleep
class Window(QtGui.QDialog): #or QtGui.QWidget ???
def __init__(self):
super(Window, self).__init__()
self.fig = Figure(figsize=(5, 4), dpi=100)
self.canvas = FigureCanvas(self.fig)
self.ax = self.fig.add_subplot(111) # create an axis
self.ax.hold(False) # discards the old graph
self.ax.set_aspect('equal', 'box')
self.circle = Circle((0,0), 1.0, animated=True)
self.ax.add_artist(self.circle)
self.ax.set_xlim([0, 10])
self.ax.set_ylim([-2, 2])
self.button = QtGui.QPushButton('Animate')
self.button.clicked.connect(self.animate)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
self.canvas.draw()
self.ax_background = self.canvas.copy_from_bbox(self.ax.bbox)
def animate(self):
self.animate_loop(0)
def animate_loop(self,i):
for i in range(10):
self.canvas.restore_region(self.ax_background)
self.circle.center=(i,0)
self.ax.draw_artist(self.circle)
self.canvas.blit(self.ax.bbox)
self.canvas.flush_events()
sleep(0.1)
def main():
app = QtGui.QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
也许这会对你有用。