Matplotlib与Tkinter窗口图标替换问题:无法同时修改双窗口图标
解决Matplotlib与Tkinter窗口同时设置GIF图标冲突的问题
问题场景
尝试用GIF图片替换Matplotlib主窗口(plt.show())和Tkinter窗口的默认图标:
- Matplotlib窗口图标替换成功
- 点击Matplotlib上的按钮打开Tkinter窗口时,设置图标报错
_tkinter.TclError: can't use "pyimage19" as iconphoto: not a photo image - 不替换Matplotlib窗口图标时,Tkinter窗口图标可正常修改,但无法同时完成两个窗口的图标替换
- 试过加载不同图片、用
fenetre.tk.call("wm","iconphoto",fenetre._w,tk.PhotoImage(file="accueil.gif"))命令,错误依旧
原代码:
import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt import tkinter as tk from PIL import Image,ImageTk def ouvrir_fenetre(event): global fenetre try:fenetre.destroy() except:pass fenetre = tk.Tk() fenetre.title("Fenêtre Tkinter") ## fenetre.tk.call("wm","iconphoto",fenetre._w,tk.PhotoImage(file="accueil.gif")) image_tkinter = Image.open('accueil.gif') photo_tkinter = ImageTk.PhotoImage(image_tkinter) # Error: "_tkinter.TclError: can't use "pyimage19" as iconphoto: not a photo image" fenetre.iconphoto(False, photo_tkinter) fenetre.mainloop() fig, ax = plt.subplots() manager = plt.get_current_fig_manager() window = manager.window image_matplotlib = Image.open('accueil.gif') photo_matplotlib = ImageTk.PhotoImage(image_matplotlib) window.iconphoto(False, photo_matplotlib) button = plt.axes([0.9, 0, 0.1, 0.05]) btn = plt.Button(button, 'Tkinter') btn.on_clicked(ouvrir_fenetre) plt.show()
问题根源
- 使用
TkAgg后端时,Matplotlib会先创建一个隐藏的Tk根窗口,之后手动调用tk.Tk()会生成第二个独立的Tk实例,两个实例的PhotoImage对象无法跨上下文共用,导致图标设置失败 ImageTk.PhotoImage对象如果没有全局引用,会被Python垃圾回收机制回收,也可能引发类似错误
解决方案
- 把Tkinter窗口改为
tk.Toplevel(),复用Matplotlib创建的Tk根上下文,避免多实例冲突 - 为PhotoImage对象添加全局引用,防止被回收
修正后的代码:
import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt import tkinter as tk from PIL import Image, ImageTk # 全局变量存储PhotoImage,防止被垃圾回收 photo_tkinter = None def ouvrir_fenetre(event): global fenetre, photo_tkinter try: fenetre.destroy() except: pass # 使用Toplevel而非Tk,复用Matplotlib的Tk根窗口 fenetre = tk.Toplevel() fenetre.title("Fenêtre Tkinter") # 加载并设置图标 image_tkinter = Image.open('accueil.gif') photo_tkinter = ImageTk.PhotoImage(image_tkinter) fenetre.iconphoto(False, photo_tkinter) fenetre.mainloop() fig, ax = plt.subplots() manager = plt.get_current_fig_manager() window = manager.window image_matplotlib = Image.open('accueil.gif') photo_matplotlib = ImageTk.PhotoImage(image_matplotlib) window.iconphoto(False, photo_matplotlib) button = plt.axes([0.9, 0, 0.1, 0.05]) btn = plt.Button(button, 'Tkinter') btn.on_clicked(ouvrir_fenetre) plt.show()
补充说明
Toplevel()是依附于已有根窗口的子窗口,和Matplotlib窗口共享同一个Tk上下文,解决了跨实例的PhotoImage兼容性问题- 将
photo_tkinter设为全局变量,避免函数执行完毕后被回收,保证图标能正常显示
内容的提问来源于stack exchange,提问作者Bast38
相关产品推荐
相关产品推荐

