Python 删除和重画Matplotlib动画

Python 删除和重画Matplotlib动画,python,matplotlib,pyside,Python,Matplotlib,Pyside,这个问题有几个部分,所以让我总结一下。我试图在PySide窗口中创建一个动画,然后根据新信息销毁并重新激活它。我创建了一个示例脚本来演示这一点,但它有点长,所以这里有一个概要: 使用PySide创建QtGui主窗口 在窗口中创建用于绘制动画的小部件 使用FuncAnimation设置小部件的动画 按下按钮时: 删除小部件和动画 使用不同的打印参数重新创建小部件 复活 在按下按钮之前,一切正常,我得到以下堆栈跟踪: Traceback (most recent call last): Fil

这个问题有几个部分,所以让我总结一下。我试图在PySide窗口中创建一个动画,然后根据新信息销毁并重新激活它。我创建了一个示例脚本来演示这一点,但它有点长,所以这里有一个概要:

  • 使用PySide创建QtGui主窗口
  • 在窗口中创建用于绘制动画的小部件
  • 使用FuncAnimation设置小部件的动画
  • 按下按钮时:
  • 删除小部件和动画
  • 使用不同的打印参数重新创建小部件
  • 复活
  • 在按下按钮之前,一切正常,我得到以下堆栈跟踪:

    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. 版本信息:
    matplotlib-1.2.1
    PySide-1.1.2
    Python-2.7.4

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

    这与
    matplotlib
    qtbackend中的一个错误有关,该错误会以错误的顺序将内容删除(请参阅)。添加

    self.ui.mplWidget.close_event()
    
    ChangeStuff
    中,会处理您遇到的异常,但不会显示新创建的画布

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

    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
    qtbackend中的一个错误有关,该错误会以错误的顺序破坏内容(请参阅)。添加

    self.ui.mplWidget.close_event()
    
    ChangeStuff
    中,会处理您遇到的异常,但不会显示新创建的画布

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

    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_())
    

    尝试了#1,获得了一个新的堆栈跟踪,但随后修复了它。但是,它没有解决原始堆栈跟踪问题。尝试#2.尝试#1,获得了新的堆栈跟踪,但随后修复了它。但是,它没有解决原始堆栈跟踪问题。尝试#2.您正在混合
    PyQt4
    PySides
    。确保在
    matplotlibrc
    文件中有行
    backend.qt4:PySides
    。与其删除小部件和动画,不如刷新数据?稍后我会更仔细地看一下。确保
    matplotlib.\uuuuuu version\uuuuuuu
    返回您期望的内容。谢谢!pyplot导入在任何情况下都是未使用的,但现在只要我在matplotlib导入pyplot为p之前动态设置
    matplotlib.rcParams['backend.qt4']='PySide'
    ,它就可以工作,我想这是有意义的。只是因为我有妄想症,我还将它添加到了
    matplotlibrc
    文件中。@badc0re:在我的实际程序中,我希望根据用户的输入,在特定条件下显示或销毁动画。最直接的方法似乎是删除小部件,尽管我可以看到可能只是用
    setVisible(False)
    隐藏绘图,而不是删除它。最后我可能会这么做。你把
    PyQt4
    PySides
    混在一起了。确保在
    matplotlibrc
    文件中有行
    backend.qt4:PySides
    。与其删除小部件和动画,不如刷新数据?稍后我会更仔细地看一下。确保
    matplotlib.\uuuuuu version\uuuuuuu
    返回您期望的内容。谢谢!pyplot导入在任何情况下都是未使用的,但现在只要我在matplotlib导入pyplot为p之前动态设置
    matplotlib.rcParams['backend.qt4']='PySide'
    ,它就可以工作,我想这是有意义的。只是因为我有妄想症,我还将它添加到了
    matplotlibrc
    文件中。@badc0re:在我的实际程序中,我希望根据用户的输入,在特定条件下显示或销毁动画。最直接的方法似乎是删除小部件,尽管我可以看到可能只是用
    setVisible(False)
    隐藏绘图,而不是删除它。我最终可能会这么做。