如何在 WHILE 循环中实时绘制?

How do I plot in real-time in a while loop?

提问人:Chris 提问时间:8/9/2012 最后编辑:Trenton McKinneyChris 更新时间:6/23/2023 访问量:832005

问:

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

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

python matplotlib while-loop 实时

评论


答:

5赞 Michael Mauderer 8/9/2012 #1

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

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

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

评论

2赞 Chris 8/9/2012
实时绘图确实是我所追求的。我将对某些东西进行 5 小时的测试,并想看看事情的进展情况。
0赞 praxmon 4/12/2014
你@Chris能够进行 5 小时的测试?我也在寻找类似的东西。我正在使用 plyplot.pause(time_duration) 来更新绘图。还有其他方法可以做到这一点吗?
34赞 BrenBarn 8/9/2012 #2

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

评论

14赞 George Aprilis 2/1/2016
我有非常相似的代码部分,当我尝试您的解决方案(绘制而不是显示和时间延迟)时,python根本没有打开图形窗口,只是通过循环...
404赞 Velimir Mlaker 3/31/2013 #3

以下是相关代码的工作版本(至少需要 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()

请注意对 的调用,它既绘制新数据,又运行 GUI 的事件循环(允许鼠标交互)。plt.pause(0.05)

评论

4赞 continuousqa 9/6/2014
这在 Python2 中对我有用。在 Python3 中,它没有。它将在渲染绘图窗口后暂停循环。但是在将 plt.show() 方法移动到循环之后......它为我解决了 Python3 的问题。
1赞 Velimir Mlaker 9/12/2014
奇怪,在 Python 3(版本 3.4.0)中工作正常 Matplotlib(版本 1.3.1) Numpy(版本 1.8.1) Ubuntu Linux 3.13.0 64位
50赞 denfromufa 9/26/2014
只需将 plt.draw() 替换为 plt.pause(0.1) 即可,而不是 plt.show() 和 plt.draw()
6赞 isti_spl 2/4/2016
在 Win64/Anaconda matplotlib.__version__ 1.5.0 上不起作用。打开了一个初始图形窗口,但没有显示任何内容,它一直处于阻塞状态,直到我关闭它
9赞 Trevor Boyd Smith 4/14/2016
这个答案需要先验的 x/y 数据知识......不需要的:我更喜欢 1。不要调用,而是创建两个列表 x 和 y 并调用 2。在循环中,将新的数据值追加到两个列表 3.叫plt.axis()plt.plot(x,y)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);
106赞 ali_m 3/31/2013 #4

如果你对实时绘图感兴趣,我建议你看看 matplotlib 的动画 API。特别是,用于避免在每一帧上重新绘制背景可以为您提供显着的速度提升 (~10x):blit

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

评论

1赞 ali_m 2/2/2015
@bejota 原始版本被设计为在交互式 matplotlib 会话中工作。要使它作为一个独立的脚本工作,有必要 1) 显式选择 matplotlib 的后端,以及 2) 在进入动画循环之前强制显示和绘制图形 和 。我已将这些更改添加到上面的代码中。plt.show()plt.draw()
3赞 Trevor Boyd Smith 4/14/2016
其意图/动机是否非常像“改进实时绘图”?如果你有一个matplotlib开发人员/博客讨论为什么/目的/意图/动机,那就太好了。(似乎这个新的 blit 操作会将 Matplotlib 从仅用于离线或非常缓慢地更改数据转换为现在您可以使用 Matplotlib 来非常快速地更新数据......几乎就像示波器一样)。blit()
2赞 Ninjakannon 12/29/2016
我发现这种方法使绘图窗口没有响应:我无法与它交互,这样做可能会使它崩溃。
2赞 James Nelson 4/26/2017
对于那些遇到“找不到 gtk”问题的人,它可以很好地使用不同的后端(我使用了“TKAgg”)。为了找到受支持的后盾,我使用了这个解决方案:stackoverflow.com/questions/3285193/...
2赞 awelkie 7/21/2017
这个答案中的链接似乎不再起作用了。这可能是一个最新的链接:scipy-cookbook.readthedocs.io/items/...
18赞 Scott 5/9/2014 #5

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

