dynamic更新matplotlib中的图
我正在Python中进行一个应用程序,它从一个串行端口收集数据,并根据到达时间绘制所收集数据的graphics。 数据到达的时间是不确定的。 我希望在收到数据时更新图表。 我search了如何做到这一点,发现了两种方法:
- 清除情节,重新绘制所有点的情节。
- 通过在特定的时间间隔之后更改曲线来使曲线animation化。
我不喜欢第一个程序运行并收集数据很长一段时间(例如一天),并且重绘图很慢。 第二个也是不可取的,因为数据的到达时间是不确定的,我希望只有在收到数据时才更新图。
只有当数据被接收时,我才能通过增加更多的点来更新图表吗?
有没有一种方法可以通过添加更多的点来更新剧情…
在matplotlib中有很多animation数据的方法,具体取决于你的版本。 你见过matplotlib食谱的例子吗? 另外,请查看matplotlib文档中更现代的animation示例 。 最后, animationAPI定义了一个函数FuncAnimation ,它能够及时地animation一个函数。 这个function可能就是你用来获取数据的function。
每种方法基本上都是设置被绘制对象的data
属性,所以不需要清除屏幕或graphics。 data
属性可以简单地扩展,所以你可以保留以前的点,只是不断添加到你的线(或图像或任何你正在绘制)。
鉴于你说你的数据到达时间是不确定的,最好的办法可能只是做一些事情:
import matplotlib.pyplot as plt import numpy hl, = plt.plot([], []) def update_line(hl, new_data): hl.set_xdata(numpy.append(hl.get_xdata(), new_data)) hl.set_ydata(numpy.append(hl.get_ydata(), new_data)) plt.draw()
然后当你从串口收到数据时,只需调用update_line
。
为了做到这一点没有FuncAnimation(例如,你想执行代码的其他部分,而生成的情节,或者你想同时更新几个情节),单独调用draw
不会产生情节(至lessqt后端)。
以下为我工作:
import matplotlib.pyplot as plt plt.ion() class DynamicUpdate(): #Suppose we know the x range min_x = 0 max_x = 10 def on_launch(self): #Set up plot self.figure, self.ax = plt.subplots() self.lines, = self.ax.plot([],[], 'o') #Autoscale on unknown axis and known lims on the other self.ax.set_autoscaley_on(True) self.ax.set_xlim(self.min_x, self.max_x) #Other stuff self.ax.grid() ... def on_running(self, xdata, ydata): #Update data (with the new _and_ the old points) self.lines.set_xdata(xdata) self.lines.set_ydata(ydata) #Need both of these in order to rescale self.ax.relim() self.ax.autoscale_view() #We need to draw *and* flush self.figure.canvas.draw() self.figure.canvas.flush_events() #Example def __call__(self): import numpy as np import time self.on_launch() xdata = [] ydata = [] for x in np.arange(0,10,0.5): xdata.append(x) ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2)) self.on_running(xdata, ydata) time.sleep(1) return xdata, ydata d = DynamicUpdate() d()
我知道我迟迟不回答这个问题,但对于你的问题,你可以看看“游戏杆”包。 我将其devise为绘制串口数据stream,但适用于任何stream。 它还允许交互式文本logging或graphics绘制(除了graphics绘制)。 没有必要在一个单独的线程中做你自己的循环,软件包会照顾它,只是给你想要的更新频率。 此外,terminal在绘图时仍可用于监视命令。 请参阅http://www.github.com/ceyzeriat/joystick/或https://pypi.python.org/pypi/joystick (使用pip安装游戏杆进行安装)
只需将np.random.random()replace为从以下代码中的串行端口读取的实际数据点即可:
import joystick as jk import numpy as np import time class test(jk.Joystick): # initialize the infinite loop decorator _infinite_loop = jk.deco_infinite_loop() def _init(self, *args, **kwargs): """ Function called at initialization, see the doc """ self._t0 = time.time() # initialize time self.xdata = np.array([self._t0]) # time x-axis self.ydata = np.array([0.0]) # fake data y-axis # create a graph frame self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1))) @_infinite_loop(wait_time=0.2) def _generate_data(self): # function looped every 0.2 second to read or produce data """ Loop starting with the simulation start, getting data and pushing it to the graph every 0.2 seconds """ # concatenate data on the time x-axis self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax) # concatenate data on the fake data y-axis self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax) self.mygraph.set_xydata(t, self.ydata) t = test() t.start() t.stop()