Python中批量给图片添加文字水印(第6节)


在日常工作和生活中,我们经常需要对图片添加水印。通过在图片中添加水印,可以有效保护图片的版权。在前面的教程中,我们已经介绍了如何通过给单个图片添加水印。在实际运用中,我们经常需要批量对多张图片添加水印,如果手动一张一张地添加水印效率极低,既费时又费力,利用Python的Pillow库和Tkinter库可以轻松实现批量添加水印的功能。

批量添加水印程序的效果图如下所示:

1、批量添加水印过程分析

程序通过Python的PIL(Pillow)库和Tkinter图形用户界面(GUI)创建一个应用,用户可以通过简单的界面选择单张图片或目标图片文件夹,输入水印文字、字体大小、不透明度、水印位置和水印字体颜色,处理后的图片还能保存到指定的文件夹中。点击“添加水印”或“批量添加水印”按钮后,程序会自动为图片添加文字水印,并保存到指定文件夹中。

具体实现步骤如下:

输入验证:验证用户输入的字体大小和不透明度是否为整数。

水印添加:定义添加文字水印的函数,可以选择处理单张图片或选择批量图片添加水印的情况。

总的GUI界面设计:使用Tkinter库创建一个操作简单的图形用户界面,方便用户操作。

输出保存:允许用户选择输出文件夹,将添加水印后的图片保存到指定文件夹中。

2、代码实现

(1) 导入必要的库

首先,确保你已经安装了Pillow库。如果还没有安装,可以通过pip安装:

pip install pillow

(2) 导入必要的库

import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from tkinter.colorchooser import askcolor
from PIL import Image, ImageDraw, ImageFont

这里导入了PIL(Pillow)库用于图片处理,os库用于操作系统文件和文件夹,Tkinter库用于创建(GUI)图形用户界面,filedialog和messagebox分别用于文件选择和消息提示,askcolor用于颜色选择对话框,允许用户通过颜色对话框选择所需的颜色对象,支持使用预定义的颜色样本或者通过设置RGB值选择自定义颜色。用户选择颜色后,对话框会返回一个包含所选颜色RGB值和十六进制值的元组‌。

(3)显示十六进制的RGB值颜色

# 显示十六进制的RGB值颜色
def rgb_to_hex(rgb):
    return "#%02x%02x%02x" % rgb

该函数用于确保输出十六进制数的RGB值颜色,

(4)验证函数

# 判断输入的内容是否为整数
def is_int_number(input_str):
    try:
        int(input_str)
        return True
    except ValueError:
        return False

该函数用于验证用户输入的字符是否为整数,如果是整数则返回True,否则返回False。

(5)创建输出文件路径

# 创建输出文件路径
def create_output_path(image_path, output_path):
    file_name = os.path.splitext(os.path.basename(image_path))[0]
    file_extension = os.path.splitext(image_path)[1]
    return os.path.join(output_path, file_name + "_watermarked" + file_extension)

通过该函数获取输入图片的路径和输出文件夹路径,生成添加水印后图片的保存路径。

(6)文字水印制作函数

# 文字水印制作函数
def add_text_watermark(image_path, output_path, watermark_text, font_size, opacity, position, color):
    try:
        # 打开原始图片
        base_image = Image.open(image_path).convert("RGBA")
        # 尝试使用Windows系统预装的黑体字体
        try:
            font = ImageFont.truetype("simhei.ttf", font_size)
        except OSError:
            try:
                # 尝试使用系统默认字体
                font = ImageFont.load_default()
            except Exception as e:
                # 如果字体加载失败,则抛出错误提示
                messagebox.showerror("错误", f"无法加载字体: {str(e)}")
                return

        # 根据颜色选择和不透明度值创建RGBA元组
        opacity_tuple = ((round(opacity * 2.55)),)
        text_color = color + opacity_tuple

        # 创建一个ImageDraw对象,用来绘制图像
        draw = ImageDraw.Draw(base_image)

        # 使用textbbox方法计算水印文本尺寸
        bbox = draw.textbbox((0, 0), watermark_text, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]

        # 获取原始图片尺寸
        img_width, img_height = base_image.size

        # 根据选择的位置调整水印位置
        if position == "左上":
            paste_position = (0, 0)
        elif position == "右上":
            paste_position = (img_width - text_width, 0)
        elif position == "居中":
            paste_position = (int((img_width - text_width)/2), int((img_height - text_height)/2))
        elif position == "左下":
            paste_position = (0, img_height - text_height)
        elif position == "右下":
            paste_position = (img_width - text_width, img_height - text_height)
        else:
            paste_position = (0, 0)

        # 创建一个新的透明度为0的白色背景图片
        text_image = Image.new('RGBA', (text_width, text_height), (255, 255, 255, 0))
        # 创建一个ImageDraw对象,用来绘制文本内容
        draw_text = ImageDraw.Draw(text_image)
        # 在白色背景的图片上写入文本内容
        draw_text.text((0, 0), watermark_text, font=font, fill=text_color)

        # 将文本图片粘贴到原始图片上
        base_image.paste(text_image, paste_position, text_image)
        base_image = base_image.convert("RGB")
        output_file = create_output_path(image_path, output_path)
        # 保存结果图片,压缩质量为95%最高质量‌
        base_image.save(output_file, quality=95)
    except Exception as e:
        messagebox.showerror("错误", f"添加文字水印失败: {str(e)}")

