Matplotlib FuncAnimation不会更新wx Panel中嵌入的绘图

时间:2018-08-28 13:34:04

标签: python matplotlib wxpython

我正在尝试使用matplotlib FuncAnimation用串行数据更新绘图。我正在使用以下示例将情节嵌入wx应用程序中。

Embedding a matplotlib figure inside a WxPython panel

但是,该图不会更新,只会显示初始图。 enter image description here

实际上,永远不会执行更新功能,该更新功能将在try和except块中使用打印语句进行检查。您可能会在这里看到脚本。

import wx

from matplotlib.figure import Figure as Fig
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar

from collections import deque
import serial
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mlp
import numpy as np

# Class that inherits wx.Panel. The purpose is to embed it into 
# a wxPython App. That part can be seen in main()
class Serial_Plot(wx.Panel):
    def __init__(self, parent, strPort, id=-1, dpi=None, **kwargs):
        super().__init__(parent, id=id, **kwargs)
        self.figure  = Fig(figsize=(20,20))
        self.ax = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.plot_data, = self.ax.plot([1,2,3,4],[1,2,3,4])

        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND)
        sizer.Add(self.toolbar, 0, wx.RIGHT | wx.EXPAND)
        self.SetSizer(sizer)
        self.Fit()
        # Serial communication
        self.ser = serial.Serial(strPort, 115200)
        # Serial data initialized as deque. The serial readings from arduino
        # are set to be one value per line.
        self.vals = deque()
        # matplotlib function animation
        anim = animation.FuncAnimation(self.figure, self.update, 
                                   interval=20)
        plt.show()
        self.close
    def update(self, i):
        try:
            print('trying')
            # read serial line
            data = float(self.ser.readline().decode('utf-8'))
            print(data)
            self.vals.append(data)
            # update plot data
            self.plot_data.set_data(range(len(self.vals)), self.vals)
        except:
            print('oops')
            pass
        return self.plot_data

    def close(self):
        # close serial
        self.ser.flush()
        self.ser.close()

def main():

    app = wx.App(False)
    frame = wx.Frame(None, -1, "WX APP!")
    demo_plot = Serial_Plot(frame,'COM3')
    frame.Show()
    app.MainLoop()

if __name__ == "__main__":
    main()

1 个答案:

答案 0 :(得分:0)

正如我在上一个答案(Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up)中所说,尝试在wxpython中使用animation.FuncAnimation()plt.show()是行不通的,因为您有2个main.loops。
如果我们从您最初的问题中剔除所有wx.python并将其缩减为基本内容,我们将得到以下信息:

from collections import deque
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
vals = deque()
figure  = plt.figure(figsize=(20,20))
ax = plt.axes(xlim=(0, 1000), ylim=(0, 5000))
plot_data, = ax.plot([], [])

def update(i):
    data = float(random.randint(1000, 5000))
    vals.append(data)
    plot_data.set_data(range(len(vals)), vals)
    return plot_data

anim = animation.FuncAnimation(figure, update, interval=20)
plt.show()

enter image description here

它之所以有效,是因为animation函数是由plt.show()控制的,即matplotlib,但是一旦引入wxpython,它将控制main.loop而不是matplotlib。
我相信您将必须使用wxpython timer来控制串行设备的读取,并将数据馈送到绘图中,如我先前的回答所示。 Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up