1

我正在尝试将一些Python 2.7代码重构为 Eclipse 中的新方法。在下面注释标记的块上使用 Eclipse 后Refactoring->Extract method,我的图像不再显示在 GUI 中:

from Tkinter import Tk, Button, W, E, N, S, Label, Frame

from PIL import Image, ImageTk

def myCallback():
    pass

root = Tk()

# start refactor method here

root.geometry('400x400')
runButton = Button(root, text="Run",bg='green',
    command=myCallback)
runButton.grid(row=2, column=0,
        padx=10, pady=10)
quitButton = Button(root, text="Quit",
command=root.quit)
quitButton.grid(row=2, column=1,
        padx=10, pady=10)

frame1 = Frame(width=200, height=200)
frame1.grid(row=1, column=0, columnspan=1, rowspan=1,
        sticky=W+E+N+S, padx=10, pady=10)
image1 = Image.open("C:/Users/me/Pictures/house.jpg")
size = 64,64
image1.thumbnail(size, Image.ANTIALIAS)
photo1 = ImageTk.PhotoImage(image1) 
label1 = Label(image=photo1) 
label1.grid(row=0, column=10, columnspan=1, rowspan=1,
        sticky=N+E, padx=10, pady=10)

# end refactor method here

root.mainloop()   

有人可以解释为什么图像消失并提出一个解决方案,以便我可以在不丢失图像的情况下进行重构吗?

重构后:

from Tkinter import Tk, Button, W, E, N, S, Label, Frame

from PIL import Image, ImageTk

def extractedMethod(root):
    root.geometry('400x400')
    runButton = Button(root, text="Run", bg='green', command=myCallback)
    runButton.grid(row=2, column=0, padx=10, pady=10)
    quitButton = Button(root, text="Quit", 
    command=root.quit)
    quitButton.grid(row=2, column=1, padx=10, pady=10)
    frame1 = Frame(width=200, height=200)
    frame1.grid(row=1, column=0, columnspan=1, rowspan=1, sticky=W + E + N + S, padx=10, pady=10)
    image1 = Image.open("C:/Users/me/Pictures/house.jpg")
    size = 64, 64
    image1.thumbnail(size, Image.ANTIALIAS)
    photo1 = ImageTk.PhotoImage(image1)
    label1 = Label(image=photo1)
    label1.grid(row=0, column=10, columnspan=1, rowspan=1, sticky=N + E, padx=10, pady=10)

def myCallback():
    pass

root = Tk()
extractedMethod(root)

root.mainloop()   

谢谢。

4

2 回答 2

2

问题是您PhotoImage过早地释放对象。

正如文档解释的那样:

您必须在 Python 程序中保留对图像对象的引用,方法是将其存储在全局变量中,或者将其附加到另一个对象。

注意:当 Python 对 PhotoImage 对象进行垃圾收集时(例如,当您从将图像存储在局部变量中的函数返回时),即使 Tkinter 小部件正在显示该图像,它也会被清除。

为避免这种情况,程序必须保留对图像对象的额外引用。一种简单的方法是将图像分配给小部件属性,如下所示:

label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()

您可能认为将它作为image参数传递给Label构造函数会使它保持活动状态。但事实并非如此;Label实际上并没有保留对对象本身的引用,PhotoImage它只是在启动后运行的 Tcl 代码中的实际标签构造才mainloop这样做。(它类似于弱引用,但不是有意或明确的,如果有帮助的话。)

因此,快速而肮脏的解决方案是将其添加到顶部extractedMethod

global photo1

…或者在该label1 = Label(image=photo1)行之后添加:

label1.photo = photo1

避免这种情况的更好方法是使用 OO 设计而不是平面程序设计,这样您就有了一些合理的对象来拥有所有引用。

即使是简单的版本,如这些示例中所示,也可以工作:

class Foo(object):
    def __init__(self, root):
        # put the code from extractedMethod here,
        # but prefix every local variable with self,
        # and do the same for myCallback.
    def myCallback(self):
        pass

但是您可能最好使用真正的 OO 设计,其中您的对象实际上代表了某些东西,如官方示例中所示:

class MyFrame(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.create_widgets()
    def myCallback(self):
        pass
    def create_widgets(self):
        # put the code from extractedMethod here,
        # again prefixing things with self
于 2013-09-06T21:24:40.527 回答
0

一种解决方案是在 的开头添加一行extractedMethod()

def extractedMethod(root):
    global photo1  # added
    ...

问题是 elsephoto1是一个局部变量,一旦函数返回就会被丢弃,所以在函数中对它的引用变得无效。

有人可能认为该label1 = Label(image=photo1)语句会增加它的引用计数并阻止这种情况发生,但它显然不是那样工作的。我以前见过这个,个人怀疑这是由于Tkinter...

于 2013-09-06T21:53:40.450 回答