Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up

你。 提交于 2019-12-14 04:03:48

问题


I have tried to modify the following example for a live plot.

Embedding a matplotlib figure inside a WxPython panel

I am trying to read the serial data coming from Arduino and plot/update the collected data. The problem is that the figure comes up before the wx App and I need to close the figure in order to see the wx App.

I believe that the problem is related with the following lines but I don't know why.

self.figure  = plt.figure(figsize=(20,20))
self.ax = plt.axes(xlim=(0, 1000), ylim=(0, 5000))

The script is as follows.

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  = plt.figure(figsize=(20,20))
        self.ax = plt.axes(xlim=(0, 1000), ylim=(0, 5000))
        self.plot_data, = self.ax.plot([], [])
        self.canvas = FigureCanvas(self, -1, self.figure)
        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)

        # 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:
            # read serial line
            data = float(self.ser.readline().decode('utf-8'))
            self.vals.append(data)
            # update plot data
            self.plot_data.set_data(range(len(self.vals)), self.vals)
        except:
            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()

Here is the figure popping up before the GUI.

After closing the figure wx app is visible.

I am trying to get rid of the figure that is popping up and only see the figure embedded in wx app. I would really appreciate any help.


回答1:


I think that you may have got hold of the wrong end of the stick by using animation.FuncAnimation because I think that that is a matplotlib function which will be expecting to be controlled by matplotlib's main loop but you are using wxpython which has its own. (I reserve the right, at this point, to be horribly wrong :) )
Below is your code, reworked to use random to avoid a serial port and including a wx.Timer to perform the updates.

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
import random

# 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  = plt.figure(figsize=(20,20))
        self.ax = plt.axes(xlim=(0, 10), ylim=(0, 50))
        self.plot_data, = self.ax.plot([], [])
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.toolbar = NavigationToolbar(self.canvas)
        self.toolbar.Realize()
#
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.update, self.timer)
#
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.EXPAND)
        sizer.Add(self.toolbar, 0, wx.RIGHT | wx.EXPAND)
        self.SetSizer(sizer)

        # 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=2)
        #plt.show()

        plt.ion() #Turn on interactive plot

        #self.close
#
        self.timer.Start(1000)

    def update(self,event):
        #try:
            # read serial line
            #data = float(self.ser.readline().decode('utf-8'))
        data = float(random.randint(1, 50))
        self.vals.append(data)
            # update plot data
        length = len(self.vals)
        self.plot_data.set_data(range(length), self.vals)

        #Update x axis to follow interactive plot
        self.ax.set_xlim(0.0,float(length + 1))

        #except:
        #    pass
        #return self.plot_data
        plt.plot()

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

N.B.
self.ax.set_xlim(0.0,float(length + 1)) could be adjusted to something like self.ax.set_xlim(float(length - 10), float(length + 1)) which would follow the current values, not just constantly extend the x axis.



来源:https://stackoverflow.com/questions/52047934/embedding-matplotlib-funcanimation-in-wxpython-unwanted-figure-pop-up

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!