【问题标题】:What is causing this memory leak with Tkinter canvas?是什么导致 Tkinter 画布出现这种内存泄漏?
【发布时间】:2012-11-11 06:30:44
【问题描述】:

使用以下代码,内存使用量随着小行星图像在屏幕上移动而迅速增加,然后随着图像移动超出画布边缘而停止增加。谁能解释为什么会这样?我想在我的程序中让图像无限期地在屏幕上移动,但它最终会耗尽我系统上的所有内存。

我是 python、Tk 和 Tkinter 的新手。关于 canvas.move(...) 或 canvas.update() 有什么明显的我遗漏的吗?我应该使用不同的方法来完成这项任务吗?谢谢。

from Tkinter import Tk, Canvas, Frame, BOTH, NW
import Image 
import ImageTk
from random import random

root = Tk()
f = Frame(root)
f.pack(fill="both", expand=True)
canvas = Canvas(f, width=1000, height=1000)
canvas.pack(fill=BOTH, expand=1)

image = ImageTk.PhotoImage(Image.open("asteroid01.png"))
sprites = []
for i in range(10):
    sprites.append(canvas.create_image(50*random(), 50*random(), image=image))


vel = {'x': 1, 'y': 1}
while True:
    for s in sprites:
        canvas.move(s, vel['x'], vel['y'])
    canvas.update()

编辑:调用更新似乎是一种不好的做法,所以这里是按照建议更改的代码。但是,程序在图像移动时仍然会消耗内存,并且在窗口关闭之前不会释放它。

from Tkinter import Tk, Canvas, Frame, BOTH, NW
import Image 
import ImageTk
from random import random

root = Tk()
f = Frame(root)
f.pack(fill="both", expand=True)
canvas = Canvas(f, width=1000, height=1000)
canvas.pack(fill=BOTH, expand=1)

image = ImageTk.PhotoImage(Image.open("asteroid01.png"))
sprites = []
for i in range(10):
    sprites.append(canvas.create_image(50*random(), 50*random(), image=image))

vel = {'x': 1, 'y': 1}

def move():
    for s in sprites:
        canvas.move(s, vel['x'], vel['y'])
    canvas.after(10, move)

move()
root.mainloop()

【问题讨论】:

    标签: python image canvas memory-leaks tkinter


    【解决方案1】:

    我的猜测是因为您在紧密循环中调用更新。每次调用 update 都会有效地创建一个新的事件循环。这可能是消耗你记忆的原因。

    相反,将您的逻辑更改为如下所示(未经测试,但应该非常接近正确):

    def move():
        for s in sprites:
            canvas.move(s, vel['x'], vel['y'])
        canvas.after(10, move)
    move()
    

    您第一次调用 move,然后它会导致将来某个时候再次调用它自己。数字的大小(以毫秒为单位)决定了物体移动的速度。

    如果您希望能够停止动画,您可以设置一个标志,然后在每次调用after 之前检查该标志。

    【讨论】:

    • 我仍然需要在 move() 中调用 canvas.update(),对吧?这样做我仍然有内存泄漏,即使设置为 1000。你能解释一下“事件循环”是什么意思吗? update() 创建什么事件循环?我知道 root.mainloop() 将启动一个事件循环来寻找与窗口的交互。 canvas.update() 不只是重绘画布吗?
    • @user487100:没有。作为一般经验法则,您应该永远致电update。不,canvas.update() 只是更新画布。它创建了一个新的事件循环,就像mainloop 一样。事件循环只是一个内部函数,看起来像“while True: wait_for_event(); process_event()”。当您调用 mainloop() 时,它会执行此操作,而每次您调用 update() 时,它都会执行此操作,因此您最终会在循环内循环内循环...
    • 我没有意识到它会在没有我调用更新的情况下重绘。我仍然看到内存泄漏。我将更新我在问题中使用的代码。可以看看吗?
    • 是什么让你相信它有内存泄漏?您是否绝对确定上述代码和上述代码会给您带来内存泄漏?我用 python 2.7 在 OSX 上运行它,运行几个小时后没有发现内存泄漏。
    • 不,我不确定,但我认为这更有可能是程序员(我)的错误,而不是 Python 的错误。我也在 OSX (10.7.4) 上运行。我使用的是 python 2.7.1,当精灵在屏幕上移动时,程序增长了大约 20MB。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多