您的用例示例:

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

评论

0赞 chwi 11/17/2015
这使得 tk 挂在某个地方
1赞 Scott 11/18/2015
如果是这样,请提交具有更多上下文 github.com/scottsievert/python-drawnow/issues 的问题
1赞 jj080808 2/20/2017
+1 这适用于我绘制从 opencv 捕获的每一帧视频的实时数据,而 matplotlib 冻结了。
0赞 Dave C 12/4/2018
我试了一下,它似乎比其他方法慢。
0赞 big-vl 4/5/2020
不使用,我的服务器重启,matplotlib 冻结
42赞 Oren 6/15/2014 #6

没有一种方法对我有用。 但是我发现这个实时 matplotlib 图在循环中不起作用

您只需要添加

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

评论

11赞 Francisco Vargas 1/26/2016
每次都会为我打开一个新的图形/绘图窗口,有没有办法更新现有图形?也许是因为我正在使用 imshow ?
0赞 Oren 12/11/2016
@FranciscoVargas如果您使用的是 imshow,则需要使用 set_data,请看这里:stackoverflow.com/questions/17835302/...
1赞 user2672474 5/2/2015 #7

如果你想画而不是冻结你的线程,因为画了更多的点,你应该使用 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)
6赞 slehar 5/6/2015 #8

这是我在我的系统上工作的一个版本。

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)

drawnow(makeFig) 线可以替换为 makeFig();plt.draw() 序列,它仍然可以正常工作。

评论

1赞 CMCDragonkai 5/17/2018
你怎么知道暂停多长时间?这似乎取决于情节本身。
0赞 Hassan Daoud 7/23/2022
不工作,它创造了很多数字
59赞 K.Mulier 7/21/2016 #9

我知道我回答这个问题有点晚了。尽管如此,我不久前还是制作了一些代码来绘制实时图形,我想分享一下:

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 文件中,然后运行它。你应该得到一个漂亮、流畅的图形:

enter image description here

评论

2赞 K.Mulier 9/26/2016
我注意到当您关闭窗口时,线程一直在后台运行。所以我添加了关键字来解决这个问题。dataSendLoopdaemon = True
1赞 Reb.Cabin 6/29/2018
为此,虚拟环境需要一些工作。终于,成功了。conda install pyqt=4
3赞 Isaac Sim 12/18/2018
非常感谢您的基本代码。它帮助我通过根据您的代码修改和添加功能来构建一些简单的 UI。它节省了我的时间 = ]
2赞 Paul 5/19/2022
@DavidCian,没有比带有工作代码的帖子更好的选择了
2赞 David Cian 5/19/2022
@Paul 很酷,这是我自己的用例。如果工作代码总是好的代码,那么软件工程就不会;)。事实证明,事实上,PyQtGraph客观上更好,因为它从一开始就是为在线绘图而设计的,与Matplotlib非常不同。
47赞 Hai Zhang 12/27/2017 #10

顶部(和许多其他)答案是建立在 之上的,但这是在 matplotlib 中对情节进行动画处理的旧方法。它不仅速度慢,而且会导致每次更新时都会引起焦点(我很难停止绘制 python 过程)。plt.pause()

TL的;DR:您可能想要使用(如文档中所述)。matplotlib.animation

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

这是我的代码,用于快速入门。它每 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()

您还可以像 FuncAnimation 文档中那样探索更好的性能。blit

文档中的示例: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()

评论

