繁体   English   中英

使用tkinter进行Itemconfigure()和内存泄漏

[英]Itemconfigure() and memory leak with tkinter

我的代码(见下文)应该显示一个开始/停止按钮,一个用于选择可视化速度的缩放按钮和一个带有许多矩形的画布,这些矩形随时间随机改变颜色。

当我运行这段代码时,内存使用量会随着时间的推移而急剧增加(如果你运行,你需要将速度提高到10左右才能更容易看到它)。 在工作中(在Windows 7工作站上),我首先测试它,几分钟后基本上无法使用(它变得非常慢),而在我的Mac笔记本电脑上,虽然内存使用量稳步增加,但它能够存活一段时间。

在寻找罪魁祸首之后,我遇到了多个线程,包括Tk Toolkit中的这一个 ,可以追溯到2010年,他们提到itemconfigure()在用于改变颜色时出现问题,这正是我的意思。这样做。

"self.update_canvas()"函数中注释"self.update_canvas()"函数可以解决我self.run_InfiniteT_MC()的问题,并且似乎同意itemconfigure()更改颜色的诊断可能仍然存在问题。

请注意,我还尝试通过命令“self.canvas.delete(self.rect [i])”删除更改的矩形,然后重新创建它们,但这根本不会改变我的内存问题。

我还尝试通过"self.canvas.destroy()"来破坏整个画布,并且每次需要更新图像时从头开始重新创建所有内容但是再次无法解决我的内存问题。

有什么办法可以解决这个内存问题而不需要改变我的整个代码(这里只是它的一小部分)?

编辑:正确缩进命令self.after ,问题消失了; 所以itemconfigure()命令根本没有责任,至少不是这个问题。

from tkinter import *
from numpy import *
from random import randint

class Application(Frame):
    def __init__(self,master):
        Frame.__init__(self,master)
        self.columnconfigure(0, pad = 10)
        self.grid()
        self.count = 0
        self.create_widgets()


def create_widgets(self):
        "create an array of cells all initiated with the same value"
        self.nx = 70
        self.ny = self.nx
        self.ntot = self.nx*self.ny
        self.state = [0 for x in range(self.ntot)]
        for x in range(self.ntot):
            self.state[x] = 0 #0 is down, 1 is right, 2 is up, 3 is left ...modulo 4
        "create a scale button to choose speed of dynamics"
        self.ScaleSpeedVar = IntVar
        self.ScaleSpeed = Scale(self, from_=1, to =20, orient = HORIZONTAL, label = "SimuSpeed", variable = self.ScaleSpeedVar, font =('Helvetica','18'))
        self.ScaleSpeed.grid()
        self.ScaleSpeed.set(1)

"create a button that starts/stops the dynamics"
        self.do_run = False
        self.startclick = True
        self.buttonStartStop = Button(self, text = "Start/Stop", font =('Helvetica','18'))
        self.buttonStartStop["command"] = self.start_stop_simu
        self.buttonStartStop.grid()
"create a big canva to contain the simulation cells"
        self.size = 500
        self.canvas = Canvas(self, width=self.size, height=self.size, bg ="red")
        self.canvas.grid()

        self.width = 1
        self.rect = [0 for x in range(self.ntot)]
        for i in range(self.ntot):
            self.rectsize = self.size/self.nx
            self.rect[i] = self.canvas.create_rectangle((i%(self.nx))*self.rectsize, self.rectsize*(i//self.nx), (i%(self.nx))*self.rectsize+self.rectsize, self.rectsize*(i//self.nx)+self.rectsize, fill="red", tag = i, width = self.width)

def start_stop_simu(self):
    if self.startclick:
        self.start_simu()
        self.startclick = False
    else :
        self.stop_simu()
        self.startclick = True

def start_simu(self):
    self.do_run = True
    self.run_InfiniteT_MC()

def stop_simu(self):
    self.do_run = False

def run_InfiniteT_MC(self):
    if self.do_run:
        self.simuspeed = pow(2,self.ScaleSpeed.get())
        for i in range(self.simuspeed):
            self.cellID = randint(0,self.ntot-1)
            self.angle = 2*randint(0,1)-1
            self.state[self.cellID] = (self.state[self.cellID]+self.angle)%4
        self.update_canvas()
    self.after(1, self.run_InfiniteT_MC)

def update_canvas(self):
    for i in range(self.ntot):
        if self.state[i] == 0:
            self.canvas.itemconfig(self.rect[i], fill = "red")
        if self.state[i] == 2:
            self.canvas.itemconfig(self.rect[i], fill = "blue")
        if self.state[i] == 1:
            self.canvas.itemconfig(self.rect[i], fill = "green")
        if self.state[i] == 3:
            self.canvas.itemconfig(self.rect[i], fill = "yellow")
    self.canvas.update_idletasks()


root = Tk()
root.title("Problematic code")
root.geometry("800x600")
app = Application(root)

root.mainloop()

如果在分配颜色时问题是tk核心中的内存泄漏,那么唯一的解决方法是避免使用itemconfigure来更改颜色。 一种解决方案是为每个位置创建四个矩形,每种颜色中的一个。 然后,您可以修改堆叠顺序,使其具有所需的颜色,这将隐藏其他颜色。

显然,这个解决方案需要画布对象数量的四倍,但除非你要绘制数十万个,否则无关紧要。 画布可以很好地处理数万个对象。

我认为问题在于对self.after 每次开始模拟时,都会创建一个新的无限循环(永远不会停止)。 使用self.after以使其在if语句中应解决您的问题,因为当self.do_run变为False时,此循环将终止。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM