首页 文章

如何使用matplotlib在while循环中实时绘图?

提问于
浏览
165

我试图使用OpenCV实时绘制来自摄像机的一些数据 . 但是,实时绘图(使用matplotlib)似乎不起作用 .

我把这个问题分成了这个简单的例子:

fig = plt.figure()
plt.axis([0, 1000, 0, 1])

i = 0
x = list()
y = list()

while i < 1000:
    temp_y = np.random.random()
    x.append(i)
    y.append(temp_y)
    plt.scatter(i, temp_y)
    i += 1
    plt.show()

我希望这个例子可以单独绘制1000个点 . 实际发生的是窗口弹出第一个点显示(确定),然后在填充图表的其余部分之前等待循环完成 .

有什么想法,为什么我没有看到一次填充一个点?

10 回答

  • 26

    这是有问题的代码的工作版本(至少需要2011-11-14版本的Matplotlib 1.1.0版本):

    import numpy as np
    import matplotlib.pyplot as plt
    
    plt.axis([0, 10, 0, 1])
    
    for i in range(10):
        y = np.random.random()
        plt.scatter(i, y)
        plt.pause(0.05)
    
    plt.show()
    

    请注意一些变化:

    • 调用 plt.pause(0.05) 以绘制新数据并运行GUI的事件循环(允许鼠标交互) .
  • 11

    如果你're interested in realtime plotting, I'建议调查matplotlib's animation API . 特别是,使用 blit 来避免在每一帧上重新绘制背景可以为您带来显着的速度增益(~10x):

    #!/usr/bin/env python
    
    import numpy as np
    import time
    import matplotlib
    matplotlib.use('GTKAgg')
    from matplotlib import pyplot as plt
    
    
    def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
        """ A simple random walk with memory """
    
        r, c = dims
        gen = np.random.RandomState(seed)
        pos = gen.rand(2, n) * ((r,), (c,))
        old_delta = gen.randn(2, n) * sigma
    
        while True:
            delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
            pos += delta
            for ii in xrange(n):
                if not (0. <= pos[0, ii] < r):
                    pos[0, ii] = abs(pos[0, ii] % r)
                if not (0. <= pos[1, ii] < c):
                    pos[1, ii] = abs(pos[1, ii] % c)
            old_delta = delta
            yield pos
    
    
    def run(niter=1000, doblit=True):
        """
        Display the simulation using matplotlib, optionally using blit for speed
        """
    
        fig, ax = plt.subplots(1, 1)
        ax.set_aspect('equal')
        ax.set_xlim(0, 255)
        ax.set_ylim(0, 255)
        ax.hold(True)
        rw = randomwalk()
        x, y = rw.next()
    
        plt.show(False)
        plt.draw()
    
        if doblit:
            # cache the background
            background = fig.canvas.copy_from_bbox(ax.bbox)
    
        points = ax.plot(x, y, 'o')[0]
        tic = time.time()
    
        for ii in xrange(niter):
    
            # update the xy data
            x, y = rw.next()
            points.set_data(x, y)
    
            if doblit:
                # restore background
                fig.canvas.restore_region(background)
    
                # redraw just the points
                ax.draw_artist(points)
    
                # fill in the axes rectangle
                fig.canvas.blit(ax.bbox)
    
            else:
                # redraw everything
                fig.canvas.draw()
    
        plt.close(fig)
        print "Blit = %s, average FPS: %.2f" % (
            str(doblit), niter / (time.time() - tic))
    
    if __name__ == '__main__':
        run(doblit=False)
        run(doblit=True)
    

    输出:

    Blit = False, average FPS: 54.37
    Blit = True, average FPS: 438.27
    
  • 5

    show 可能不是最好的选择 . 我要做的是改用 pyplot.draw() . 您还可能希望在循环中包含一个小的时间延迟(例如, time.sleep(0.05) ),以便您可以看到正在发生的情节 . 如果我对你的例子进行了这些修改,它对我有用,我看到每个点一次出现一个 .

  • 32

    这些方法都不适合我 . 但我发现了这个Real time matplotlib plot is not working while still in a loop

    你只需要添加

    plt.pause(0.0001)
    

    而且你可以看到新的情节 .

    So your code should look like this, and it will work

    import matplotlib.pyplot as plt
    import numpy as np
    plt.ion() ## Note this correction
    fig=plt.figure()
    plt.axis([0,1000,0,1])
    
    i=0
    x=list()
    y=list()
    
    while i <1000:
        temp_y=np.random.random();
        x.append(i);
        y.append(temp_y);
        plt.scatter(i,temp_y);
        i+=1;
        plt.show()
        plt.pause(0.0001) #Note this correction
    
  • 58

    我知道我回答这个问题有点晚了 . 不过,我前段时间制作了一些代码来绘制实时图表,我想分享一下:

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

    试一试吧 . 将此代码复制粘贴到新的python文件中,然后运行它 . 你应该得到一个漂亮,流畅的图表:

    enter image description here

  • 234

    我知道这个问题已经过时了,但现在有一个名为drawnow的软件包在GitHub上被称为"python-drawnow" . 这提供了类似于MATLAB的绘图界面 - 您可以轻松更新图形 .

    您的用例的示例:

    import matplotlib.pyplot as plt
    from drawnow import drawnow
    
    def make_fig():
        plt.scatter(x, y)  # I think you meant this
    
    plt.ion()  # enable interactivity
    fig = plt.figure()  # make a figure
    
    x = list()
    y = list()
    
    for i in range(1000):
        temp_y = np.random.random()
        x.append(i)
        y.append(temp_y)  # or any arbitrary update to your figure's data
        i += 1
        drawnow(make_fig)
    

    python-drawnow是一个围绕 plt.draw 的薄包装器,但提供了在图形显示后确认(或调试)的能力 .

  • 7

    顶部(以及许多其他)答案 Build 在 plt.pause() 之上,但这是在matplotlib中制作动画的旧方法 . 它不仅速度慢,而且在每次更新时都会引起焦点(我很难停止绘制python进程) .

    TL;DR: 您可能想要使用 matplotlib.animationas mentioned in documentation) .

    在挖掘了各种答案和代码之后,事实证明这是为我无限地绘制传入数据的一种平滑方式 .

    这是我的快速入门代码 . 它以无限的每200ms在[0,100]中随机编号绘制当前时间,同时还处理视图的自动重新缩放:

    from datetime import datetime
    from matplotlib import pyplot
    from matplotlib.animation import FuncAnimation
    from random import randrange
    
    x_data, y_data = [], []
    
    figure = pyplot.figure()
    line, = pyplot.plot_date(x_data, y_data, '-')
    
    def update(frame):
        x_data.append(datetime.now())
        y_data.append(randrange(0, 100))
        line.set_data(x_data, y_data)
        figure.gca().relim()
        figure.gca().autoscale_view()
        return line,
    
    animation = FuncAnimation(figure, update, interval=200)
    
    pyplot.show()
    

    您还可以探索 blit 以获得更好的性能as in FuncAnimation documentation .

  • 1

    问题似乎是你希望 plt.show() 显示窗口然后返回 . 它没有这样做 . 程序将在此时停止,并且只有在关闭窗口后才会恢复 . 您应该能够测试:如果您关闭窗口,则应弹出另一个窗口 .

    要解决该问题,请在循环后调用 plt.show() 一次 . 然后你得到完整的情节 . (但不是'real-time plotting')

    您可以尝试设置关键字参数 block ,如下所示: plt.show(block=False) 一次开头,然后使用.draw()进行更新 .

  • 4

    这是我在我的系统上工作的版本 .

    import matplotlib.pyplot as plt
    from drawnow import drawnow
    import numpy as np
    
    def makeFig():
        plt.scatter(xList,yList) # I think you meant this
    
    plt.ion() # enable interactivity
    fig=plt.figure() # make a figure
    
    xList=list()
    yList=list()
    
    for i in np.arange(50):
        y=np.random.random()
        xList.append(i)
        yList.append(y)
        drawnow(makeFig)
        #makeFig()      The drawnow(makeFig) command can be replaced
        #plt.draw()     with makeFig(); plt.draw()
        plt.pause(0.001)
    

    drawow(makeFig)行可以用makeFig()替换; plt.draw()序列,它仍然可以正常工作 .

  • 25

    如果你想要绘制而不是冻结线程,因为绘制了更多的点你应该使用plt.pause()而不是time.sleep()

    我使用以下代码绘制一系列xy坐标 .

    import matplotlib.pyplot as plt 
    import math
    
    
    pi = 3.14159
    
    fig, ax = plt.subplots()
    
    x = []
    y = []
    
    def PointsInCircum(r,n=20):
        circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
        return circle
    
    circle_list = PointsInCircum(3, 50)
    
    for t in range(len(circle_list)):
        if t == 0:
            points, = ax.plot(x, y, marker='o', linestyle='--')
            ax.set_xlim(-4, 4) 
            ax.set_ylim(-4, 4) 
        else:
            x_coord, y_coord = circle_list.pop()
            x.append(x_coord)
            y.append(y_coord)
            points.set_data(x, y)
        plt.pause(0.01)
    

相关问题