在Python中重构(在Eclipse中提取新方法)

时间:2013-09-06 20:19:19

标签: python eclipse

我正在尝试将一些 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()   

感谢。

2 个答案:

答案 0 :(得分:2)

问题是你过早发布PhotoImage对象。

正如the docs解释:

  

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

     

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

     

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

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

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

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

global photo1

...或在label1 = Label(image=photo1)行之后添加此内容:

label1.photo = photo1

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

即使是these examples中显示的普通版本也可以使用:

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设计,你的对象实际上代表了某些东西,如the official examples

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

答案 1 :(得分:0)

一种解决方案是在extractedMethod()

的开头添加一行
def extractedMethod(root):
    global photo1  # added
    ...

问题在于,否则photo1是一个局部变量,一旦函数返回就会被丢弃,因此函数中对它的引用变得无效。

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

中的错误