我正在尝试将一些 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()
感谢。
答案 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
...