Tkinter - 画布内存泄漏 [英] Tkinter - memory leak with canvas

查看:29
本文介绍了Tkinter - 画布内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个处理 Modbus 通信的 Python 脚本.我添加的一个功能是一个图表",它显示响应时间以及指示响应是成功、异常还是错误的颜色编码线.该图形只是来自 Tkinter 的可滚动画布小部件.

I have a Python script that handles Modbus communications. One feature I added was a "graph" that shows the response times along with a color coded line that indicates if the response was successful, had an exception, or an error. The graph is just a scrollable canvas widget from Tkinter.

在绘制一定数量的线后,旧线将被删除,然后在最后添加一条新线.在这个例子中,我将它设置为 10,这意味着画布上一次永远不会超过 10 行.

After graphing a certain number of lines old lines will be deleted and then a new one will be added to the end. For this example I have it set to 10, which means there will never be more than 10 lines on the canvas at once.

代码运行正常,但此函数中某处存在内存泄漏.我让它运行了大约 24 小时,24 小时后它占用了大约 6 倍的内存.该函数是更大类的一部分.

The code works correctly but there is a memory leak somewhere in this function. I let it run for about 24 hours and it took about 6x more memory after 24 hours. The function is part of a larger class.

我目前的猜测是我的代码导致画布大小不断扩大",这会慢慢吃掉内存.

My current guess is that my code causes the canvas size to constantly "expand," which slowly eats up the memory.

self.lineList = []
self.xPos = 0

def UpdateResponseTimeGraph(self):
    if not self.graphQueue.empty():
        temp = self.graphQueue.get() #pull from queue. A separate thread handles calculating the length and color of the line. 
        self.graphQueue.task_done()

        lineName     = temp[0] #assign queue values to variables
        lineLength   = temp[1]
        lineColor    = temp[2]

        if len(self.lineList) >= 10: #if more than 10 lines are on the graph, delete the first one.
            self.responseTimeCanvas.delete(self.lineList[0])
            del self.lineList[0]

        #Add line to canvas and a list so it can be referenced.
        self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-lineLength, 
                                                fill=lineColor, outline=''))

        self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?

        self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))

        self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.



    self.graphFrame.after(10, self.UpdateResponseTimeGraph)

一旦达到限制,一个解决方案可以循环回到图表的开头,但我宁愿不这样做,因为它可能会混淆图表的开始位置.通常我收到的回复比 10 多得多.

One solution could be loop back to the start of the graph once a limit is reached but I would rather not do this since it may be confusing where the graph starts. Usually I have far more responses than 10.

我仍在做跟踪和错误的事情,但看起来内存泄漏可以通过 Bryan 的建议消除,只要不通过 itemconfig 更改行属性.下面的代码应该能够按原样运行,如果您使用的是 python 2.7,将导入语句从 tkinter 更改为 Tkinter(小写与大写 t).这段代码会有内存泄漏.注释掉 itemconfig 行,它将被消除.

I'm still doing to trail and error stuff but it looks like the memory leak can be eliminated with Bryan's suggestion as long as the line attributes are not changed via itemconfig. The code below should be able to run as is, if you're on python 2.7 change the import statement from tkinter to Tkinter (lower case vs uppercase t). This code will have the memory leak in it. Comment out the itemconfig line and it will be eliminated.

import tkinter
from tkinter import Tk, Frame, Canvas, ALL
import random

def RGB(r, g, b):
    return '#{:02x}{:02x}{:02x}'.format(r, g, b)

class MainUI:
    def __init__(self, master):
        self.master = master
        self.lineList = []
        self.xPos = 0

        self.maxLine = 122

        self.responseIndex = 0 


        self.responseWidth = 100
        self.responseTimeCanvas = Canvas(self.master, height=self.responseWidth)
        self.responseTimeCanvas.pack()

        self.UpdateResponseTimeGraph()

    def UpdateResponseTimeGraph(self):
        self.lineLength   = random.randint(10,99)

        if len(self.lineList) >= self.maxLine:
            self.lineLength = random.randint(5,95)
            self.responseTimeCanvas.coords(self.lineList[self.responseIndex % self.maxLine], self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength)

            #if i comment out the line below the memory leak goes away.
            self.responseTimeCanvas.itemconfig(self.lineList[self.responseIndex % self.maxLine], fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)))
        else:
            self.lineList.append(self.responseTimeCanvas.create_rectangle(self.xPos, self.responseWidth, self.xPos + 4, self.responseWidth-self.lineLength, 
                                                fill=RGB(random.randint(0,255), random.randint(0,255), random.randint(0,255)), outline=''))


        self.xPos += 5 #will cause the next line to start 5 pixels later. MEMORY LEAK HERE?
        self.responseIndex += 1

        self.responseTimeCanvas.config(scrollregion=self.responseTimeCanvas.bbox(ALL))

        self.responseTimeCanvas.xview_moveto(1.0) #move to the end of the canvas which is scrollable.



        self.responseTimeCanvas.after(10, self.UpdateResponseTimeGraph)


mw = Tk()
mainUI = MainUI(mw)
mw.mainloop()

推荐答案

底层 tk 画布不重用或回收对象标识符.每当您创建新对象时,都会生成一个新标识符.这些对象的内存永远不会被回收.

The underlying tk canvas doesn't reuse or recycle object identifiers. Whenever you create a new object, a new identifier is generated. The memory of these objects is never reclaimed.

注意:这是嵌入式tcl解释器内部的内存,而不是python管理的内存.

Note: this is memory inside the embedded tcl interpreter, rather than memory managed by python.

解决方案是重新配置旧的、不再使用的元素,而不是删除它们并创建新元素.

The solution is to reconfigure old, no longer used elements rather than deleting them and creating new ones.

这篇关于Tkinter - 画布内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