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

Posted

技术标签:

【中文标题】如何使用 matplotlib 在 while 循环中实时绘图?【英文标题】:How do I plot in real-time in a while loop using matplotlib? 【发布时间】:2012-08-06 04:02:00 【问题描述】:

我正在尝试使用 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 个点。实际发生的情况是窗口弹出并显示第一个点(可以),然后等待循环完成,然后再填充图表的其余部分。

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

【问题讨论】:

【参考方案1】:

这是相关代码的工作版本(至少需要 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 的事件循环(允许鼠标交互)。

【讨论】:

这在 Python2 中对我有用。在 Python3 中它没有。它会在渲染绘图窗口后暂停循环。但是在将 plt.show() 方法移到循环之后......它为 Python3 解决了它,对我来说。 很奇怪,我在 Python 3 (ver 3.4.0) Matplotlib (ver 1.3.1) Numpy (ver 1.8.1) Ubuntu Linux 3.13.0 64-bit 中工作正常 而不是 plt.show() 和 plt.draw() 只需将 plt.draw() 替换为 plt.pause(0.1) 在 Win64/Anaconda matplotlib.__version__ 1.5.0 上不起作用。初始图形窗口打开,但没有显示任何内容,它一直处于阻塞状态,直到我关闭它 这个答案需要 x/y 数据的先验知识......这不是必需的:我更喜欢 1. 不要调用 plt.axis(),而是创建两个列表 x 和 y 并调用plt.plot(x,y) 2. 在您的循环中,将新数据值附加到两个列表 3. 调用 plt.gca().lines[0].set_xdata(x); plt.gca().lines[0].set_ydata(y); plt.gca().relim(); plt.gca().autoscale_view(); plt.pause(0.05);【参考方案2】:

如果您对实时绘图感兴趣,我建议您查看matplotlib's animation API。特别是,使用blit 来避免在每一帧上重绘背景可以给您带来显着的速度提升(~10 倍):

#!/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

【讨论】:

@bejota 原始版本设计用于交互式 matplotlib 会话。为了让它作为一个独立的脚本工作,有必要 1) 为 matplotlib 显式选择一个后端,以及 2) 在使用 plt.show()plt.draw() 进入动画循环之前强制显示和绘制图形。我已将这些更改添加到上面的代码中。 blit() 的意图/动机似乎是“改进实时绘图”?如果您有一个 matplotlib 开发人员/博客讨论为什么/目的/意图/动机,那就太好了。 (似乎这个新的 blit 操作会将 Matplotlib 从仅用于离线或非常缓慢变化的数据转换为现在您可以使用具有非常快速更新数据的 Matplotlib ......几乎就像一个示波器)。 我发现这种方法会使绘图窗口无响应:我无法与之交互,这样做可能会导致它崩溃。 对于那些遇到“gtk not found”问题的人,它适用于不同的后端(我使用了 'TKAgg')。为了找到受支持的支持,我使用了这个解决方案:***.com/questions/3285193/… 此答案中的链接似乎不再起作用。这可能是一个最新的链接:scipy-cookbook.readthedocs.io/items/…【参考方案3】:

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

PyQt4 代码:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt4)                    #
#                  -----------------------------                  #
#            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_())

''''''

  我最近重写了 PyQt5 的代码。PyQt5 的代码:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt5)                    #
#                  -----------------------------                  #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################

import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading

class CustomMainWindow(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 = QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget  background-color: %s " % QColor(210,210,235,255).name())
        self.LAYOUT_A = QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)
        # Place the zoom button
        self.zoomBtn = QPushButton(text = 'zoom')
        self.zoomBtn.setFixedSize(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()
        return

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

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

''' 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)
        return

    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([], [])
        return

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

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

    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
        return

    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]
        return

''' 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(QObject):
    data_signal = 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 = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

试试看吧。将此代码复制粘贴到一个新的 python 文件中,然后运行它。你应该得到一个漂亮的、平滑移动的图形:

【讨论】:

我注意到当您关闭窗口时dataSendLoop 线程一直在后台运行。所以我添加了daemon = True 关键字来解决这个问题。 为此的虚拟环境需要做一些工作。最后,conda install pyqt=4 成功了。 非常感谢基本代码。它通过根据您的代码修改和添加功能帮助我构建了一些简单的 UI。它节省了我的时间 = ] 嗨@IsaacSim,非常感谢你的好意。我很高兴这段代码很有帮助:-) 所以我采用了这个脚本并通过修改信号槽机制以使用 np.ndarry 类型并发出相对时间戳和信号的 np.array 来将时间戳添加到 x 轴。我正在更新每个帧绘制上的 xlim() ,这可以很好地显示带有新轴的信号,但 x-labels/ticks 仅在我更改窗口大小时才会短暂更新。 @K.Mulier 我基本上是在像数据一样滑动 xtick 轴,想知道你是否在这样的事情上取得了任何成功?【参考方案4】:

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

你只需要添加

plt.pause(0.0001)

然后你就可以看到新的地块了。

所以你的代码应该看起来像这样,它会工作

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

【讨论】:

这每次都会为我打开一个新的图形/绘图窗口,有没有办法更新现有的图形?也许是因为我正在使用 imshow ? @FranciscoVargas 如果你使用imshow,你需要使用set_data,看这里:***.com/questions/17835302/…【参考方案5】:

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

【讨论】:

我的代码部分非常相似,当我尝试您的解决方案时(绘制而不是显示和时间延迟)python 根本没有打开图形窗口,只是循环... 【参考方案6】:

***(和许多其他)答案是基于plt.pause(),但这是在 matplotlib 中为情节设置动画的一种古老方式。它不仅速度慢,而且每次更新都会引起焦点(我很难停止绘制 python 进程)。

