Tkinter Canvas多图显示异常求助:仅显示最后一张图片
问题分析与解决方案
让我来帮你拆解问题并给出解决办法:
为什么Canvas只显示最后一张图片?
你碰到的核心问题是Tkinter的PhotoImage对象被Python的垃圾回收机制清理了。在你的代码里,你用self.canvas.image_names = gif1来存储第一张图的引用,但之后又把这个变量重新赋值为gif2,这就导致gif1失去了唯一的Python引用——Tkinter的图像对象必须有一个持续存在的引用才能保持在Canvas上显示,否则即使你创建了图像,也会被自动销毁,所以第一张图的位置就只剩下空白了。
修复现有代码:保留所有图像引用
解决这个问题很简单,只要把每个PhotoImage对象都存储在一个不会被覆盖的容器里(比如你已经定义的self.list_images列表),确保每个图像都有独立的引用:
class open_child_window: def __init__(self, master): self.master = master master.title("Img") url1 ="http:img1" url2 ="http:img2" vscrollbar = Tk.Scrollbar(self.master,orient=VERTICAL) hscrollbar=Tk.Scrollbar(self.master,orient=HORIZONTAL) self.canvas= Tk.Canvas(self.master,background = "White",yscrollcommand=vscrollbar.set,xscrollcommand=hscrollbar.set) self.frame = Tk.Frame(self.canvas) vscrollbar.config(command=self.canvas.yview) vscrollbar.pack(side=Tk.RIGHT, fill=Tk.Y) hscrollbar.pack(side=Tk.BOTTOM,fill=Tk.X) hscrollbar.config(command=self.canvas.xview) self.canvas.create_window(0,0,window=self.frame, anchor='nw') self.list_images = [] # 用这个列表保存所有图像引用,避免被回收 # 处理第一张图片 contents = urllib.request.urlopen(url1) plt.imshow(plt.imread(contents)) file_name = "./img1.png" plt.savefig(file_name, format='png', dpi=180) gif1 = PhotoImage(file = file_name) self.list_images.append(gif1) # 将图像添加到列表,保留引用 self.canvas.create_image(50, 10, image = gif1, anchor = NW) # 处理第二张图片 contents = urllib.request.urlopen(url2) plt.imshow(plt.imread(contents)) file_name = "./img2.png" plt.savefig(file_name, format='png', dpi=180) gif2 = PhotoImage(file = file_name) self.list_images.append(gif2) # 同样添加到列表 self.canvas.create_image(600, 700, image = gif2, anchor = NW) self.master.update() self.canvas.config(scrollregion=self.canvas.bbox("all")) self.canvas.pack(side="top",fill='both',expand=True) self.frame.pack()
无需保存本地即可显示图片的方法
当然有更高效的方式!你可以借助Pillow(Python Imaging Library的分支)直接从URL读取图像字节流,转换成Tkinter可用的图像对象,完全跳过本地文件保存的步骤:
步骤说明:
- 先安装Pillow:
pip install pillow - 使用
urllib获取URL的字节数据,用PIL.Image.open()直接加载,再转成ImageTk.PhotoImage
完整代码:
from PIL import Image, ImageTk import urllib.request import tkinter as Tk import io # 用于处理字节流 class open_child_window: def __init__(self, master): self.master = master master.title("Img") url1 ="http:img1" url2 ="http:img2" vscrollbar = Tk.Scrollbar(self.master,orient=VERTICAL) hscrollbar=Tk.Scrollbar(self.master,orient=HORIZONTAL) self.canvas= Tk.Canvas(self.master,background = "White",yscrollcommand=vscrollbar.set,xscrollcommand=hscrollbar.set) self.frame = Tk.Frame(self.canvas) vscrollbar.config(command=self.canvas.yview) vscrollbar.pack(side=Tk.RIGHT, fill=Tk.Y) hscrollbar.pack(side=Tk.BOTTOM,fill=Tk.X) hscrollbar.config(command=self.canvas.xview) self.canvas.create_window(0,0,window=self.frame, anchor='nw') self.list_images = [] # 依然需要保留图像引用 # 加载并显示第一张图(无需保存本地) with urllib.request.urlopen(url1) as response: img_bytes = response.read() pil_img1 = Image.open(io.BytesIO(img_bytes)) tk_img1 = ImageTk.PhotoImage(pil_img1) self.list_images.append(tk_img1) self.canvas.create_image(50, 10, image=tk_img1, anchor=Tk.NW) # 加载并显示第二张图(无需保存本地) with urllib.request.urlopen(url2) as response: img_bytes = response.read() pil_img2 = Image.open(io.BytesIO(img_bytes)) tk_img2 = ImageTk.PhotoImage(pil_img2) self.list_images.append(tk_img2) self.canvas.create_image(600, 700, image=tk_img2, anchor=Tk.NW) self.master.update() self.canvas.config(scrollregion=self.canvas.bbox("all")) self.canvas.pack(side="top",fill='both',expand=True) self.frame.pack()
这个方法不仅省去了本地文件的读写开销,还避免了plt.savefig可能带来的格式、dpi适配问题,运行效率更高。
内容的提问来源于stack exchange,提问作者vas
相关产品推荐
相关产品推荐

