Tkinter matplotlib画布更新实时数据太慢

10
我正在使用一个设备,每秒发送100个读数,并希望我的GUI能够绘制这些数据的图表,显示最近收集的300个点。然而,我发现:
1. 将新数据点添加到y轴队列中 2. 清除已经存在的图形 3. 绘制新数据列表 4. 重新绘制画布
每绘制一个点需要将近0.2-0.4秒,速度非常慢。
以下是我目前正在使用的代码。一个while循环不断检查队列,一旦有新元素被推入它,就会以该元素为参数调用update函数。请问有人能够建议一些提高效率或替代matplotlib的方法吗?
class GraphFrame:
    def __init__(self,root,channel,index):
        self.root=root
        self.frame=tk.Frame(self.root)
        self.frame.pack(side=tk.LEFT)
     
        self.y = Queue(maxsize = 300)
        
        self.fig, self.axes = plt.subplots(1,1)
        self.axes.plot(list(self.y.queue))

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, padx=5, pady=5)

    def update(self, new_point):
        if self.y.full():
            self.y.get()
        self.y.put(new_point)
        self.fig.axes[0].clear()
        self.fig.axes[0].plot(list(self.y.queue))
        self.canvas.draw()

编辑:使用blitting和将Tkinter画布替换为带有图形图像的标签解决了问题。将尝试在github上上传代码并链接。


不要清除图形并从头开始构建,而应该修改线条的艺术家。 - Paul H
你能否发布一份代表性数据列表?比如15-25个数据点? - Paul H
Queue 是从哪里导入的? - Paul H
@PaulH 数据点只是来自传感器的浮点读数。当没有人站在前面时,值约为2000,而当有人站在前面时,值为20,000。没有什么特别的,只是浮点数。 - Mohamed Moustafa
@PaulH 从队列中导入Queue - Mohamed Moustafa
1个回答

5

不要为每个新点重新构建您的图形,而是修改您已有的LineArtist。

class GraphFrame:
    def __init__(self,root,channel,index):
        self.root=root
        self.frame=tk.Frame(self.root)
        self.frame.pack(side=tk.LEFT)
     
        self.y = Queue(maxsize = 300)
        
        self.fig, self.axes = plt.subplots(1,1)

        # capture the artist
        self.line, = self.axes.plot(list(self.y.queue))

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, padx=5, pady=5)

    def update(self, new_point):
        if self.y.full():
            self.y.get()
        self.y.put(new_point)
        
        # update the artist
        self.line.set_xdata(list(range(len(self.y))))
        self.line.set_ydata(list(self.y.queue))


@MohamedMoustafa,你在分配中包含了逗号吗?如果是这样,你可能只需要从该列表中提取出第一个(唯一的?)元素。 - Paul H
@MohamedMoustafa 在更新艺术家之后,可能需要加上 self.fig.draw() - Paul H
创建了一个GraphFrame对象,然后循环不断地轮询一个队列,当队列有元素时,获取该元素并将其作为参数调用更新函数。当设置一个标志位为false时,循环中断。这就是正在发生的一切。 - Mohamed Moustafa
我理解您的意思,但是我无法直接运行您的代码,因此我(或其他任何人)提出的任何建议都只是凭空猜测。请参阅https://stackoverflow.com/help/minimal-reproducible-example。 - Paul H
没事了,问题已经解决。请查看问题的编辑。 - Mohamed Moustafa
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接