Reputation: 125
I am trying to update a plot with serial data with matplotlib FuncAnimation. I am using the following example to embed the plot in a wx app.
Embedding a matplotlib figure inside a WxPython panel
However, the plot is not updated and only the initial plot is displayed.
In fact, the update function is never executed which is checked with print statements in try and except blocks. You may see the script here.
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()
Upvotes: 0
Views: 2124
Reputation: 22438
As I said in my previous answer(Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up), trying to use animation.FuncAnimation()
and plt.show()
within wxpython is not going to work, because you have 2 main.loops.
If we cull all of the wx.python from your original question and pare it down to the basics, we get this:
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()
It works because the animation
function is controlled by plt.show()
i.e. matplotlib but once you introduce wxpython, it controls the main.loop not matplotlib.
I believe that you will have to use a wxpython timer
to control the serial device read, feeding data into the plot, as demonstrated in my previous answer.
Embedding matplotlib FuncAnimation in wxPython: Unwanted figure pop-up
Upvotes: 1