我正在做一个Tkinter项目,我正在绘制来自不同传感器的实时数据. 我正在使用Tkinter来创建图形用户界面和matplotlib动画.FuncAnimation来创建每给定时间(即1秒)更新的实时绘图.代码是用python3编写的.
只要总点数不多,这项工作就可以了.当点数超过300-400时,系统开始累积延迟、减速并最终冻结. 即,如果我的目标是每1秒进行一次读取,则在开始时,系统每隔1秒和几毫秒返回一次读取.然而,随着时间的推移,它开始以线性趋势增加间隔(请参见下图)
我可以通过创建一个在x轴上具有迭代次数(即读数)、在y轴上具有每次迭代之间的增量时间并每秒更新该图(即使我使用更长的时间间隔,结果也是相同的)的图来重现该问题.
我试着将动画功能放在一个独立的线程中,就像在其他帖子中建议的那样,但完全没有帮助.
如果我没有创建绘图,但我使用生成的数据(即增量时间)来更新标签,则我没有问题,因此它一定与绘图的创建/更新有关.
打开/关闭blit无济于事,不管怎样,我还是宁愿把它关掉.
请参见下面的代码的简短版本,以重现错误和600次迭代后的输出图像.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends import backend_tkagg as bk
import matplotlib.animation as animation
import numpy as np
import time
import threading
class Application(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
# =============================================================================
# # Test 1 flags initialization
# =============================================================================
self.ani = None
# =============================================================================
# Canvas frame
# =============================================================================
self.fig = plt.figure(figsize=(15,5))
frm = tk.Frame(self)
frm.pack()
self.canvas = bk.FigureCanvasTkAgg(self.fig, master=frm)
self.canvas.get_tk_widget().pack()
# =============================================================================
# # Figure initialization
# =============================================================================
self.ax = self.fig.add_subplot(1,1,1)
self.ax.plot([],[],'-k', label='delta time')
self.ax.legend(loc='upper right')
self.ax.set_xlabel('n of readings [-]')
self.ax.set_ylabel('time difference between readings [s]')
# =============================================================================
# # Start/Quick button
# =============================================================================
frm4 = tk.Frame(self)
frm4.pack(side='top', fill='x')
frm_acquisition = tk.Frame(frm4)
frm_acquisition.pack()
self.button= tk.Button(frm_acquisition, text="Start acquisition", command=lambda: self.on_click(), bg='green')
self.button.grid(column = 0, row=0)
# =============================================================================
# # Methods
# =============================================================================
def on_click(self):
'''the button is a start/stop button '''
if self.ani is None:
self.button.config(text='Quit acquisition', bg='red')
print('acquisition started')
return self.start()
else:
self.ani.event_source.stop()
self.button.config(text='Start acquisition', bg='green')
print('acquisition stopped')
self.ani = None
return
def start(self):
self.starting_time = time.time()
self.time = np.array([])
self.ani = animation.FuncAnimation(self.fig, self.animate_thread, interval =1000, blit=False, cache_frame_data=False) #interval in ms
self.ani._start()
return
# Some post suggested to put animate() in an indipendent thread, but did not solve the problem
def animate_thread(self, *args):
self.w = threading.Thread(target=self.animate)
self.w.daemon = True # Daemonize the thread so it exits when the main program finishes
self.w.start()
return self.w
def animate(self, *args):
self.time = np.append(self.time, time.time()-self.starting_time)
if len(self.time) > 1:
self.ax.scatter(len(self.time),self.time[-1]-self.time[-2], c='k')
# root.update() # another post suggested root.update() but did not help either
return self.ax,
if __name__ == "__main__":
root = tk.Tk()
app=Application(root)
app.pack()
root.mainloop()
延时曲线图: