Python 在自己的GUI中创建Matplotlib动画

Python 在自己的GUI中创建Matplotlib动画,python,animation,matplotlib,pyqt,pyqt4,Python,Animation,Matplotlib,Pyqt,Pyqt4,我正在用Python编写软件。我需要将Matplotlib时间动画嵌入到自制的GUI中。以下是关于它们的更多细节: 1.GUI GUI也是用Python编写的,使用PyQt4库。我的图形用户界面与你在网上找到的普通图形用户界面差别不大。我只是将QtGui.QMainWindow子类化,并添加一些按钮、布局 2.动画 Matplotlib动画基于animation.TimedAnimation类。以下是动画的代码: import numpy as np import matplotlib.pypl

我正在用Python编写软件。我需要将Matplotlib时间动画嵌入到自制的GUI中。以下是关于它们的更多细节:

1.GUI GUI也是用Python编写的,使用PyQt4库。我的图形用户界面与你在网上找到的普通图形用户界面差别不大。我只是将QtGui.QMainWindow子类化,并添加一些按钮、布局

2.动画 Matplotlib动画基于animation.TimedAnimation类。以下是动画的代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.animation as animation

        class CustomGraph(animation.TimedAnimation):

        def __init__(self):

            self.n = np.linspace(0, 1000, 1001)
            self.y = 1.5 + np.sin(self.n/20)
            #self.y = np.zeros(self.n.size)


            # The window
            self.fig = plt.figure()
            ax1 = self.fig.add_subplot(1, 2, 1)
            self.mngr = plt.get_current_fig_manager() 
            self.mngr.window.setGeometry(50,100,2000, 800)

            # ax1 settings
            ax1.set_xlabel('time')
            ax1.set_ylabel('raw data')
            self.line1 = Line2D([], [], color='blue')
            ax1.add_line(self.line1)
            ax1.set_xlim(0, 1000)
            ax1.set_ylim(0, 4)


            animation.TimedAnimation.__init__(self, self.fig, interval=20, blit=True)


        def _draw_frame(self, framedata):
            i = framedata
            print(i)


            self.line1.set_data(self.n[ 0 : i ], self.y[ 0 : i ])

            self._drawn_artists = [self.line1]

        def new_frame_seq(self):
            return iter(range(self.n.size))

        def _init_draw(self):
            lines = [self.line1]
            for l in lines:
                l.set_data([], [])

        def showMyAnimation(self):
            plt.show()


    ''' End Class '''


    if __name__== '__main__':
        print("Define myGraph")
        myGraph = CustomGraph()
        myGraph.showMyAnimation()
此代码生成一个简单的动画:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
import matplotlib.animation as animation

        class CustomGraph(animation.TimedAnimation):

        def __init__(self):

            self.n = np.linspace(0, 1000, 1001)
            self.y = 1.5 + np.sin(self.n/20)
            #self.y = np.zeros(self.n.size)


            # The window
            self.fig = plt.figure()
            ax1 = self.fig.add_subplot(1, 2, 1)
            self.mngr = plt.get_current_fig_manager() 
            self.mngr.window.setGeometry(50,100,2000, 800)

            # ax1 settings
            ax1.set_xlabel('time')
            ax1.set_ylabel('raw data')
            self.line1 = Line2D([], [], color='blue')
            ax1.add_line(self.line1)
            ax1.set_xlim(0, 1000)
            ax1.set_ylim(0, 4)


            animation.TimedAnimation.__init__(self, self.fig, interval=20, blit=True)


        def _draw_frame(self, framedata):
            i = framedata
            print(i)


            self.line1.set_data(self.n[ 0 : i ], self.y[ 0 : i ])

            self._drawn_artists = [self.line1]

        def new_frame_seq(self):
            return iter(range(self.n.size))

        def _init_draw(self):
            lines = [self.line1]
            for l in lines:
                l.set_data([], [])

        def showMyAnimation(self):
            plt.show()


    ''' End Class '''


    if __name__== '__main__':
        print("Define myGraph")
        myGraph = CustomGraph()
        myGraph.showMyAnimation()

动画本身运行良好。运行代码,动画会在一个小窗口中弹出,并开始运行。但是如何将动画嵌入到我自己自制的GUI中呢

3.在自制GUI中嵌入动画 我做了一些研究来找出答案。这里有一些我试过的东西。我已将以下代码添加到python文件中。请注意,此添加的代码实际上是一个额外的类定义:

from PyQt4 import QtGui
from PyQt4 import QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

class CustomFigCanvas(FigureCanvas):

    def __init__(self):

        self.myGraph = CustomGraph()
        FigureCanvas.__init__(self, self.myGraph.fig)
我在这里尝试的是将CustomGraph()对象(本质上是我的动画)嵌入到FigureCanvas

我在另一个python文件中编写了GUI(但仍在同一文件夹中)。通常我可以将小部件添加到GUI中。我相信类CustomFigCanvas(..)中的对象是通过继承实现的小部件。这就是我在GUI中尝试的内容:

    ..
    myFigCanvas = CustomFigCanvas()
    self.myLayout.addWidget(myFigCanvas)
    ..
它在某种程度上起作用。我的GUI中确实显示了一个图形。但这个数字是空的。动画不会运行:

甚至还有另一种奇怪的现象正在发生。我的GUI显示这个空图形,但我同时得到一个常规Matplotlib弹出窗口,其中包含我的动画图形。此外,此动画未运行


很明显,我在这里遗漏了一些东西。请帮我解决这个问题。非常感谢大家的帮助。

我想我找到了解决办法。所有的功劳都归于制作Python教程网站的哈里森先生。他帮了我一把

这就是我所做的。两大变化:

1.结构变化 我以前上过两门课:CustomGraph(TimedAnimation)CustomFigCanvas(FigureCanvas)。现在我只剩下一个了,但他同时继承了TimedAnimation和FigureCanvas:CustomFigCanvas(TimedAnimation,FigureCanvas)

2.在设置地物对象时所做的更改 这是我之前的数字:

self.fig = plt.figure()
“plt”来自import语句
“import matplotlib.pyplot as plt”
。当您想要将图形嵌入到自己的GUI中时,这种生成图形的方法显然会带来麻烦。 因此,有一种更好的方法:

self.fig = Figure(figsize=(5,5), dpi=100)
现在它工作了

以下是完整的代码:

import numpy as np
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas


    class CustomFigCanvas(FigureCanvas, TimedAnimation):

        def __init__(self):

            # The data
            self.n = np.linspace(0, 1000, 1001)
            self.y = 1.5 + np.sin(self.n/20)

            # The window
            self.fig = Figure(figsize=(5,5), dpi=100)
            ax1 = self.fig.add_subplot(111)

            # ax1 settings
            ax1.set_xlabel('time')
            ax1.set_ylabel('raw data')
            self.line1 = Line2D([], [], color='blue')
            ax1.add_line(self.line1)
            ax1.set_xlim(0, 1000)
            ax1.set_ylim(0, 4)

            FigureCanvas.__init__(self, self.fig)
            TimedAnimation.__init__(self, self.fig, interval = 20, blit = True)


        def _draw_frame(self, framedata):
            i = framedata
            print(i)


            self.line1.set_data(self.n[ 0 : i ], self.y[ 0 : i ])
            self._drawn_artists = [self.line1]

        def new_frame_seq(self):
            return iter(range(self.n.size))

        def _init_draw(self):
            lines = [self.line1]
            for l in lines:
                l.set_data([], [])


    ''' End Class '''
这是在matplotlib中制作动画的代码。现在,您可以轻松地将其嵌入到自己的Qt GUI中:

    ..
    myFigCanvas = CustomFigCanvas()
    self.myLayout.addWidget(myFigCanvas)
    ..
它似乎工作得很好。 谢谢你,哈里森先生


编辑:
几个月后我又回到这个问题上来。这是完整的代码。只需将其复制粘贴到一个新的
.py
文件中,然后运行它:

###################################################################
#                                                                 #
#                     PLOTTING A LIVE GRAPH                       #
#                  ----------------------------                   #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################


import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading



def setCustomSize(x, width, height):
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMinimumSize(QtCore.QSize(width, height))
    x.setMaximumSize(QtCore.QSize(width, height))

''''''

class CustomMainWindow(QtGui.QMainWindow):

    def __init__(self):

        super(CustomMainWindow, self).__init__()

        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")

        # Create FRAME_A
        self.FRAME_A = QtGui.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
        self.LAYOUT_A = QtGui.QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)

        # Place the zoom button
        self.zoomBtn = QtGui.QPushButton(text = 'zoom')
        setCustomSize(self.zoomBtn, 100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))

        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))

        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
        myDataLoop.start()

        self.show()

    ''''''


    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)

    ''''''

    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)



''' End Class '''


class CustomFigCanvas(FigureCanvas, TimedAnimation):

    def __init__(self):

        self.addedData = []
        print(matplotlib.__version__)

        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50

        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)


        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)


        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)

    def new_frame_seq(self):
        return iter(range(self.n.size))

    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])

    def addData(self, value):
        self.addedData.append(value)

    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()


    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass

    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])


        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]



