【问题标题】:Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up在 wxPython 中嵌入 matplotlib FuncAnimation:不需要的图形弹出
【发布时间】:2019-02-02 12:01:11
【问题描述】:

我已尝试将以下示例修改为实时绘图。

Embedding a matplotlib figure inside a WxPython panel

我正在尝试读取来自 Arduino 的串行数据并绘制/更新收集的数据。问题是图形出现在 wx App 之前,我需要关闭图形才能看到 wx App。

我认为问题与以下几行有关,但我不知道为什么。

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

脚本如下。

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

这是在 GUI 前弹出的图。

关闭图wx应用后可见。

我试图摆脱弹出的图形,只看到嵌入在 wx 应用程序中的图形。非常感谢任何帮助。

【问题讨论】:

  • @macroland 我以为我也在做同样的事情。你会建议我改变什么?
  • 请忘记我的第一条评论,我想我能看到的唯一区别是plt.show(),您在构造函数中调用它,这就是数字排在第一位的原因。
  • @macroland 如果没有plt.show(),该图不会弹出。但在 GUI 中,该图是空的。
  • plt.show() 正在调用 matplotlib 显示功能,请尝试使用 plt.draw()
  • @RolfofSaxony 再次,图形没有弹出,但 GUI 中的图形是空的。

标签: python matplotlib wxpython wxwidgets


【解决方案1】:

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

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

注意
self.ax.set_xlim(0.0,float(length + 1)) 可以调整为 self.ax.set_xlim(float(length - 10), float(length + 1)) 之类的东西,它会跟随当前值,而不仅仅是不断地延伸 x 轴。

【讨论】:

  • 你最后破解了这个小坚果吗?我从你的另一个问题中猜到这让你很伤心。
猜你喜欢
  • 1970-01-01
  • 2012-07-30
  • 1970-01-01
  • 2012-05-31
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多