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

使用Pillow库添加水印时遭遇AttributeError问题求助

问题分析与解决方案

错误原因

  1. textsize 方法已被移除:Pillow 9.1.0及以上版本中,ImageDraw 对象的 textsize() 方法被弃用并移除,导致触发 AttributeError。
  2. 图像对象被错误覆盖:在 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:48:14