''' End Class '''


# You need to setup a signal slot mechanism, to 
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
    data_signal = QtCore.pyqtSignal(float)

''' End Class '''



def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)

    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0

    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###




if __name__== '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()


    sys.exit(app.exec_())

''''''
###################################################################
#                                                                 #
#绘制活动图形#
#                  ----------------------------                   #
#将MATPLOTLIB动画嵌入到#
#自己的GUI#
#                                                                 #
###################################################################
导入系统
导入操作系统
从PyQt4导入QtGui
从PyQt4导入QtCore
导入功能工具
将numpy作为np导入
将随机导入为rd
导入matplotlib
matplotlib.use(“Qt4Agg”)
从matplotlib.figure导入图形
从matplotlib.animation导入时间信息
从matplotlib.lines导入Line2D
从matplotlib.backends.backend_qt4agg导入FigureCanvas qtagg as FigureCanvas
导入时间
导入线程
def setCustomSize(x、宽度、高度):
sizePolicy=QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x、 设置大小策略(大小策略)
x、 setMinimumSize(QtCore.QSize(宽度、高度))
x、 setMaximumSize(QtCore.QSize(宽度、高度))
''''''
类CustomMainWindow(QtGui.QMainWindow):
定义初始化(自):
超级(CustomMainWindow,self)。\uu初始化
#定义主窗口的几何图形
self.setGeometry(300300800400)
self.setWindowTitle(“我的第一个窗口”)
#创建一个框架
self.FRAME_A=QtGui.QFrame(self)
self.FRAME_A.setStyleSheet(“QWidget{background color:%s}”%QtGui.QColor(210210235255.name())
self.LAYOUT_A=QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
#放置缩放按钮
self.zoomBtn=QtGui.QPushButton(文本='zoom')
setCustomSize(self.zoomBtn,100,50)
self.zoomBtn.clicked.connect(self.zoomBtn操作)
self.LAYOUT\u A.addWidget(self.zoomBtn,*(0,0))
#放置matplotlib图形
self.myFig=CustomFigCanvas()
self.LAYOUT\u A.addWidget(self.myFig,*(0,1))
#将callbackfunc添加到。。
myDataLoop=threading.Thread(name='myDataLoop',target=dataSendLoop,daemon=True,args=(self.addData\u callbackFunc,)
myDataLoop.start()
self.show()
''''''
def缩放动作(自身):
打印(“放大”)
self.myFig.zoomIn(5)
''''''
def ADDDDATA_callbackFunc(自身,值):
#打印(“添加数据:+str(值))
self.myFig.addData(值)
“最终类”
类CustomFigCanvas(图类、时间信息):
定义i
###################################################################
#                                                                 #
#                     PLOTTING A LIVE GRAPH                       #
#                  ----------------------------                   #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################


import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import numpy as np
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
import matplotlib
matplotlib.use("Qt5Agg")


def setCustomSize(x, width, height):
    sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMaximumSize(QtCore.QSize(width, height))


class CustomMainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(CustomMainWindow, self).__init__()

        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")

        # Create FRAME_A
        self.FRAME_A = QtWidgets.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210, 210, 235, 255).name())
        self.LAYOUT_A = QtWidgets.QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)

        # Place the zoom button
        self.zoomBtn = QtWidgets.QPushButton(text='zoom')
        setCustomSize(self.zoomBtn, 100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0, 0))

        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0, 1))

        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name='myDataLoop', target=dataSendLoop, daemon=True, args=(self.addData_callbackFunc,))
        myDataLoop.start()

        self.show()

    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)

    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)


class CustomFigCanvas(FigureCanvas, TimedAnimation):
    def __init__(self):
        self.addedData = []
        print('Matplotlib Version:', matplotlib.__version__)

        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50

        # The window
        self.fig = Figure(figsize=(5, 5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)

        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)

        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval=50, blit=True)

    def new_frame_seq(self):
        return iter(range(self.n.size))

    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])

    def addData(self, value):
        self.addedData.append(value)

    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom, top)
        self.draw()

    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass

    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])

        self.line1.set_data(self.n[0:self.n.size - margin], self.y[0:self.n.size - margin])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
        for l in self._drawn_artists:
            l.set_animated(True)


# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
    data_signal = QtCore.pyqtSignal(float)


def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)

    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0

    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i])  # <- Here you emit a signal!
        i += 1


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    QtWidgets.QApplication.setStyle(QtWidgets.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()

    sys.exit(app.exec_())