我正在做一个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()

延时曲线图:

Time delay plot

推荐答案

大多数matplotlib动画示例建议在动画初始化期间创建情节轴,而不是创建内部动画函数,如上面的代码部分所示.

因此一种可能方法是创建散布对象并将其保存在初始化一次的变量中.它实际上返回matplotlib collections.比如像这样的东西,

    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]')
    self.scatplot = self.ax.scatter(x, y, c='k') #here x and y are array data initalized empty.

在动画功能中,你可以使用下面这样的‘self.散点图’,并使用适当的数据格式和定制,参考这里How to animate a scatter plotFuncAnimation

    def animate(self, *args):
        self.time = np.append(self.time, time.time()-self.starting_time)
        data = np.stack([len(self.time), self.time[-1]-self.time[-2]])
        if len(self.time) > 1:
            self.scatplot.set_offsets(data)
        return self.scatplot,

Python相关问答推荐

使用Keras的线性回归参数估计

仿制药的类型铸造

Matlab中是否有Python的f-字符串等效物

try 在树叶 map 上应用覆盖磁贴

处理(潜在)不断增长的任务队列的并行/并行方法

使用miniconda创建环境的问题

输出中带有南的亚麻神经网络

从numpy数组和参数创建收件箱

给定高度约束的旋转角解析求解

使用groupby方法移除公共子字符串

可以bcrypts AES—256 GCM加密损坏ZIP文件吗?

基于行条件计算(pandas)

判断Python操作:如何从字面上得到所有decorator ?

用fft计算指数复和代替求和来模拟衍射?

我可以不带视频系统的pygame,只用于游戏手柄输入吗?''

在第一次调用时使用不同行为的re. sub的最佳方式

Python日志(log)库如何有效地获取lineno和funcName?

比较两个有条件的数据帧并删除所有不合格的数据帧

无法在盐流道中获得柱子

如何将参数名作为参数传入到函数中?