TL;DR:您可能想使用matplotlib.animation (as mentioned in documentation)。

在挖掘了各种答案和代码片段之后,事实证明这对我来说是一种无限绘制传入数据的流畅方式。

这是我的快速入门代码。它每 200 毫秒用 [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。

blit 文档中的一个示例:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()

【讨论】:

嗨,如果这一切都在一个循环中会发生什么。说for i in range(1000): x,y = some func_func()。这里some_func() 生成在线x,y 数据对,一旦它们可用,我想绘制它们。是否可以使用FuncAnimation 执行此操作。我的目标是在每次迭代中逐步构建由数据定义的曲线。 @Alexander Cska pyploy.show() 应该阻止。如果要附加数据,请检索它们并在 update 函数中更新。 恐怕我不太明白您的回复。请您放大您的建议。 我的意思是,如果你在一个循环中调用pyplot.show,这个循环会被这个调用阻塞,并且不会继续。如果您想逐步将数据附加到曲线上,请将您的逻辑放在update 中,每个interval 都会调用它,所以它也是逐步的。 Zhang 的代码可以在控制台上运行,但不能在 jupyter 中运行。我只是在那里得到一个空白图。事实上,当我在 jupyter 中按顺序循环填充数组并随着 pet.plot 语句的增长而打印数组时,我可以单独打印出数组,但只能打印一个图。看到这个代码:gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a【参考方案7】:

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

您的用例示例:

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 的一个精简包装器,但提供了在图形显示后确认(或调试)的能力。

【讨论】:

这会让 tk 挂在某个地方 如果是这样,请提交更多上下文的问题github.com/scottsievert/python-drawnow/issues +1 这对我有用,用于绘制从 opencv 捕获的每帧视频的实时数据,而 matplotlib 冻结了。 我试过这个,它似乎比其他方法慢。 不要使用,我的服务器重启,matplotlib 冻结【参考方案8】:

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

要解决这个问题,只需在循环后调用一次plt.show()。然后你得到完整的情节。 (但不是“实时绘图”)

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

【讨论】:

实时绘图确实是我想要的。我将对某件事进行 5 小时的测试,并想看看事情进展如何。 @Chris 你能进行 5 小时的测试吗?我也在寻找类似的东西。我正在使用 plyplot.pause(time_duration) 来更新情节。还有其他方法吗?【参考方案9】:

另一种选择是使用bokeh。 IMO,至少对于实时绘图来说,它是一个不错的选择。这是问题中代码的散景版本:

from bokeh.plotting import curdoc, figure
import random
import time

def update():
    global i
    temp_y = random.random()
    r.data_source.stream('x': [i], 'y': [temp_y])
    i += 1

i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)

运行它:

pip3 install bokeh
bokeh serve --show test.py

散景通过 websocket 通信在 web 浏览器中显示结果。当数据由远程无头服务器进程生成时,它特别有用。

【讨论】:

是的@samisnotinsane,但需要一些修改。请参考 push_notebook() 的文档和相关教程。【参考方案10】:

这是我必须在我的系统上工作的版本。

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() 序列,它仍然可以正常工作。

【讨论】:

你怎么知道暂停多长时间?它似乎取决于情节本身。【参考方案11】:

实时绘制 CPU 使用情况的示例用例。

import time
import psutil
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)

i = 0
x, y = [], []

while True:
    x.append(i)
    y.append(psutil.cpu_percent())

    ax.plot(x, y, color='b')

    fig.canvas.draw()

    ax.set_xlim(left=max(0, i - 50), right=i + 50)
    fig.show()
    plt.pause(0.05)
    i += 1

【讨论】:

大约 2 分钟后它真的开始变慢了。可能是什么原因?或许应该删除不属于当前视图的较早点。 这看起来很不错,但有几个问题:1. 无法退出 2. 几分钟后,程序消耗了近 100 Mb 的 RAM,并开始显着减速。 cmets 中出现问题的原因是算法附加了新值而不删除旧值(尽管它只显示了最后 50 个步骤)。如果超出绘图限制(对 x 和 y 都使用 pop(0)),最好使用最大大小的队列从数组的开头删除旧值【参考方案12】:

如果你想绘制而不是在绘制更多点时冻结你的线程,你应该使用 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)

【讨论】:

【参考方案13】:

这是使用while循环绘制动态实时matplot动画的正确方法

There is a medium article on that too:

pip install celluloid # 这将捕获图像/动画

import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera # getting the camera
import matplotlib.animation as animation
from IPython import display
import time
from IPython.display import html

import warnings
%matplotlib notebook
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')

fig = plt.figure() #Empty fig object
ax = fig.add_subplot() #Empty axis object
camera = Camera(fig) # Camera object to capture the snap

def f(x):
    ''' function to create a sine wave'''
    return np.sin(x) + np.random.normal(scale=0.1, size=len(x))

l = []

while True:
    value = np.random.randint(9) #random number generator
    l.append(value) # appneds each time number is generated
    X = np.linspace(10, len(l)) # creates a line space for x axis, Equal to the length of l

    for i in range(10): #plots 10 such lines
        plt.plot(X, f(X))

    fig.show() #shows the figure object
    fig.canvas.draw() 
    camera.snap() # camera object to capture teh animation
    time.sleep(1)

以及保存等:

animation = camera.animate(interval = 200, repeat = True, repeat_delay = 500)
HTML(animation.to_html5_video())
animation.save('abc.mp4') # to save 

输出是:

【讨论】:

以上是关于如何使用 matplotlib 在 while 循环中实时绘图?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中的另一个 while 循环中正确地创建一个 while 循环?

如何选用forwhiledo while循环

循环链表

循环结构 while 和 do while 方法使用

校招 刷题

Python while 循环使用实例