在 wxPython 中嵌入 matplotlib FuncAnimation:不需要的图形弹出窗口 [英] Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up
问题描述
我已尝试修改以下示例以用于实时绘图.
关闭图 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屋!