Itemconfigure() 和 tkinter 的内存泄漏
Itemconfigure() and memory leak with tkinter
我的代码(见下文)应该显示一个 start/stop 按钮,一个 select 可视化速度的缩放按钮和一个 canvas 具有许多随机变化的矩形随时间变色。
当我运行这段代码时,内存使用量随时间急剧增加(如果你运行,你需要将速度提高到10左右才能更容易看到)。在工作中(在 Windows 7 工作站上),我首先测试了它,几分钟后它变得基本上无法使用(它变得非常非常慢),而在我的 Mac 笔记本电脑上它可以存活更长的时间,尽管内存使用量稳步增加。
在寻找罪魁祸首之后,我遇到了多个线程,包括来自 Tk Toolkit 的 this one,可追溯到 2010 年,他们提到 itemconfigure()
在使用时存在问题改变颜色,这正是我正在做的。
在 self.run_InfiniteT_MC()
函数中注释 "self.update_canvas()"
函数解决了我所看到的问题并且似乎同意 itemconfigure()
改变颜色可能仍然有问题的诊断.
请注意,我还尝试通过命令 "self.canvas.delete(self.rect[i])" 删除不断变化的矩形,然后重新创建它们,但这根本不会改变我的内存问题。
我也曾尝试通过 "self.canvas.destroy()"
破坏整个 canvas 并在每次需要更新图像时从头开始重新创建所有内容,但同样未能解决我的内存问题。
有什么办法可以在不更改整个代码的情况下解决这个内存问题(这里只是其中的一小部分)?
编辑:正确缩进命令 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
来更改颜色。一种解决方案是为每个位置创建四个矩形,每种颜色一个。然后您可以修改堆叠顺序以使您想要的颜色位于顶部,这将隐藏其他颜色。
显然,此解决方案需要四倍于 canvas 对象的数量,但除非您要绘制数十万个对象,否则这无关紧要。 canvas 可以很好地处理数万个对象。
我认为问题出在对 self.after
的调用上。每次开始模拟时,都会创建一个 new 无限循环(永远不会停止)。使用 self.after
缩进行使其位于 if
语句内应该可以解决您的问题,因为这样一个循环将在 self.do_run
变为 False
.[=15 时终止=]
我的代码(见下文)应该显示一个 start/stop 按钮,一个 select 可视化速度的缩放按钮和一个 canvas 具有许多随机变化的矩形随时间变色。
当我运行这段代码时,内存使用量随时间急剧增加(如果你运行,你需要将速度提高到10左右才能更容易看到)。在工作中(在 Windows 7 工作站上),我首先测试了它,几分钟后它变得基本上无法使用(它变得非常非常慢),而在我的 Mac 笔记本电脑上它可以存活更长的时间,尽管内存使用量稳步增加。
在寻找罪魁祸首之后,我遇到了多个线程,包括来自 Tk Toolkit 的 this one,可追溯到 2010 年,他们提到 itemconfigure()
在使用时存在问题改变颜色,这正是我正在做的。
在 self.run_InfiniteT_MC()
函数中注释 "self.update_canvas()"
函数解决了我所看到的问题并且似乎同意 itemconfigure()
改变颜色可能仍然有问题的诊断.
请注意,我还尝试通过命令 "self.canvas.delete(self.rect[i])" 删除不断变化的矩形,然后重新创建它们,但这根本不会改变我的内存问题。
我也曾尝试通过 "self.canvas.destroy()"
破坏整个 canvas 并在每次需要更新图像时从头开始重新创建所有内容,但同样未能解决我的内存问题。
有什么办法可以在不更改整个代码的情况下解决这个内存问题(这里只是其中的一小部分)?
编辑:正确缩进命令 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
来更改颜色。一种解决方案是为每个位置创建四个矩形,每种颜色一个。然后您可以修改堆叠顺序以使您想要的颜色位于顶部,这将隐藏其他颜色。
显然,此解决方案需要四倍于 canvas 对象的数量,但除非您要绘制数十万个对象,否则这无关紧要。 canvas 可以很好地处理数万个对象。
我认为问题出在对 self.after
的调用上。每次开始模拟时,都会创建一个 new 无限循环(永远不会停止)。使用 self.after
缩进行使其位于 if
语句内应该可以解决您的问题,因为这样一个循环将在 self.do_run
变为 False
.[=15 时终止=]