删除和重绘 Matplotlib 动画 [英] Deleting and Redrawing Matplotlib Animation

查看:136
本文介绍了删除和重绘 Matplotlib 动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题有几个部分,让我总结一下.我试图能够在PySide窗口中创建动画,然后在以后根据新信息销毁它并对其进行动画处理.我已经创建了一个示例脚本来演示这一点,但它有点长,所以这里有一个大纲:

There are a few parts to this problem, so let me summarize. I'm trying to be able to create an animation in a PySide window, and then later destroy and reanimate it based on new information. I've created a sample script to demonstrate this, but it's a bit long, so here's an outline:

  1. 使用 PySide 创建 QtGui 主窗口
  2. 在窗口中创建一个小部件以绘制动画
  3. 使用FuncAnimation对小部件进行动画处理
  4. 在按钮上按:
  1. 删除小部件和动画
  2. 使用不同的绘图参数重新创建小部件
  3. 复活

一切正常,直到按下按钮,我得到以下堆栈跟踪:

Everything works until the button press, and I get the following stack trace:


Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_qt4.py", line 366, in idle_draw
    self.draw()  
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_qt4agg.py", line 149, in draw
    self.update()
RuntimeError: Internal C++ object (MplWidget) already deleted.

我怎样才能摆脱当前的动画,以便我可以重新绘制一个单独的动画?这是代码.

How can I get rid of the current animation so that I can redraw a separate one? Here's the code.

from matplotlib import pyplot as p
from mpl_toolkits.mplot3d import Axes3D    # @UnusedImport
from PySide import QtGui, QtCore

import matplotlib
import matplotlib.animation as animation
import sys
# specify the use of PySide
matplotlib.rcParams['backend.qt4'] = "PySide"

# import the figure canvas for interfacing with the backend
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
                                                            as FigureCanvas
from matplotlib.figure import Figure

import numpy as np
from math import pi, cos, sin

class Ui_MainWindow(object):
    def setupUi(self, MainWindow, direction, maxRadius):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 500)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum,
                                       QtGui.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
                            self.pushButton.sizePolicy().hasHeightForWidth())
        self.pushButton.setSizePolicy(sizePolicy)
        self.pushButton.setMaximumSize(QtCore.QSize(150, 16777215))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("Change Direction")
        self.horizontalLayout.addWidget(self.pushButton)
        self.frame = QtGui.QFrame(self.centralwidget)
        self.frame.setFrameShape(QtGui.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtGui.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.gridLayout = QtGui.QGridLayout(self.frame)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout.addWidget(self.frame)
        # ------
        self.mplWidget = MplWidget(self.frame, direction, maxRadius)
        # ------
        MainWindow.setCentralWidget(self.centralwidget)


        QtCore.QMetaObject.connectSlotsByName(MainWindow)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.direction = 'up'
        self.maxRadius = 0.3
        self.ui.setupUi(self, self.direction, self.maxRadius)
        self.ui.pushButton.clicked.connect(self.changeStuff)
        self.animation = self.ui.mplWidget.animate()

    def changeStuff(self):
        del self.animation
        self.ui.mplWidget.deleteLater()
        dirs = {'up' :'down', 'down':'up'}
        rads = {0.3:1, 1:0.3}
        self.direction = dirs[self.direction]
        self.maxRadius = rads[self.maxRadius]
        self.ui.mplWidget = MplWidget(self.ui.frame, self.direction,
                                      self.maxRadius)
        self.animation = self.ui.mplWidget.animate()


class MplWidget(FigureCanvas):
    def __init__(self, parent=None, direction='up', maxRadius=0.3):
        self.figure = Figure()
        super(MplWidget, self).__init__(self.figure)
        self.setParent(parent)
        self.axes = self.figure.add_subplot(111, projection='3d')
        self.axes.set_xlabel("x label")
        self.axes.set_ylabel("y label")
        self.axes.set_zlabel("z label")
        self.axes.set_xlim3d([-1, 1])
        self.axes.set_ylim3d([-1, 1])
        self.axes.set_zlim3d([-1, 1])
        self.axes.set_aspect('equal')
        if   direction == 'up':
            self.c = 1
        elif direction == 'down':
            self.c = -1
        else:
            self.c = 1
        self.maxRadius = maxRadius
        self.frames = 50
        self.plot_handle = self.func_plot(self.frames)

    def func_plot(self, z):
        z /= float(self.frames) * self.c
        theta = np.arange(0, 2 * pi + pi / 50, pi / 50)
        xdata = self.maxRadius * z * np.array([cos(q) for q in theta])
        ydata = self.maxRadius * z * np.array([sin(q) for q in theta])
        zdata = z * np.ones(np.shape(xdata))
        if not hasattr(self, 'plot_handle'):
            plot_handle = self.axes.plot(xdata, ydata, zdata)[0]
        else:
            plot_handle = self.plot_handle
        plot_handle.set_data(xdata, ydata)
        plot_handle.set_3d_properties(zdata)
        return plot_handle

    def animate(self):
        return animation.FuncAnimation(
                    fig=self.figure, func=self.func_plot, frames=self.frames,
                    interval=1000.0 / self.frames, blit=False)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

版本信息:
matplotlib - 1.2.1
PySide-1.1.2
Python - 2.7.4

Version information:
matplotlib - 1.2.1
PySide - 1.1.2
Python - 2.7.4

我按照 matplotlib 安装说明从 git 进行源代码构建.我现在安装了1.4.x版.但是,原始问题仍然存在.

I followed the matplotlib Installation Instructions to do a source build from git. I now have version 1.4.x installed. However, the original problem persists.

推荐答案

这与 matplotlib qtbackend中的错误有关,该错误以错误的顺序将其分解(请参阅

This is related to a bug in matplotlib qtbackend that tears things down in the wrong order (see here). Adding

self.ui.mplWidget.close_event()

ChangeStuff 中的

会处理您遇到的异常,但是不会显示新创建的画布.

in ChangeStuff takes care of the exception you have, but the newly created canvas isn't shown.

另一方面,我不明白为什么为什么不会显示任何画布,因为窗口小部件从未添加到布局中.如果您稍微调整一下设置并明确地向 gridLayout 添加/删除小部件,它会执行您想要的操作:

On the other hand, I don't understand why any canvas is shown at all as the widget is never added to a layout. If you tweak the set up a bit and explicitly add/remove the widget to the gridLayout it does what you want:

from mpl_toolkits.mplot3d import Axes3D    # @UnusedImport
from PySide import QtGui, QtCore

import matplotlib
import matplotlib.animation as animation
import sys
# specify the use of PySide
matplotlib.rcParams['backend.qt4'] = "PySide"

# import the figure canvas for interfacing with the backend
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg \
                                                            as FigureCanvas
from matplotlib.figure import Figure

import numpy as np
from math import pi, cos, sin


class Ui_MainWindow(object):
    def setupUi(self, MainWindow, direction, maxRadius):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 500)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum,
                                       QtGui.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
                            self.pushButton.sizePolicy().hasHeightForWidth())
        self.pushButton.setSizePolicy(sizePolicy)
        self.pushButton.setMaximumSize(QtCore.QSize(150, 16777215))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.setText("Change Direction")
        self.horizontalLayout.addWidget(self.pushButton)
        self.frame = QtGui.QFrame(self.centralwidget)
        self.frame.setFrameShape(QtGui.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtGui.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.gridLayout = QtGui.QGridLayout(self.frame)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout.addWidget(self.frame)
        # ------
        self.mplWidget = MplWidget(None, direction, maxRadius)
        self.gridLayout.addWidget(self.mplWidget)
        # ------
        MainWindow.setCentralWidget(self.centralwidget)

        QtCore.QMetaObject.connectSlotsByName(MainWindow)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.direction = 'up'
        self.maxRadius = 0.3
        self.ui.setupUi(self, self.direction, self.maxRadius)
        self.ui.pushButton.clicked.connect(self.changeStuff)
        self.animation = self.ui.mplWidget.animate()

    def changeStuff(self):
        self.ui.mplWidget.close_event()  # mpl clean up
        self.ui.mplWidget.deleteLater()  # QT cleanup
        self.ui.gridLayout.removeWidget(self.ui.mplWidget)

        dirs = {'up': 'down', 'down': 'up'}
        rads = {0.3: 1, 1: 0.3}
        self.direction = dirs[self.direction]
        self.maxRadius = rads[self.maxRadius]
        self.ui.mplWidget = MplWidget(self.ui.frame, self.direction,
                                      self.maxRadius)
        self.ui.gridLayout.addWidget(self.ui.mplWidget)
        self.animation = self.ui.mplWidget.animate()
        print self.ui.frame.children()
        print 'finished change stuff'


class MplWidget(FigureCanvas):
    def __init__(self, parent=None, direction='up', maxRadius=0.3):
        self.figure = Figure()
        super(MplWidget, self).__init__(self.figure)
        self.setParent(parent)
        self.axes = self.figure.add_subplot(111, projection='3d')
        self.axes.set_xlabel("x label")
        self.axes.set_ylabel("y label")
        self.axes.set_zlabel("z label")
        self.axes.set_xlim3d([-1, 1])
        self.axes.set_ylim3d([-1, 1])
        self.axes.set_zlim3d([-1, 1])
        self.axes.set_aspect('equal')
        if direction == 'up':
            self.c = 1
        elif direction == 'down':
            self.c = -1
        else:
            self.c = 1
        self.maxRadius = maxRadius
        self.frames = 50
        self.plot_handle = self.func_plot(self.frames)

    def func_plot(self, z):
        z /= float(self.frames) * self.c
        theta = np.arange(0, 2 * pi + pi / 50, pi / 50)
        xdata = self.maxRadius * z * np.array([cos(q) for q in theta])
        ydata = self.maxRadius * z * np.array([sin(q) for q in theta])
        zdata = z * np.ones(np.shape(xdata))
        if not hasattr(self, 'plot_handle'):
            plot_handle = self.axes.plot(xdata, ydata, zdata)[0]
        else:
            plot_handle = self.plot_handle
        plot_handle.set_data(xdata, ydata)
        plot_handle.set_3d_properties(zdata)
        return plot_handle

    def animate(self):
        return animation.FuncAnimation(
                    fig=self.figure, func=self.func_plot, frames=self.frames,
                    interval=1000.0 / self.frames, blit=False)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())

这篇关于删除和重绘 Matplotlib 动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