Tkinter图像无Matplotlib时无法显示问题排查
问题:Tkinter Canvas显示numpy生成图像异常,依赖Matplotlib才能临时显示
我通过numpy生成图像数组,想在Tkinter的Canvas组件中显示。用Matplotlib调试时,打开Matplotlib窗口能正常显示图像,但关闭该窗口后,Canvas里的图像就变成全白。如果完全删掉Matplotlib相关代码,图像直接一直是全白,根本显示不出来。
现象截图
- 双窗口打开时正常显示:

- 关闭Matplotlib窗口后全白:

原代码
from skimage.draw import polygon from PIL import Image, ImageTk import matplotlib.pyplot as plt import tkinter as tk import numpy as np def generate(): # clear canvas canvas.delete("all") # make clean array img = np.ones((width, height, 3), dtype=np.float32) * 0.75 # randomly generate positions x_generated = np.random.uniform(0, width, 5) y_generated = np.random.uniform(0, height, 5) for x, y in zip(x_generated, y_generated): base_height = 70 base_width = 70 # generate the corners x0 = x - base_width/2 x1 = x + base_width/2 y0 = y - base_height/2 y1 = y + base_height/2 # create rectangle poly = np.array(( (x0, y0), (x1, y0), (x1, y1), (x0, y1), )) rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape) img[rr, cc, :] = 0.5 # # canvas method. I don't want to use this! # canvas.create_rectangle(x0, y0, x1, y1, fill="red", outline="") # display generated image in tkinter canvas scaled_to_8bit = img * 255 newarr = scaled_to_8bit.astype(np.uint8) imgarray = ImageTk.PhotoImage(image=Image.fromarray(newarr, 'RGB')) # create the canvas image object from the numpy array canvas.create_image(20, 20, anchor="nw", image=imgarray) # display pixel data for debugging plt.imshow(img) plt.show() if __name__ == "__main__": width = 1000 height = 1000 # Create a root window root = tk.Tk() root.configure(background="green") # Create a canvas widget canvas = tk.Canvas(root, width=width, height=height) canvas.pack() # Create a button widget button = tk.Button(root, text="Generate", command=generate) button.pack() # start main tk loop root.mainloop()
问题原因与解决方法
核心原因
ImageTk.PhotoImage创建的对象imgarray是局部变量,函数执行完毕后会被Python垃圾回收机制回收。之前Matplotlib的plt.show()会阻塞函数执行,暂时保留了这个对象;一旦关闭Matplotlib窗口,函数执行完成,imgarray被回收,Canvas找不到图像源就会变成全白。
修复代码
给imgarray添加持久引用,比如绑定到Canvas对象的属性上,避免被垃圾回收。修改后的代码如下:
from skimage.draw import polygon from PIL import Image, ImageTk import matplotlib.pyplot as plt import tkinter as tk import numpy as np def generate(): # clear canvas canvas.delete("all") # make clean array img = np.ones((width, height, 3), dtype=np.float32) * 0.75 # randomly generate positions x_generated = np.random.uniform(0, width, 5) y_generated = np.random.uniform(0, height, 5) for x, y in zip(x_generated, y_generated): base_height = 70 base_width = 70 # generate the corners x0 = x - base_width/2 x1 = x + base_width/2 y0 = y - base_height/2 y1 = y + base_height/2 # create rectangle poly = np.array(( (x0, y0), (x1, y0), (x1, y1), (x0, y1), )) rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape) img[rr, cc, :] = 0.5 # # canvas method. I don't want to use this! # canvas.create_rectangle(x0, y0, x1, y1, fill="red", outline="") # display generated image in tkinter canvas scaled_to_8bit = img * 255 newarr = scaled_to_8bit.astype(np.uint8) imgarray = ImageTk.PhotoImage(image=Image.fromarray(newarr, 'RGB')) # 为图像对象添加持久引用,避免被垃圾回收 canvas.image = imgarray # create the canvas image object from the numpy array canvas.create_image(20, 20, anchor="nw", image=imgarray) # 可选:保留Matplotlib调试代码,或直接删除 # plt.imshow(img) # plt.show() if __name__ == "__main__": width = 1000 height = 1000 # Create a root window root = tk.Tk() root.configure(background="green") # Create a canvas widget canvas = tk.Canvas(root, width=width, height=height) canvas.pack() # Create a button widget button = tk.Button(root, text="Generate", command=generate) button.pack() # start main tk loop root.mainloop()
关键修改点
添加canvas.image = imgarray这一行,将图像对象绑定到Canvas的属性上,确保函数执行完成后对象不会被立即回收,Canvas就能持续显示图像。即使删除所有Matplotlib相关代码,图像也能正常显示。
内容的提问来源于stack exchange,提问作者Gordon13
相关产品推荐
相关产品推荐

