You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Tkinter打开新窗口时的pyimage不存在错误

问题

使用Tkinter开发多窗口Python程序,为每个窗口设置图片页眉。第一个窗口图片显示正常,但点击“Explore Flights”按钮打开第二个窗口时,图片无法加载,报错:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/__init__.py", line 1921, in __call__
    return self.func(*args)
  File "<ipython-input-3-840b8d9f47f9>", line 114, in flights_menu
    bg_label = tk.Label(flights_men_win, image=bg_image)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/__init__.py", line 3177, in __init__
    Widget.__init__(self, master, 'label', cnf, kw)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tkinter/__init__.py", line 2601, in __init__
    self.tk.call(
_tkinter.TclError: image "pyimage14" doesn't exist

试过用PIL导入图片但未解决,怀疑是图片被垃圾回收导致错误。

完整代码

import tkinter  as tk

from PIL import Image, ImageTk

from tkinter import Label, messagebox, PhotoImage, ttk

class Application:
    def __init__(self, main_window):
        self.main_window = main_window
        self.main_window.resizable(False, False)

        

        ## Login Window
        self.main_window.title("Login")
        self.main_window.geometry("300x150")
        self.main_window.iconbitmap('login_icon.png')

        self.label_usrname = tk.Label(self.main_window, text = "Username")
        self.label_passwrd = tk.Label(self.main_window, text = "Password")

        self.entry_usrname = tk.Entry(self.main_window)
        self.entry_passwrd = tk.Entry(self.main_window, show="*")

        self.login_button = tk.Button(self.main_window, text = "Login", command=self.login_button_clicked)
        self.exit_button = tk.Button(self.main_window, text = "Exit", command=self.main_window.quit())


        self.label_usrname.grid(row=0, sticky='e', padx=10, pady=10)
        self.label_passwrd.grid(row=1, sticky='e', padx=10, pady=10)
        self.entry_usrname.grid(row=0, column=1, padx=10, pady=10)
        self.entry_passwrd.grid(row=1, column=1, padx=10, pady=10)
        self.login_button.grid(row=2, column=0, padx=10, pady=10)
        self.exit_button.grid(row=2, column=1, padx=10, pady=10)


    def login_button_clicked(self):
        username = self.entry_usrname.get()
        password = self.entry_passwrd.get()

        if username == "admin" and password == "password":
            self.main_window.destroy()
            self.agency_window()

        else:
            messagebox.showerror("Login Error", "Please enter the correct username and password")


    def agency_window(self):
        agency_win = tk.Tk()
        
        # Set the title and size of the main window
        agency_win.title("Prog2 Travel Agency")
        agency_win.geometry("600x280")
        agency_win.configure(bg="white")
        agency_win.resizable(False,False)
        
        img = tk.Image("photo", file="agency_icon.png")
        agency_win.iconphoto(True, img)
        #agency_win.tk.call('wm','iconphoto', root._w, img)


        # Background image (You can replace 'background.jpg' with your actual image path)
        bg_image = tk.PhotoImage(file="agency.png")
        bg_label = tk.Label(agency_win, image=bg_image)
        bg_label.pack(fill=tk.X)

        # Welcome message label
        welcome_label = tk.Label(agency_win, text="Hi Davey, welcome to the Prog2 Travel Agency", bg="white", fg="DeepSkyBlue2", font=("Arial", 20, "bold"), pady=20)
        welcome_label.pack(fill=tk.X)

        # Button styles using ttk (themed tk)
        style = ttk.Style()
        layout = style.layout('TButton')
        style.layout('Blue.TButton', layout)
        style.configure("Blue.TButton", font=("Arial", 20, "bold"), padding=5, background="DeepSkyBlue2", foreground="white")

        # Frame to hold buttons
        button_frame = tk.Frame(agency_win, background="DeepSkyBlue2")
        button_frame.pack(side=tk.BOTTOM, fill=tk.X)


        # Explore Flights button
        explore_flights_btn = ttk.Button(button_frame, text="Explore Flights", command=self.flights_menu)
        explore_flights_btn.grid(row=0, column=0, padx=5)

        # Explore Destinations button
        explore_destinations_btn = ttk.Button(button_frame, text="Explore Destinations", command=self.destinations_menu)
        explore_destinations_btn.grid(row=0, column=1, padx=5)

        # Book a Trip button
        book_trip_btn = ttk.Button(button_frame, text="Book a Trip", command=self.trips_menu)
        book_trip_btn.grid(row=0, column=2, padx=5)

        # Exit button
        exit_btn = ttk.Button(button_frame, text="Exit", command= agency_win.quit())
        exit_btn.grid(row=0, column=3, padx=5)

        agency_win.mainloop()

    ### FLIGHTS MENU   
    def flights_menu(self):
        flights_men_win = tk.Tk()
        
        # Set the title and size of the main window
        flights_men_win.title("Explore Flights")
        flights_men_win.geometry("600x400") # Adjusted the size to fit the design
        flights_men_win.configure(bg="white")
        flights_men_win.resizable(False, False)
        
        # Header image
        bg_image = tk.PhotoImage(file="flight.png")
        bg_label = tk.Label(flights_men_win, image=bg_image)
        bg_label.pack(fill=tk.X)


        # Welcome message label
        welcome_label = Label(flights_men_win, text="Hi Davey, welcome to the Flights section", bg="white", fg="DeepSkyBlue2", font=("Arial", 18))
        welcome_label.place(x=140, y=70) 

        # Button Frame
        button_frame = tk.Frame(flights_men_win, bg="white")
        button_frame.place(x=200, y=220) 
        
        # Buttons inside the frame
        view_all_flights_btn = ttk.Button(button_frame, text="View All Flights", command=self.view_flights)
        view_all_flights_btn.grid(row=0, column=0, padx=5, pady=5)

        view_flights_by_country_btn = ttk.Button(button_frame, text="View Flights by Country", command=self.view_flights_filtered)
        view_flights_by_country_btn.grid(row=0, column=1, padx=5, pady=5)

        add_flight_btn = ttk.Button(button_frame, text="Add Flight", command=self.add_flight)
        add_flight_btn.grid(row=0, column=2, padx=5, pady=5)

        remove_flight_btn = ttk.Button(button_frame, text="Remove Flight", command=self.remove_flight)
        remove_flight_btn.grid(row=1, column=0, padx=5, pady=5)

        close_btn = ttk.Button(button_frame, text="Close", command=flights_men_win.destroy) # Using destroy to close the window
        close_btn.grid(row=1, column=1, padx=5, pady=5, columnspan=2) # Spanning two columns for the close button

        flights_men_win.mainloop()

    def view_flights(self):
        pass

    def view_flights_filtered(self):
        pass

    def add_flight(self):
        pass

    def remove_flight(self):
        pass

解决方案

问题根源有两个:

  1. 多Tk实例冲突:Tkinter程序应仅存在一个tk.Tk()主窗口实例,后续窗口需用tk.Toplevel()创建,否则会导致Tcl解释器资源冲突,引发图片ID错误。
  2. 图片对象被垃圾回收:PhotoImage对象如果没有持久引用,会被Python垃圾回收机制销毁,导致Tkinter无法找到图片资源。

修改步骤:

1. 替换多窗口创建方式

将flights_menu中的tk.Tk()改为tk.Toplevel(),并移除多余的mainloop()调用:

def flights_menu(self):
    # 用Toplevel创建子窗口,而非新的Tk实例
    flights_men_win = tk.Toplevel()
    
    flights_men_win.title("Explore Flights")
    flights_men_win.geometry("600x400")
    flights_men_win.configure(bg="white")
    flights_men_win.resizable(False, False)
    
    # Header image
    bg_image = tk.PhotoImage(file="flight.png")
    bg_label = tk.Label(flights_men_win, image=bg_image)
    # 保留图片引用,避免被GC回收
    bg_label.image = bg_image
    bg_label.pack(fill=tk.X)

    # ... 其余代码不变 ...
    
    # 移除该行,主窗口已在运行mainloop
    # flights_men_win.mainloop()

2. 保留图片对象引用

在所有创建PhotoImage的位置,将图片对象绑定到对应Label的属性上:

  • 在agency_window中:
bg_image = tk.PhotoImage(file="agency.png")
bg_label = tk.Label(agency_win, image=bg_image)
# 添加该行保留引用
bg_label.image = bg_image
bg_label.pack(fill=tk.X)

3. 修正按钮command参数错误

代码中部分按钮的command直接调用了quit(),会导致程序启动时就执行退出操作,需改为传递函数引用:

# 原代码
self.exit_button = tk.Button(self.main_window, text = "Exit", command=self.main_window.quit())
# 修改后
self.exit_button = tk.Button(self.main_window, text = "Exit", command=self.main_window.quit)

# 原代码
exit_btn = ttk.Button(button_frame, text="Exit", command= agency_win.quit())
# 修改后
exit_btn = ttk.Button(button_frame, text="Exit", command= agency_win.quit)

总结

通过以上修改,既解决了多窗口Tcl解释器冲突问题,又避免了图片对象被垃圾回收,可彻底解决图片加载失败的错误。

内容的提问来源于stack exchange,提问作者kenzie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:27:04