Tkinter自定义类中按钮无法显示图片的问题求助
问题
使用Python3 + Tkinter构建界面时,Window1包含三列控件:中间列布局独特,左右列布局一致仅内容不同。为实现复用,用Column类封装左右列,但Column类内的按钮无法显示图标,Window1内的按钮却正常显示。已尝试给图片绑定self但无效,修改Column类结构虽能解决问题,但会破坏列内控件的并排布局。需在保留现有类结构的前提下解决图标不显示的问题。
简化代码如下:
import tkinter as tk from tkinter import ttk """define basics""" class app(tk.Tk): def __init__(self): tk.Tk.__init__(self) self.state('zoomed') #open app in an expanded window #distribute 3 columns evenly for i in range(3): self.grid_columnconfigure(i, weight=1) #create a frame where more windows can be stacked if needed containerFrame = tk.Frame(self) containerFrame.grid() self.frames = {} #create window to place widgets frame = Window1(parent=containerFrame, window = self) self.frames["window1"] = frame #store frame in dict frame.grid(row=0, column=0, sticky="n") #place frame on the app window """define the window""" class Window1(tk.Frame): def __init__(self, parent, window): tk.Frame.__init__(self, parent) # create working frame columnLeft = Column(window) # create left column columnLeft.frame.grid(row=0, column=0) # place frame of left column columnLeft.place() # place widgets of the column columnRight = Column(window) # create left column columnRight.frame.grid(row=0, column=2) # place frame of left column columnRight.place() # place widgets of the column # this button is displayed with an icon self.iconDetail1 = (tk.PhotoImage(file=r"icons/detail.png")).subsample(10, 10) self.button1 = ttk.Button(window, image=self.iconDetail1, command=lambda: print("Yes")) self.button1.grid(row=0, column=1) """define the columns""" class Column(): def __init__(self, root): self.frame = tk.Frame() # create frame for placing widgets for i in range(3): # main column consists of 3 sub columns self.frame.grid_columnconfigure(i, weight=1) """define widgets""" #this button is placed right, but without an icon self.iconFolder2 = (tk.PhotoImage(file=r"icons/folder.png")).subsample(4, 4) #get image self.button2 = ttk.Button(self.frame, image=self.iconFolder2, command=lambda: print("no....")) #create button self.text = tk.Label(self.frame, text= "show text received from button") def place(self): #method to place widgets independently from creating an instance self.button2.grid(row=0, column=0, sticky=tk.W) self.text.grid(row=0, column=2, sticky=tk.E) if __name__ == "__main__": app = app() app.mainloop()
解决方案
问题根源
Tkinter的PhotoImage对象会被Python垃圾回收机制回收:
- Window1内的图标绑定在类实例属性
self.iconDetail1上,Window1实例存活期间该属性不会被回收,因此按钮图标正常显示。 - Column类的
self.iconFolder2虽绑定了图片,但Window1中创建的Column实例是局部变量(columnLeft/columnRight),__init__方法执行完毕后,这些局部变量会被销毁,Column实例被回收,连带PhotoImage也被回收,导致按钮无图标。 - 此外,Column类的
frame初始化时未指定父容器,默认绑定到主窗口,存在布局混乱的潜在问题。
修复步骤
- 在Window1中,将Column实例赋值给
self的属性,让Window1实例持有Column实例,避免其被垃圾回收。 - 给Column类的
frame指定父容器为传入的root,确保布局层级正确。
修改后的代码
import tkinter as tk from tkinter import ttk """define basics""" class app(tk.Tk): def __init__(self): tk.Tk.__init__(self) self.state('zoomed') #open app in an expanded window #distribute 3 columns evenly for i in range(3): self.grid_columnconfigure(i, weight=1) #create a frame where more windows can be stacked if needed containerFrame = tk.Frame(self) containerFrame.grid() self.frames = {} #create window to place widgets frame = Window1(parent=containerFrame, window = self) self.frames["window1"] = frame #store frame in dict frame.grid(row=0, column=0, sticky="n") #place frame on the app window """define the window""" class Window1(tk.Frame): def __init__(self, parent, window): tk.Frame.__init__(self, parent) # create working frame # 修改:将Column实例赋值给self的属性,避免被垃圾回收 self.columnLeft = Column(window) # create left column self.columnLeft.frame.grid(row=0, column=0) # place frame of left column self.columnLeft.place() # place widgets of the column self.columnRight = Column(window) # create right column self.columnRight.frame.grid(row=0, column=2) # place frame of right column self.columnRight.place() # place widgets of the column # this button is displayed with an icon self.iconDetail1 = (tk.PhotoImage(file=r"icons/detail.png")).subsample(10, 10) self.button1 = ttk.Button(window, image=self.iconDetail1, command=lambda: print("Yes")) self.button1.grid(row=0, column=1) """define the columns""" class Column(): def __init__(self, root): # 修改:指定frame的父容器为传入的root,确保布局层级正确 self.frame = tk.Frame(root) # create frame for placing widgets for i in range(3): # main column consists of 3 sub columns self.frame.grid_columnconfigure(i, weight=1) """define widgets""" #this button is placed right, but without an icon self.iconFolder2 = (tk.PhotoImage(file=r"icons/folder.png")).subsample(4, 4) #get image self.button2 = ttk.Button(self.frame, image=self.iconFolder2, command=lambda: print("no....")) #create button self.text = tk.Label(self.frame, text= "show text received from button") def place(self): #method to place widgets independently from creating an instance self.button2.grid(row=0, column=0, sticky=tk.W) self.text.grid(row=0, column=2, sticky=tk.E) if __name__ == "__main__": app = app() app.mainloop()
内容的提问来源于stack exchange,提问作者53B45T14N
相关产品推荐
相关产品推荐

