在 wxPython 中嵌入 matplotlib FuncAnimation:不需要的图形弹出窗口 [英] Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up

查看:50
本文介绍了在 wxPython 中嵌入 matplotlib FuncAnimation:不需要的图形弹出窗口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已尝试修改以下示例以用于实时绘图.

关闭图 wx 应用程序后可见.

我试图摆脱弹出的图形,只看到嵌入在 wx 应用程序中的图形.我真的很感激任何帮助.

解决方案

我认为您可能通过使用 animation.FuncAnimation 抓住了错误的一端,因为我认为那是一个 matplotlib 函数,它期望由 matplotlib 的主循环控制,但您使用的是有自己的 wxpython.(在这一点上,我保留大错特错的权利:))
下面是您的代码,重新编写以使用 random 来避免串行端口,并包含一个 wx.Timer 来执行更新.

import wxfrom matplotlib.figure import Figure as Fig从 matplotlib.backends.backend_wxagg 导入 FigureCanvasWxAgg 作为 FigureCanvas从 matplotlib.backends.backend_wxagg 导入 NavigationToolbar2WxAgg 作为 NavigationToolbar从集合导入双端队列#导入序列号导入 matplotlib.pyplot 作为 plt导入 matplotlib.animation 作为动画将 matplotlib 导入为 mlp将 numpy 导入为 np随机导入# 继承 wx.Panel 的类.目的是将其嵌入# 一个 wxPython 应用程序.这部分可以在 main() 中看到类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)# 串行通信# self.ser = serial.Serial(strPort, 115200)# 串行数据初始化为双端队列.来自 arduino 的串行读数# 设置为每行一个值.self.vals = deque()# matplotlib 函数动画#anim = animation.FuncAnimation(self.figure, self.update,#间隔=2)#plt.show()plt.ion() #开启交互式绘图#self.close#self.timer.Start(1000)定义更新(自我,事件):#尝试:# 读取串行线#data = float(self.ser.readline().decode('utf-8'))数据 = 浮点数(随机.randint(1, 50))self.vals.append(data)# 更新绘图数据长度 = len(self.vals)self.plot_data.set_data(范围(长度),self.vals)#更新 x 轴以跟随交互式绘图self.ax.set_xlim(0.0,float(length + 1))#除了:#    经过#return self.plot_dataplt.plot()定义关闭(自我):# 关闭串口self.ser.flush()self.ser.close()定义主():app = wx.App(False)frame = wx.Frame(None, -1, "WX APP!")demo_plot = Serial_Plot(frame,'COM3')frame.Show()app.MainLoop()如果 __name__ == "__main__":主要的()

注意
self.ax.set_xlim(0.0,float(length + 1)) 可以调整为类似 self.ax.set_xlim(float(length - 10), float(length + 1)) 将遵循当前值,而不仅仅是不断扩展 x 轴.

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.

解决方案

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.

这篇关于在 wxPython 中嵌入 matplotlib FuncAnimation:不需要的图形弹出窗口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