(7)批量文字水印制作函数

def batch_add_watermark(folder_path, output_path, watermark_text, font_size, opacity, position, color):
    # 遍历文件夹中的所有图片文件
    for app, dirs, files in os.walk(folder_path):
        for file in files:
            file_extension = os.path.splitext(file)[1].lower()
            if file_extension in [".png", ".jpg", ".jpeg", ".bmp", ".gif"]:
                image_path = os.path.join(app, file)
                add_text_watermark(image_path, output_path, watermark_text, font_size, opacity, position, color)

该函数用于批量处理指定文件夹中的所有图片。通过for循环遍历文件夹中的所有文件,筛选出图片格式的文件,最后调用add_text_watermark()水印制作函数为每张图片添加水印。

(8)GUI界面设计

# GUI界面设计
class WatermarkApp:
    def __init__(self, app):
        self.app = app
        self.app.title("添加文字水印工具")
        self.image_path = ""
        self.folder_path = ""
        self.output_path = ""
        self.create_widgets()

    def create_widgets(self):
        # 添加水印部分(非批量)
        self.edit_watermark_frame = tk.LabelFrame(self.app, text="添加文字水印(非批量)", labelanchor='n')
        self.edit_watermark_frame.grid(row=0, column=0, padx=10, pady=10)

        tk.Label(self.edit_watermark_frame, text="选择待加水印图片:").grid(row=0, column=0)
        self.open_image_entry = tk.Entry(self.edit_watermark_frame, width=30)
        self.open_image_entry.grid(row=0, column=1, pady=5, padx=2)

        self.image_button = tk.Button(self.edit_watermark_frame, text="选择图片", command=self.select_image)
        self.image_button.grid(row=0, column=2)

        tk.Label(self.edit_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)
        self.output_entry = tk.Entry(self.edit_watermark_frame, width=30)
        self.output_entry.grid(row=1, column=1, pady=5, padx=2)

        self.output_path_button = tk.Button(self.edit_watermark_frame, text="选择输出文件夹",
                                              command=self.select_output_path)
        self.output_path_button.grid(row=1, column=2, pady=5, padx=5)

        # 水印文字内容
        tk.Label(self.edit_watermark_frame, text="输入水印文字: ", width=12, anchor="w").grid(row=2, column=0, pady=5, padx=2)
        self.default_value = tk.StringVar()
        self.default_value.set("水印文字内容")
        self.watermark_text = tk.Entry(self.edit_watermark_frame, textvariable=self.default_value, width=30)
        self.watermark_text.grid(row=2, column=1, pady=5, padx=2, sticky="w")

        # 水印字体大小
        self.current_font_size = tk.IntVar(value=50)
        tk.Label(self.edit_watermark_frame, text="字体大小: ", width=12, anchor="w").grid(row=3, column=0, pady=5, padx=2)
        self.font_size = tk.Spinbox(self.edit_watermark_frame,
                            from_=8,
                            to=250,
                            wrap=True,
                            textvariable=self.current_font_size,)
        self.font_size.grid(column=1, row=3, pady=5, padx=2, sticky="w")

        # 水印不透明度
        self.current_opacity = tk.IntVar(value=100)
        tk.Label(self.edit_watermark_frame, text="不透明度", width=12, anchor="w").grid(row=4, column=0, pady=5, padx=2)
        self.opacity = tk.Spinbox(self.edit_watermark_frame,
                                from_=0,
                                to=100,
                                textvariable=self.current_opacity,
                                wrap=False,
                                )
        self.opacity.grid(row=4, column=1, pady=5, padx=2, sticky="w")

        # 水印位置选择
        self.position_label = tk.Label(self.edit_watermark_frame, pady=5, padx=2, text="水印位置:", width=12, anchor="w")
        self.position_label.grid(column=0, row=5)
        self.position_var = tk.StringVar()
        self.position_option = ttk.Combobox(
            self.edit_watermark_frame,
            textvariable=self.position_var,
            values=["左上", "右上", "居中", "左下", "右下"],
            width=10,
            state="readonly",
        )
        self.position_option.current(2)
        self.position_option.grid(column=1, row=5, pady=5, padx=2, sticky="w")

        # 水印颜色
        self.text_color = (0, 0, 0)
        self.color_selection = tk.Canvas(self.edit_watermark_frame,
                                bg=rgb_to_hex(self.text_color),
                                width=80,
                                height=15)
        self.color_selection.grid(column=0, row=6, pady=5, padx=2)
        self.color_button = tk.Button(self.edit_watermark_frame,
                            text="更改字体颜色",
                            command=self.change_text_color,
                            width=18)
        self.color_button.grid(column=1, row=6, columnspan=1, pady=5, padx=2, sticky="w")

        self.add_watermark_button = tk.Button(self.edit_watermark_frame, text="添加水印", command=self.add_watermark)
        self.add_watermark_button.grid(row=7, column=1, pady=5, padx=5)

        # 批量添加水印部分
        self.batch_edit_watermark_frame = tk.LabelFrame(self.app, text="批量添加文字水印", labelanchor='n')
        self.batch_edit_watermark_frame.grid(row=0, column=1, padx=10, pady=10)

        tk.Label(self.batch_edit_watermark_frame, text="选择文件夹:").grid(row=0, column=0)
        self.batch_folder_entry = tk.Entry(self.batch_edit_watermark_frame, width=30)
        self.batch_folder_entry.grid(row=0, column=1, pady=5, padx=2)

        self.batch_folder_button = tk.Button(self.batch_edit_watermark_frame, text="选择文件夹",
                                             command=self.select_batch_folder)
        self.batch_folder_button.grid(row=0, column=2)

        tk.Label(self.batch_edit_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)

        self.batch_output_path_entry = tk.Entry(self.batch_edit_watermark_frame, width=30)
        self.batch_output_path_entry.grid(row=1, column=1, pady=5, padx=2)

        self.batch_output_path_button = tk.Button(self.batch_edit_watermark_frame, text="选择输出文件夹",
                                                    command=self.select_batch_output_path)
        self.batch_output_path_button.grid(row=1, column=2, pady=5, padx=5)

        # 水印文字内容
        tk.Label(self.batch_edit_watermark_frame, text="输入水印文字: ", width=12, anchor="w").grid(row=2, column=0, pady=5, padx=2)
        self.default_value = tk.StringVar()
        self.default_value.set("水印文字内容")
        self.batch_watermark_text = tk.Entry(self.batch_edit_watermark_frame, textvariable=self.default_value, width=30)
        self.batch_watermark_text.grid(row=2, column=1, pady=5, padx=2, sticky="w")

        # 水印字体大小
        self.current_font_size = tk.IntVar(value=50)
        tk.Label(self.batch_edit_watermark_frame, text="字体大小: ", width=12, anchor="w").grid(row=3, column=0, pady=5, padx=2)
        self.batch_font_size = tk.Spinbox(self.batch_edit_watermark_frame,
                            from_=8,
                            to=250,
                            wrap=True,
                            textvariable=self.current_font_size,)
        self.batch_font_size.grid(column=1, row=3, pady=5, padx=2, sticky="w")

        # 水印不透明度
        self.current_opacity = tk.IntVar(value=100)
        tk.Label(self.batch_edit_watermark_frame, text="不透明度", width=12, anchor="w").grid(row=4, column=0, pady=5, padx=2)
        self.batch_opacity = tk.Spinbox(self.batch_edit_watermark_frame,
                                from_=0,
                                to=100,
                                textvariable=self.current_opacity,
                                wrap=False,
                                )
        self.batch_opacity.grid(row=4, column=1, pady=5, padx=2, sticky="w")

        # 水印位置选择
        self.batch_position_label = tk.Label(self.batch_edit_watermark_frame, pady=5, padx=2, text="水印位置:", width=12, anchor="w")
        self.batch_position_label.grid(column=0, row=5)

        self.batch_position_var = tk.StringVar()
        self.batch_position_option = ttk.Combobox(
            self.batch_edit_watermark_frame,
            textvariable=self.batch_position_var,
            values=["左上", "右上", "居中", "左下", "右下"],
            width=10,
            state="readonly",
        )
        self.batch_position_option.current(2)
        self.batch_position_option.grid(column=1, row=5, pady=5, padx=2, sticky="w")

        # 水印颜色
        self.batch_text_color = (0, 0, 0)
        self.batch_color_selection = tk.Canvas(self.batch_edit_watermark_frame,
                                bg=rgb_to_hex(self.batch_text_color),
                                width=80,
                                height=15)
        self.batch_color_selection.grid(column=0, row=6, pady=5, padx=2)
        self.batch_color_button = tk.Button(self.batch_edit_watermark_frame,
                            text="更改字体颜色",
                            command=self.batch_change_text_color,
                            width=18)
        self.batch_color_button.grid(column=1, row=6, columnspan=1, pady=5, padx=2, sticky="w")

        self.batch_add_watermark_button = tk.Button(self.batch_edit_watermark_frame, text="批量添加水印",
                                                    command=self.batch_add_watermark_action)
        self.batch_add_watermark_button.grid(row=7, column=1, pady=5, padx=5)

    # 打开一个文件选择对话框,允许用户选择一个图片文件
    def select_image(self):
        # 支持多种格式
        self.image_path = filedialog.askopenfilename(title="选择图片",
                                                     filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp *.gif")])
        if self.image_path:
            self.open_image_entry.delete(0, tk.END)
            self.open_image_entry.insert(0, self.image_path)

    # 选择输出文件夹路径,用于存放添加水印后的图片
    def select_output_path(self):
        self.output_path = filedialog.askdirectory(title="选择输出文件夹")
        if self.output_path:
            self.output_entry.delete(0, tk.END)
            self.output_entry.insert(0, self.output_path)

    # 文字水印颜色选择器(非批量)
    def change_text_color(self):
        self.colors = askcolor(title="水印颜色选择器")
        self.text_color = self.colors[0]      # colors[0]是RGB值
        self.color_selection.configure(bg=self.colors[1])     # colors[1]是十六进制值

    # 文字水印颜色选择器(批量)
    def batch_change_text_color(self):
        self.colors = askcolor(title="水印颜色选择器")
        self.batch_text_color = self.colors[0]      # colors[0]是RGB值
        self.batch_color_selection.configure(bg=self.colors[1])     # colors[1]是十六进制值

    # 添加文字水印函数(非批量)
    def add_watermark(self):
        watermark_text = self.watermark_text.get()
        font_size_str = self.font_size.get()
        opacity_str = self.opacity.get()
        position = self.position_var.get()
        color = self.text_color
        if not is_int_number(font_size_str) or not is_int_number(opacity_str):
            messagebox.showerror("错误", "字体大小和透明度必须为有效的整数!")
            return
        font_size = int(font_size_str)
        opacity = int(opacity_str)
        if self.image_path and self.output_path:
            add_text_watermark(self.image_path, self.output_path, watermark_text, font_size, opacity, position, color)
            messagebox.showinfo("提示", "水印添加成功!")
        else:
            messagebox.showerror("错误", "请先选择图片和输出文件夹!")

    # 选择文件夹路径,包含原图片
    def select_batch_folder(self):
        self.folder_path = filedialog.askdirectory(title="选择文件夹")
        if self.folder_path:
            self.batch_folder_entry.delete(0, tk.END)
            self.batch_folder_entry.insert(0, self.folder_path)

    # 选择输出文件夹路径,用于存放添加水印后的图片
    def select_batch_output_path(self):
        self.output_path = filedialog.askdirectory(title="选择输出文件夹")
        if self.output_path:
            self.batch_output_path_entry.delete(0, tk.END)
            self.batch_output_path_entry.insert(0, self.output_path)

    # 批量添加文字水印函数
    def batch_add_watermark_action(self):
        watermark_text = self.batch_watermark_text.get()
        font_size_str = self.batch_font_size.get()
        opacity_str = self.batch_opacity.get()
        position = self.batch_position_var.get()
        color = self.batch_text_color
        if not is_int_number(font_size_str) or not is_int_number(opacity_str):
            messagebox.showerror("错误", "字体大小和透明度必须为有效的整数!")
            return
        font_size = int(font_size_str)
        opacity = int(opacity_str)
        if self.folder_path and self.output_path:
            batch_add_watermark(self.folder_path, self.output_path, watermark_text, font_size, opacity, position, color)
            messagebox.showinfo("提示", "批量水印添加成功!")
        else:
            messagebox.showerror("错误", "请先选择文件夹和输出文件夹!")

该WatermarkApp类使用Tkinter库创建了一个简单的GUI界面,主要分为两个部分,左边的框架可以添加单张图片水印,右边的框架可以批量添加水印。用户可以通过按钮选择图片、文件夹和输出文件夹,输入水印文字、字体大小、不透明度、水印位置和水印字体颜色,然后点击相应的按钮进行操作。

(9)主程序

# 当前模块直接被执行
if __name__ == "__main__":
    # 创建程序主窗口
    app = tk.Tk()
    window = WatermarkApp(app)
    # 运行主循环
    app.mainloop()

在主程序中,创建Tkinter程序的主窗口,通过实例化WatermarkApp类运行主循环,使图形GUI界面保持运行状态。

(10)批量给图片添加文字水印的完整代码如下所示:

import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from tkinter.colorchooser import askcolor
from PIL import Image, ImageDraw, ImageFont

# 显示十六进制的RGB值颜色
def rgb_to_hex(rgb):
    return "#%02x%02x%02x" % rgb

# 判断输入的内容是否为整数
def is_int_number(input_str):
    try:
        int(input_str)
        return True
    except ValueError:
        return False

# 创建输出文件路径
def create_output_path(image_path, output_path):
    file_name = os.path.splitext(os.path.basename(image_path))[0]
    file_extension = os.path.splitext(image_path)[1]
    return os.path.join(output_path, file_name + "_watermarked" + file_extension)

# 文字水印制作函数
def add_text_watermark(image_path, output_path, watermark_text, font_size, opacity, position, color):
    try:
        # 打开原始图片
        base_image = Image.open(image_path).convert("RGBA")
        # 尝试使用Windows系统预装的黑体字体
        try:
            font = ImageFont.truetype("simhei.ttf", font_size)
        except OSError:
            try:
                # 尝试使用系统默认字体
                font = ImageFont.load_default()
            except Exception as e:
                # 如果字体加载失败,则抛出错误提示
                messagebox.showerror("错误", f"无法加载字体: {str(e)}")
                return

        # 根据颜色选择和不透明度值创建RGBA元组
        opacity_tuple = ((round(opacity * 2.55)),)
        text_color = color + opacity_tuple

        # 创建一个ImageDraw对象,用来绘制图像
        draw = ImageDraw.Draw(base_image)

        # 使用textbbox方法计算水印文本尺寸
        bbox = draw.textbbox((0, 0), watermark_text, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]

        # 获取原始图片尺寸
        img_width, img_height = base_image.size

        # 根据选择的位置调整水印位置
        if position == "左上":
            paste_position = (0, 0)
        elif position == "右上":
            paste_position = (img_width - text_width, 0)
        elif position == "居中":
            paste_position = (int((img_width - text_width)/2), int((img_height - text_height)/2))
        elif position == "左下":
            paste_position = (0, img_height - text_height)
        elif position == "右下":
            paste_position = (img_width - text_width, img_height - text_height)
        else:
            paste_position = (0, 0)

        # 创建一个新的透明度为0的白色背景图片
        text_image = Image.new('RGBA', (text_width, text_height), (255, 255, 255, 0))
        # 创建一个ImageDraw对象,用来绘制文本内容
        draw_text = ImageDraw.Draw(text_image)
        # 在白色背景的图片上写入文本内容
        draw_text.text((0, 0), watermark_text, font=font, fill=text_color)

        # 将文本图片粘贴到原始图片上
        base_image.paste(text_image, paste_position, text_image)
        base_image = base_image.convert("RGB")
        output_file = create_output_path(image_path, output_path)
        # 保存结果图片,压缩质量为95%最高质量‌
        base_image.save(output_file, quality=95)
    except Exception as e:
        messagebox.showerror("错误", f"添加文字水印失败: {str(e)}")

# 批量文字水印制作函数
def batch_add_watermark(folder_path, output_path, watermark_text, font_size, opacity, position, color):
    # 遍历文件夹中的所有图片文件
    for app, dirs, files in os.walk(folder_path):
        for file in files:
            file_extension = os.path.splitext(file)[1].lower()
            if file_extension in [".png", ".jpg", ".jpeg", ".bmp", ".gif"]:
                image_path = os.path.join(app, file)
                add_text_watermark(image_path, output_path, watermark_text, font_size, opacity, position, color)

# GUI界面设计
class WatermarkApp:
    def __init__(self, app):
        self.app = app
        self.app.title("添加文字水印工具")
        self.image_path = ""
        self.folder_path = ""
        self.output_path = ""
        self.create_widgets()

    def create_widgets(self):
        # 添加水印部分(非批量)
        self.edit_watermark_frame = tk.LabelFrame(self.app, text="添加文字水印(非批量)", labelanchor='n')
        self.edit_watermark_frame.grid(row=0, column=0, padx=10, pady=10)

        tk.Label(self.edit_watermark_frame, text="选择待加水印图片:").grid(row=0, column=0)
        self.open_image_entry = tk.Entry(self.edit_watermark_frame, width=30)
        self.open_image_entry.grid(row=0, column=1, pady=5, padx=2)

        self.image_button = tk.Button(self.edit_watermark_frame, text="选择图片", command=self.select_image)
        self.image_button.grid(row=0, column=2)

        tk.Label(self.edit_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)
        self.output_entry = tk.Entry(self.edit_watermark_frame, width=30)
        self.output_entry.grid(row=1, column=1, pady=5, padx=2)

        self.output_path_button = tk.Button(self.edit_watermark_frame, text="选择输出文件夹",
                                              command=self.select_output_path)
        self.output_path_button.grid(row=1, column=2, pady=5, padx=5)

        # 水印文字内容
        tk.Label(self.edit_watermark_frame, text="输入水印文字: ", width=12, anchor="w").grid(row=2, column=0, pady=5, padx=2)
        self.default_value = tk.StringVar()
        self.default_value.set("水印文字内容")
        self.watermark_text = tk.Entry(self.edit_watermark_frame, textvariable=self.default_value, width=30)
        self.watermark_text.grid(row=2, column=1, pady=5, padx=2, sticky="w")

        # 水印字体大小
        self.current_font_size = tk.IntVar(value=50)
        tk.Label(self.edit_watermark_frame, text="字体大小: ", width=12, anchor="w").grid(row=3, column=0, pady=5, padx=2)
        self.font_size = tk.Spinbox(self.edit_watermark_frame,
                            from_=8,
                            to=250,
                            wrap=True,
                            textvariable=self.current_font_size,)
        self.font_size.grid(column=1, row=3, pady=5, padx=2, sticky="w")

        # 水印不透明度
        self.current_opacity = tk.IntVar(value=100)
        tk.Label(self.edit_watermark_frame, text="不透明度", width=12, anchor="w").grid(row=4, column=0, pady=5, padx=2)
        self.opacity = tk.Spinbox(self.edit_watermark_frame,
                                from_=0,
                                to=100,
                                textvariable=self.current_opacity,
                                wrap=False,
                                )
        self.opacity.grid(row=4, column=1, pady=5, padx=2, sticky="w")

        # 水印位置选择
        self.position_label = tk.Label(self.edit_watermark_frame, pady=5, padx=2, text="水印位置:", width=12, anchor="w")
        self.position_label.grid(column=0, row=5)
        self.position_var = tk.StringVar()
        self.position_option = ttk.Combobox(
            self.edit_watermark_frame,
            textvariable=self.position_var,
            values=["左上", "右上", "居中", "左下", "右下"],
            width=10,
            state="readonly",
        )
        self.position_option.current(2)
        self.position_option.grid(column=1, row=5, pady=5, padx=2, sticky="w")

        # 水印颜色
        self.text_color = (0, 0, 0)
        self.color_selection = tk.Canvas(self.edit_watermark_frame,
                                bg=rgb_to_hex(self.text_color),
                                width=80,
                                height=15)
        self.color_selection.grid(column=0, row=6, pady=5, padx=2)
        self.color_button = tk.Button(self.edit_watermark_frame,
                            text="更改字体颜色",
                            command=self.change_text_color,
                            width=18)
        self.color_button.grid(column=1, row=6, columnspan=1, pady=5, padx=2, sticky="w")

        self.add_watermark_button = tk.Button(self.edit_watermark_frame, text="添加水印", command=self.add_watermark)
        self.add_watermark_button.grid(row=7, column=1, pady=5, padx=5)

        # 批量添加水印部分
        self.batch_edit_watermark_frame = tk.LabelFrame(self.app, text="批量添加文字水印", labelanchor='n')
        self.batch_edit_watermark_frame.grid(row=0, column=1, padx=10, pady=10)

        tk.Label(self.batch_edit_watermark_frame, text="选择文件夹:").grid(row=0, column=0)
        self.batch_folder_entry = tk.Entry(self.batch_edit_watermark_frame, width=30)
        self.batch_folder_entry.grid(row=0, column=1, pady=5, padx=2)

        self.batch_folder_button = tk.Button(self.batch_edit_watermark_frame, text="选择文件夹",
                                             command=self.select_batch_folder)
        self.batch_folder_button.grid(row=0, column=2)

        tk.Label(self.batch_edit_watermark_frame, text="选择输出文件夹:").grid(row=1, column=0)

        self.batch_output_path_entry = tk.Entry(self.batch_edit_watermark_frame, width=30)
        self.batch_output_path_entry.grid(row=1, column=1, pady=5, padx=2)

        self.batch_output_path_button = tk.Button(self.batch_edit_watermark_frame, text="选择输出文件夹",
                                                    command=self.select_batch_output_path)
        self.batch_output_path_button.grid(row=1, column=2, pady=5, padx=5)

        # 水印文字内容
        tk.Label(self.batch_edit_watermark_frame, text="输入水印文字: ", width=12, anchor="w").grid(row=2, column=0, pady=5, padx=2)
        self.default_value = tk.StringVar()
        self.default_value.set("水印文字内容")
        self.batch_watermark_text = tk.Entry(self.batch_edit_watermark_frame, textvariable=self.default_value, width=30)
        self.batch_watermark_text.grid(row=2, column=1, pady=5, padx=2, sticky="w")

        # 水印字体大小
        self.current_font_size = tk.IntVar(value=50)
        tk.Label(self.batch_edit_watermark_frame, text="字体大小: ", width=12, anchor="w").grid(row=3, column=0, pady=5, padx=2)
        self.batch_font_size = tk.Spinbox(self.batch_edit_watermark_frame,
                            from_=8,
                            to=250,
                            wrap=True,
                            textvariable=self.current_font_size,)
        self.batch_font_size.grid(column=1, row=3, pady=5, padx=2, sticky="w")

        # 水印不透明度
        self.current_opacity = tk.IntVar(value=100)
        tk.Label(self.batch_edit_watermark_frame, text="不透明度", width=12, anchor="w").grid(row=4, column=0, pady=5, padx=2)
        self.batch_opacity = tk.Spinbox(self.batch_edit_watermark_frame,
                                from_=0,
                                to=100,
                                textvariable=self.current_opacity,
                                wrap=False,
                                )
        self.batch_opacity.grid(row=4, column=1, pady=5, padx=2, sticky="w")

        # 水印位置选择
        self.batch_position_label = tk.Label(self.batch_edit_watermark_frame, pady=5, padx=2, text="水印位置:", width=12, anchor="w")
        self.batch_position_label.grid(column=0, row=5)

        self.batch_position_var = tk.StringVar()
        self.batch_position_option = ttk.Combobox(
            self.batch_edit_watermark_frame,
            textvariable=self.batch_position_var,
            values=["左上", "右上", "居中", "左下", "右下"],
            width=10,
            state="readonly",
        )
        self.batch_position_option.current(2)
        self.batch_position_option.grid(column=1, row=5, pady=5, padx=2, sticky="w")

        # 水印颜色
        self.batch_text_color = (0, 0, 0)
        self.batch_color_selection = tk.Canvas(self.batch_edit_watermark_frame,
                                bg=rgb_to_hex(self.batch_text_color),
                                width=80,
                                height=15)
        self.batch_color_selection.grid(column=0, row=6, pady=5, padx=2)
        self.batch_color_button = tk.Button(self.batch_edit_watermark_frame,
                            text="更改字体颜色",
                            command=self.batch_change_text_color,
                            width=18)
        self.batch_color_button.grid(column=1, row=6, columnspan=1, pady=5, padx=2, sticky="w")

        self.batch_add_watermark_button = tk.Button(self.batch_edit_watermark_frame, text="批量添加水印",
                                                    command=self.batch_add_watermark_action)
        self.batch_add_watermark_button.grid(row=7, column=1, pady=5, padx=5)

    # 打开一个文件选择对话框,允许用户选择一个图片文件
    def select_image(self):
        # 支持多种格式
        self.image_path = filedialog.askopenfilename(title="选择图片",
                                                     filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp *.gif")])
        if self.image_path:
            self.open_image_entry.delete(0, tk.END)
            self.open_image_entry.insert(0, self.image_path)

    # 选择输出文件夹路径,用于存放添加水印后的图片
    def select_output_path(self):
        self.output_path = filedialog.askdirectory(title="选择输出文件夹")
        if self.output_path:
            self.output_entry.delete(0, tk.END)
            self.output_entry.insert(0, self.output_path)

    # 文字水印颜色选择器(非批量)
    def change_text_color(self):
        self.colors = askcolor(title="水印颜色选择器")
        self.text_color = self.colors[0]      # colors[0]是RGB值
        self.color_selection.configure(bg=self.colors[1])     # colors[1]是十六进制值

    # 文字水印颜色选择器(批量)
    def batch_change_text_color(self):
        self.colors = askcolor(title="水印颜色选择器")
        self.batch_text_color = self.colors[0]      # colors[0]是RGB值
        self.batch_color_selection.configure(bg=self.colors[1])     # colors[1]是十六进制值

    # 添加文字水印函数(非批量)
    def add_watermark(self):
        watermark_text = self.watermark_text.get()
        font_size_str = self.font_size.get()
        opacity_str = self.opacity.get()
        position = self.position_var.get()
        color = self.text_color
        if not is_int_number(font_size_str) or not is_int_number(opacity_str):
            messagebox.showerror("错误", "字体大小和透明度必须为有效的整数!")
            return
        font_size = int(font_size_str)
        opacity = int(opacity_str)
        if self.image_path and self.output_path:
            add_text_watermark(self.image_path, self.output_path, watermark_text, font_size, opacity, position, color)
            messagebox.showinfo("提示", "水印添加成功!")
        else:
            messagebox.showerror("错误", "请先选择图片和输出文件夹!")

    # 选择文件夹路径,包含原图片
    def select_batch_folder(self):
        self.folder_path = filedialog.askdirectory(title="选择文件夹")
        if self.folder_path:
            self.batch_folder_entry.delete(0, tk.END)
            self.batch_folder_entry.insert(0, self.folder_path)

    # 选择输出文件夹路径,用于存放添加水印后的图片
    def select_batch_output_path(self):
        self.output_path = filedialog.askdirectory(title="选择输出文件夹")
        if self.output_path:
            self.batch_output_path_entry.delete(0, tk.END)
            self.batch_output_path_entry.insert(0, self.output_path)

    # 批量添加文字水印函数
    def batch_add_watermark_action(self):
        watermark_text = self.batch_watermark_text.get()
        font_size_str = self.batch_font_size.get()
        opacity_str = self.batch_opacity.get()
        position = self.batch_position_var.get()
        color = self.batch_text_color
        if not is_int_number(font_size_str) or not is_int_number(opacity_str):
            messagebox.showerror("错误", "字体大小和透明度必须为有效的整数!")
            return
        font_size = int(font_size_str)
        opacity = int(opacity_str)
        if self.folder_path and self.output_path:
            batch_add_watermark(self.folder_path, self.output_path, watermark_text, font_size, opacity, position, color)
            messagebox.showinfo("提示", "批量水印添加成功!")
        else:
            messagebox.showerror("错误", "请先选择文件夹和输出文件夹!")

# 当前模块直接被执行
if __name__ == "__main__":
    # 创建程序主窗口
    app = tk.Tk()
    window = WatermarkApp(app)
    # 运行主循环
    app.mainloop()