2赞 Alexander Cska 8/23/2018
嗨,如果这一切都在循环中会发生什么。说。这里生成在线数据对,我想在它们可用后绘制它们。是否可以用 .我的目标是在每次迭代中逐步构建由数据定义的曲线。for i in range(1000): x,y = some func_func()some_func()x,yFuncAnimation
0赞 Hai Zhang 8/24/2018
@Alexander Cska 应该阻止。如果要追加数据,请检索它们并在函数中更新。pyploy.show()update
0赞 Alexander Cska 8/24/2018
我担心我真的不明白你的回答。请你放大你的建议。
0赞 Hai Zhang 8/25/2018
我的意思是,如果你在一个循环中调用,这个循环将被这个调用阻止,并且不会继续。如果你想一步一步地将数据追加到曲线上,把你的逻辑放在 中,它将被调用 ever,所以它也是一步一步的。pyplot.showupdateinterval
0赞 aquagremlin 1/31/2020
Zhang 的代码可以在控制台上工作,但不能在 jupyter 中工作。我只是在那里得到一个空白的地块。事实上,当我在顺序循环中填充 jupyter 中的数组并使用 pet.plot 语句在数组增长时打印数组时,我可以单独打印数组,但只能打印一个图。请参阅此代码:gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
8赞 Hamid Fadishei 3/17/2020 #11

另一种选择是散景。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 浏览器中显示结果。当数据由远程无头服务器进程生成时,它特别有用。

bokeh sample plot

评论

1赞 Hamid Fadishei 8/7/2021
是的,@samisnotinsane,但需要一些修改。请参考 push_notebook() 的文档和相关教程。
6赞 Nilani Algiriyage 4/5/2020 #12

实时绘制 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

评论

0赞 pfabri 4/27/2020
大约 2 分钟后,它真的开始变慢。原因可能是什么?也许应该放弃先前的观点,这些观点超出了当前的观点。
0赞 pfabri 4/28/2020
这看起来真的很不错,但它有几个问题:1.不可能戒掉 2.几分钟后,程序消耗了近 100 Mb 的 RAM,并开始急剧减慢。
0赞 nrofis 9/9/2021
注释中出现问题的原因是算法附加了新值而不删除旧值(尽管它仅显示最后 50 个步骤)。如果超过绘图限制,最好使用最大大小的队列从数组开头删除旧值(对 x 和 y 都使用 pop(0) )
1赞 yogender 9/29/2021 #13

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

也有一篇关于这一点的中等文章:

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 

输出为:

enter image description here

1赞 Jaakko 5/7/2022 #14

保留线型的圆形缓冲区的实时绘图:

import os
import time
import psutil
import collections

import matplotlib.pyplot as plt

pts_n = 100
x = collections.deque(maxlen=pts_n)
y = collections.deque(maxlen=pts_n)
(line, ) = plt.plot(x, y, linestyle="--")

my_process = psutil.Process(os.getpid())
t_start = time.time()
while True:
    x.append(time.time() - t_start)
    y.append(my_process.cpu_percent())

    line.set_xdata(x)
    line.set_ydata(y)
    plt.gca().relim()
    plt.gca().autoscale_view()
    plt.pause(0.1)

评论

0赞 Hassan Daoud 7/23/2022
不工作。它增加了很多数字
1赞 Jaakko 7/23/2022
我在上面测试了使用 Ubuntu 20.04.4、Python 3.8.10、matplotlib==3.1.2。
0赞 Hassan Daoud 7/24/2022
我在 vscode 和 google colab 中测试了它,但没有工作 😢
0赞 eEmanuel Oviedo 6/23/2023 #15

我以略有不同的观点创建了此代码:

import numpy as np
from matplotlib import pyplot


figure = pyplot.figure()
# get current axes  # If figure.axes == [], a new one is created
axes = figure.gca()
axes.axis([0, 1000, 0, 1])
figure.show()

x_val, x_values, y_values = 0, list(), list()
while x_val < 1000:
    if not pyplot.fignum_exists(figure.number):
        break  # break when window is closed
    y_val = np.random.random()
    x_values.append(x_val)
    y_values.append(y_val)
    axes.scatter(x_val, y_val)
    x_val += 1
    figure.canvas.draw()
    figure.canvas.flush_events()