用Matplotlib在PyQt5界面绘制动态曲线、柱形、二维、三维图(下)

文章目录

    • 前言
    • 循环的实现
    • 图形数据的更新
      • 曲线图的更新
      • 柱形图的更新
      • 二维图的更新
      • 三维图的更新

前言

上一篇介绍了如何在PyQt的界面上通过Matplotlib绘制静态的曲线图、柱形图、二维图和三维图。这一篇介绍一下如何通过数据更新实现这些图形的动态显示。

循环的实现

要实现图形或数据的动态更新,必然需要一个时间循环,在PyQt中一般通过QTimer来实现循环控制。QTimer的使用方式如下所示。

    def __init__(self,parent=None):
        super(ImgDisp,self).__init__(parent)
        self.setupUi(self)
        self.Init_Widgets()
        self.timer=QTimer()
        self.timer.start(1)
         self.ts=time.time()
        self.timer.timeout.connect(self.UpdateImgs)

我们在上一篇创建的ImgDisp类的初始函数中添加QTimer计时器。QTimer计时器的使用方法分成三步:
1、创建一个计时器:

self.timer=QTimer()

2、启动计时器,括号里的数字代表循环的周期,单位为毫秒:

self.timer.start(1)

需要注意的是,如果待循环的函数体如果在设置的周期内没有完成,那么计时器会等待循环函数体执行完后再开始下一个循环。

3、设置循环函数体:

self.timer.timeout.connect(self.UpdateImgs)

这样,程序开始运行后就会开始计时,每个计时周期调用一次self.UpdateImgs函数,知道关闭程序。
如果程序运行中想要停止循环,可以通过调用计时器停止函数:

self.timer.stop()

图形数据的更新

如前所述,我们定义好了循环函数self.UpdateImgs,接下来需要在该函数中添加代码实现图形的动态更新。
self.UpdateImgs函数的代码内容如下:

    def UpdateImgs(self):
        dt=time.time()-self.ts
        self.LineUpdate(dt)
        self.BarUpdate(dt)
        self.ImgUpdate(dt)
        self.SurfUpdate(dt)

我们定义程序运行当前时间与程序开始的时间之差作为时间变量,实现图像的动态变化。图形的更新通过代码中四个函数调用实现。下面我们来分别编写这四个功能函数实现图形更新功能。
在Matlab里我们要对图形进行更新只要在该轴上重新绘制图形即可,而在Matplotlib中,重新调用plot、imshow等函数会在原来的图形上重复绘制新的图形,不仅达不到预期效果,还会消耗大量的计算资源。通过调用clear函数可以清楚掉以前的图形,但同时也会清楚掉坐标范围等属性,并不理想,最好的办法是直接更新产生图形的数据。

曲线图的更新

对于曲线图,直接调用我们上一篇定义的Line2D对象的set_ydata()函数即可对其数据进行更新,然后调用draw()函数即可:

    def LineUpdate(self,dt):
        z=np.sin(self.x+dt)
        self.line.set_ydata(z)
        self.LineFigure.draw()

柱形图的更新

对于柱形图,我们只需要调用set_height()函数更新每一个矩形的高度,然后调用draw函数更新图形,即可:

    def BarUpdate(self,dt):
        x=np.sin(np.arange(-4,4,0.5)+dt)
        for i in range(len(self.patches)):
            self.patches[i].set_height(x[i])
        self.bar.patches=self.patches
        self.BarFigure.draw()

二维图的更新

对于二维图的更新比较简单,只需要调用set_data函数,将新的二维矩阵传递给imag对象即可:

    def ImgUpdate(self,dt):
        X=self.X+dt
        Y=self.Y+dt
        R=np.sqrt(X**2+Y**2)
        Z=np.sin(R)
        self.ImgFig.set_data(Z)
        self.ImgFigure.draw()

三维图的更新

对于三维图则比较麻烦,它没有给出直接更新其数据的方法。如果我们去阅读plot_surface函数的帮助文档,会发现其返回的是一个Patch3DCollection的对象,再去查阅Patch3DCollection的定以可以发现它是一个多边形(polys)的列表。我们通过plot_surface的函数代码可以发现,它也是先利用X,Y,X数据创建好polys列表,然后再调用set_verts函数创建Patch3DCollection对象。因此,为了更新三维图,需要先利用新的X,Y,Z数据生成polys对象,然后再调用set_verts函数。而生成polys对象的方法可以从plot_surface函数的定义中借鉴过来。
因此,实现三维图的更新的代码如下所示,我们要先定义一个Get3dVerts(self,X,Y,Z)函数来生成polys对象,然后调用set_verts函数将该对象传递给Patch3DCollection对象。

    def SurfUpdate(self,dt):
        X = self.X + dt
        Y = self.Y + dt
        R = np.sqrt(X ** 2 + Y ** 2)
        Z = np.sin(R)
        polys=self.Get3dVerts(self.X,self.Y,Z)
        self.Surf.set_verts(polys)
        self.SurfFigure.draw()
    def Get3dVerts(self,X,Y,Z):
        if Z.ndim != 2:
            raise ValueError("Argument Z must be 2-dimensional.")
        X, Y, Z = np.broadcast_arrays(X, Y, Z)
        rows, cols = Z.shape
        rcount = 50
        ccount = 50
        rstride = int(max(np.ceil(rows / rcount), 1))
        cstride = int(max(np.ceil(cols / ccount), 1))
        # evenly spaced, and including both endpoints
        row_inds = list(range(0, rows - 1, rstride)) + [rows - 1]
        col_inds = list(range(0, cols - 1, cstride)) + [cols - 1]
        polys = []
        for rs, rs_next in zip(row_inds[:-1], row_inds[1:]):
            for cs, cs_next in zip(col_inds[:-1], col_inds[1:]):
                ps = [
                    # +1 ensures we share edges between polygons
                    cbook._array_perimeter(a[rs:rs_next + 1, cs:cs_next + 1])
                    for a in (X, Y, Z)
                ]
                # ps = np.stack(ps, axis=-1)
                ps = np.array(ps).T
                polys.append(ps)
        return polys

OK,到这里所有的代码都写完啦,运行下程序看看效果吧。以上程序的完整代码可以通过这里下载,提取码是:y9q7
用Matplotlib在PyQt5界面绘制动态曲线、柱形、二维、三维图(下)_第1张图片

你可能感兴趣的:(程序界面设计)