使用Pillow库添加水印时遭遇AttributeError问题求助
问题分析与解决方案
错误原因
textsize方法已被移除:Pillow 9.1.0及以上版本中,ImageDraw对象的textsize()方法被弃用并移除,导致触发AttributeError。- 图像对象被错误覆盖:在
upload_image方法中,你将原始PIL Image对象直接替换为了ImageTk.PhotoImage对象,后续无法对原始图像进行水印合成操作(PhotoImage是tkinter专用格式,不支持PIL的图像合成方法)。
修正后的完整代码
import tkinter as tk from tkinter import filedialog from PIL import Image, ImageTk, ImageDraw, ImageFont class WatermarkApp: def __init__(self, root): self.root = root self.root.title("Watermark App") # 创建控件 self.upload_button = tk.Button(root, text="Upload Image", command=self.upload_image) self.upload_button.pack() self.watermark_text_entry = tk.Entry(root) self.watermark_text_entry.pack() self.add_watermark_button = tk.Button(root, text="Add Watermark", command=self.add_watermark) self.add_watermark_button.pack() # 初始化变量 self.image_path = None self.original_image = None # 存储原始PIL图像对象 self.display_image = None # 存储用于GUI显示的PhotoImage对象 self.image_label = None def upload_image(self): self.image_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png *.jpg *.jpeg *.gif *.bmp")]) if self.image_path: self.original_image = Image.open(self.image_path) # 复制图像用于显示,避免修改原始图像 display_copy = self.original_image.copy() display_copy.thumbnail((400, 400)) # 调整尺寸适配GUI # 在GUI上显示图像 self.display_image = ImageTk.PhotoImage(display_copy) if self.image_label: self.image_label.destroy() self.image_label = tk.Label(root, image=self.display_image) self.image_label.pack() def add_watermark(self): if self.image_path and self.original_image: watermark_text = self.watermark_text_entry.get() if watermark_text: # 确保图像为RGBA格式,支持透明合成 if self.original_image.mode != "RGBA": base_image = self.original_image.convert("RGBA") else: base_image = self.original_image.copy() watermark = Image.new("RGBA", base_image.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(watermark) try: font = ImageFont.truetype("arialbd.ttf", 36) # 可按需更换字体 except IOError: # 若指定字体不存在,使用默认字体 font = ImageFont.load_default(size=36) # 用textbbox替代textsize获取文本尺寸 bbox = draw.textbbox((0, 0), watermark_text, font=font) text_width = bbox[2] - bbox[0] text_height = bbox[3] - bbox[1] x = base_image.width - text_width - 10 y = base_image.height - text_height - 10 draw.text((x, y), watermark_text, font=font, fill=(255, 255, 255, 128)) watermarked_image = Image.alpha_composite(base_image, watermark) # 调整水印后图像的尺寸用于显示 display_copy = watermarked_image.copy() display_copy.thumbnail((400, 400)) self.display_image = ImageTk.PhotoImage(display_copy) self.image_label.configure(image=self.display_image) self.image_label.image = self.display_image # 保留引用避免被垃圾回收 else: print("Please enter a watermark text.") root = tk.Tk() app = WatermarkApp(root) root.mainloop()
关键修改说明
- 拆分图像存储:新增
self.original_image保存原始PIL图像,self.display_image保存GUI显示用的PhotoImage,避免原始图像被覆盖。 - 替换文本尺寸获取方法:用
draw.textbbox()获取文本边界框,通过计算边界框坐标差得到文本宽高,替代已移除的textsize。 - 图像格式兼容:添加RGBA格式转换逻辑,确保透明水印合成正常工作。
- 字体容错处理:增加字体加载异常捕获,避免因系统缺少指定字体导致程序崩溃。
- 保留图像引用:更新显示图像时明确保留PhotoImage引用,防止被Python垃圾回收机制清理导致图像消失。
内容的提问来源于stack exchange,提问作者Abhay Singh
相关产品推荐
相关产品推荐

