带ui的简易图片批量处理工具(python3, tkinter)

简单封装了一些常用的批量图片处理操作,适用于没必要上PS的轻量级场合,比如快速制作机器学习的图片训练集,裁剪图片形状,又或者只是需要调整一下大小、旋转和翻转,并应用到所有指定图片上的情况。
为了方便使用,用tkinter写了简单的ui,用于批量调整训练集图片。
使用pyinstaller打包后,exe文件大小26.1MB,也算是一个方便的小工具。

语言:python3
封装的图片处理库:PIL, imageio
可视化UI:tkinter

1. 使用方式

在这里插入图片描述
1、选择添加图片文件后,可以单选或者多选,将确认的文件显示在列表中,此时可以选择一部分文件然后移除,也可以直接清空文件选择列表,文件添加可以多次操作。
2、完成文件选择后,选择所需要的操作,然后填入相应的操作参数,之后点击预览可以预览列表中选中的第一个图片文件,如果没有选中,则默认预览列表第一张。
3、在最下面选择输出的模型,再点击保存,确认操作后,修改保存将被执行。

2. 实现的功能

2.1 旋转

在这里插入图片描述

2.2 翻转

在这里插入图片描述

2.3 缩放

在这里插入图片描述

2.4 正方形裁剪

在这里插入图片描述

2.5 百分比缩放

在这里插入图片描述

2.6 像素化

在这里插入图片描述

2.7 黑白化

在这里插入图片描述

3. 代码实现

from tkinter import Tk, Frame, Label, Button, Scrollbar, Listbox, filedialog, \
    StringVar, messagebox, IntVar, Radiobutton, Entry
from tkinter.ttk import Combobox
from tkinter.filedialog import askdirectory
from tkinter.messagebox import askokcancel
from os import mkdir
from os.path import split as path_split
from os.path import realpath, exists, splitext
from PIL.Image import fromarray
from imageio import mimread, mimsave
from PIL.Image import open as Image_open
from PIL.ImageTk import PhotoImage as ImageTk_PhotoImage
from PIL.Image import ANTIALIAS, BILINEAR, BICUBIC, NEAREST, \
    FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, ROTATE_270


# 记录配置信息
class Config:
    # 版本号
    version = 'v1.3'
    # 主窗口的长宽
    root_height = 540
    root_width = 960

    # 预览图片最大长度限制
    size_limit = 200


# 记录缓存数据
class Data:
    # 程序运行路径
    running_path = path_split(realpath(__file__))[0]

    # 记录操作参数值
    rotation = None
    flip = None
    resize_method = None
    square_cut = None
    percent_reduce = None
    pixel_ratio = None
    output_method = None
    convert_method = None

    # 高度文本框
    height_entry = None
    # 宽度文本框
    width_entry = None

    # 输出路径
    output_path = None

    # 预览图片
    ori_img = None
    preview_img = None
    # 用于显示的图片
    ori_img_display = None
    preview_img_display = None


# 使用Image库打开图片
def load_image(img_path):
    return Image_open(img_path)


# 将图片转换为适用于界面显示的gif
def image_to_display(img):
    # 将长宽中较大值压到极限,等比例缩小
    if max(img.size) > Config.size_limit:
        rate = Config.size_limit / max(img.size)
        shape = (int(img.size[0] * rate), int(img.size[1] * rate))
        img = img.resize(shape)
    return ImageTk_PhotoImage(img)


# 处理图片
def change_img(img, operate):
    # 啥都不做
    if operate[0] == "无操作":
        pass

    elif operate[0] == "旋转":
        # 向左旋转90度
        if operate[1] == 0:
            img = img.transpose(ROTATE_90)
        # 向右旋转90度
        elif operate[1] == 1:
            img = img.transpose(ROTATE_270)
        # 旋转180度
        elif operate[1] == 2:
            img = img.transpose(ROTATE_180)

    elif operate[0] == "翻转":
        # 水平翻转
        if operate[1] == 0:
            img = img.transpose(FLIP_LEFT_RIGHT)
        # 垂直翻转
        elif operate[1] == 1:
            img = img.transpose(FLIP_TOP_BOTTOM)

    elif operate[0] == "缩放":
        height, width, resize_method = operate[1]
        type_dict = {0: ANTIALIAS, 1: NEAREST, 2: BILINEAR, 3: BICUBIC}
        img = img.resize((width, height), type_dict[resize_method])

    elif operate[0] == "正方形裁剪":
        width, height = img.size
        cut_length = max(img.size) - min(img.size)
        crop_shape = (0, 0, width, height)

        # crop_shape(left, upper, right, lower)
        # 坐标系统的原点(0, 0)是左上角
        # 保留居中
        if operate[1] == 0:
            # 裁剪宽
            if width > height:
                crop_shape = (cut_length//2, 0, cut_length//2 + height, height)
            # 裁剪高
            elif height > width:
                crop_shape = (0, cut_length//2, width, cut_length//2+width)
        # 保留左侧/顶部',
        elif operate[1] == 1:
            # 裁剪宽
            if width > height:
                crop_shape = (0, 0, width-cut_length, height)
            # 裁剪高
            elif height > width:
                crop_shape = (0, 0, width, height-cut_length)
        # 保留右侧/底部
        elif operate[1] == 2:
            # 裁剪宽
            if width > height:
                crop_shape = (cut_length, 0, width, height)
            # 裁剪高
            elif height > width:
                crop_shape = (0, cut_length, width, height)

        # 裁剪图片
        img = img.crop(crop_shape)

    elif operate[0] == "百分比缩放":
        width, height = img.size
        percent_reduce = operate[1]
        img = img.resize((width*percent_reduce//100, height*percent_reduce//100), ANTIALIAS)

    elif operate[0] == "像素化":
        width, height = img.size
        pixel_ratio = operate[1]
        img = img.resize((width*pixel_ratio//100, height*pixel_ratio//100), ANTIALIAS)
        img = img.resize((width, height), NEAREST)

    elif operate[0] == "黑白化":
        # 灰色图
        if operate[1] == 0:
            img = img.convert("L")

        # 非黑即白
        if operate[1] == 1:
            img = img.convert("1")
            img = img.convert("L")
    return img


def main():
    # 主窗口
    root = Tk()
    # 主窗口标题
    root.title('图像批量处理工具')
    # 主窗口大小
    root.geometry(f'{Config.root_width}x{Config.root_height}')

    # 主框架-----------------------------------------------------------------------
    root_frame = Frame(root)

    # 文件选择框架
    file_field_frame = Frame(root_frame)
    # 操作框架
    operate_field_frame = Frame(root_frame)
    # 保存操作框架
    save_field_frame = Frame(root_frame)
    # 信息框架
    info_field_frame = Frame(root_frame)

    # 文件选择框架-----------------------------------------------------------------------
    # 顶部子框架
    file_field_top_frame = Frame(file_field_frame)
    # 中部子框架
    file_field_middle_frame = Frame(file_field_frame)
    # 底部子框架
    file_field_bottom_frame = Frame(file_field_frame)

    # 已选择的文件列表与下拉框
    file_scrolbar = Scrollbar(file_field_bottom_frame)
    file_listbox = Listbox(file_field_bottom_frame, yscrollcommand=file_scrolbar.set, selectmode="extended")
    file_scrolbar.config(command=file_listbox.yview)

    # 文件选择信息标签
    file_field_label = Label(file_field_top_frame, text='选择你要处理的图片文件')

    # 添加文件按钮
    def add_choisen_file():
        img_paths = filedialog.askopenfilenames(
            filetypes=[('图片文件', ('.jpg', '.jpeg', '.png', '.bmp', ".gif")), ('所有文件', '*')])
        # 将选中的图片文件路径添加到list_box内
        for file in img_paths:
            # 在添加时检验是否为合法的图片文件,无法正确打开的文件将被跳过
            try:
                load_image(file)
            except:
                messagebox.showinfo('图片文件错误', f'{file}不是正确的图片文件,将被跳过!')
            else:
                file_listbox.insert("end", file)
    add_file_button = Button(file_field_top_frame, text='添加图片文件', command=add_choisen_file)

    # 删除选中文件列表
    def remove_choisen_file():
        for index in file_listbox.curselection():
            file_listbox.delete(index)
    delete_file_button = Button(file_field_top_frame, text='移除选中的文件', command=remove_choisen_file)

    # 清空选中文件列表
    def clear_choisen_file():
        file_listbox.delete(0, "end")
    # 清空文件按钮
    clear_file_button = Button(file_field_top_frame, text='清空选择列表', command=clear_choisen_file)


    # 文件选择提示标签
    file_choice_describe_label = Label(file_field_middle_frame, text='此时已选择的图片文件列表(按住 Shift 键或 Ctrl 键或拖拽鼠标进行多选):')

    # 操作框架-----------------------------------------------------------------------
    # 左侧子框架,图像操作参数区域,预览按钮
    operate_field_left_frame = Frame(operate_field_frame)
    # 右侧子框架,图像预览区域
    operate_field_right_frame = Frame(operate_field_frame)

    # 图像预览区域上下分为描述区和图像区,每个区域又分左右两部分
    operate_field_right_top_frame = Frame(operate_field_right_frame)
    operate_field_right_bottom_frame = Frame(operate_field_right_frame)

    # 原始图片标签与预览图片描述标签
    ori_desc_label = Label(operate_field_right_top_frame, text='原始图像(以选中的第一张为例):', anchor='nw')
    preview_desc_label = Label(operate_field_right_top_frame, text='修改预览(以选中的第一张为例):', anchor='nw')

    # 原始图片标签与预览图片显示标签,带有边框
    ori_img_label = Label(operate_field_right_bottom_frame, text='暂无图像', relief="raised")
    preview_img_label = Label(operate_field_right_bottom_frame, text='暂无图像', relief="raised")
    # 作为可变对象,先pack
    ori_desc_label.pack(side="left", fill="x", expand="yes")
    preview_desc_label.pack(side="right", fill="x", expand="yes")
    ori_img_label.pack(side="left", fill="both", expand="yes")
    preview_img_label.pack(side="right", fill="both", expand="yes")

    # 图片操作区
    # 操作选择区
    operate_field_left_top_frame = Frame(operate_field_left_frame)
    # 参数配置区,默认空白
    operate_field_left_bottom_frame = Frame(operate_field_left_frame)

    # 显示无参数时的标签
    operate_parameter_init_label = Label(operate_field_left_bottom_frame, text='暂无参数可用', anchor='w')
    operate_parameter_init_label.pack(fill="x")
    # 作为分页切换的特殊frame,提前打包
    operate_field_left_bottom_frame.pack(side="bottom", fill="both", expand="yes")

    operate_desc_label = Label(operate_field_left_top_frame, text='选择需要的操作:')
    # 操作选择下拉框
    xVariable = StringVar()
    operater_combobox = Combobox(operate_field_left_top_frame, textvariable=xVariable, state="readonly")
    # 可选的操作类型
    operater_combobox["value"] = ("无操作", "旋转", "翻转", "缩放", "正方形裁剪", "百分比缩放", "像素化", "黑白化")
    # 默认操作序号
    operater_combobox.current(0)

    # 更新参数框架的窗体布局
    def update_operater(event):
        # 针对图片参数配置区进行操作
        # 首先摧毁旧frame里面的所有组件
        for widget in operate_field_left_bottom_frame.winfo_children():
            widget.destroy()
        # 为frame配备新组件
        # 获取操作名
        operate = operater_combobox.get()
        # 根据操作分配新的组件布局
        if operate == "无操作":
            # 显示无参数时的标签
            operate_parameter_init_label = Label(operate_field_left_bottom_frame, text='暂无参数可用', anchor='w')
            operate_parameter_init_label.pack(fill="x")

        elif operate == "旋转":
            Data.rotation = IntVar()
            Data.rotation.set(0)
            Radiobutton(operate_field_left_bottom_frame, text='向左旋转90度',
                        variable=Data.rotation, value=0, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='向右旋转90度',
                        variable=Data.rotation, value=1, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='旋转180度',
                        variable=Data.rotation, value=2, anchor='w').pack(side="top", fill="x")

        elif operate == "翻转":
            Data.flip = IntVar()
            Data.flip.set(0)
            Radiobutton(operate_field_left_bottom_frame, text='水平翻转',
                        variable=Data.flip, value=0, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='垂直翻转',
                        variable=Data.flip, value=1, anchor='w').pack(side="top", fill="x")

        elif operate == "缩放":
            # 三层小frame
            # 长度frame
            height_frame = Frame(operate_field_left_bottom_frame)
            height_label = Label(height_frame, text='新高度(像素):', anchor='w')
            height_label.pack(side="left", fill="both", expand="yes")
            height_entry = Entry(height_frame)
            height_entry.pack(side="left", fill="both", expand="yes")
            Data.height_entry = height_entry
            height_frame.pack(side="top", fill="x")
            # 宽度frame
            width_frame = Frame(operate_field_left_bottom_frame)
            width_label = Label(width_frame, text='新宽度(像素):', anchor='w')
            width_label.pack(side="left", fill="both", expand="yes")
            width_entry = Entry(width_frame)
            width_entry.pack(side="left", fill="both", expand="yes")
            Data.width_entry = width_entry
            width_frame.pack(side="top", fill="x")
            # 缩放算法
            Data.resize_method = IntVar()
            Data.resize_method.set(0)
            Label(operate_field_left_bottom_frame, text='请选择缩放算法:', anchor='w').pack(side="top", fill="both")
            Radiobutton(operate_field_left_bottom_frame, text='高质量(推荐)',
                        variable=Data.resize_method, value=0, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='低质量',
                        variable=Data.resize_method, value=1, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='双线性',
                        variable=Data.resize_method, value=2, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='三次样条插值',
                        variable=Data.resize_method, value=3, anchor='w').pack(side="top", fill="x")

        elif operate == "正方形裁剪":
            Data.square_cut = IntVar()
            Data.square_cut.set(0)
            Label(operate_field_left_bottom_frame, text='将长方形图片裁剪为正方形的裁剪方式:',
                  anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='保留中间',
                        variable=Data.square_cut, value=0, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='保留左侧/顶部',
                        variable=Data.square_cut, value=1, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='保留右侧/底部',
                        variable=Data.square_cut, value=2, anchor='w').pack(side="top", fill="x")

        elif operate == "百分比缩放":
            Data.percent_reduce = IntVar()
            Data.percent_reduce.set(33)
            Label(operate_field_left_bottom_frame, text='选择需要缩放的比例:',
                   anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='10%',
                        variable=Data.percent_reduce, value=10, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='20%',
                        variable=Data.percent_reduce, value=20, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='25%',
                        variable=Data.percent_reduce, value=25, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='33%',
                        variable=Data.percent_reduce, value=33, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='50%',
                        variable=Data.percent_reduce, value=50, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='75%',
                        variable=Data.percent_reduce, value=75, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='150%',
                        variable=Data.percent_reduce, value=150, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='200%',
                        variable=Data.percent_reduce, value=200, anchor='w').pack(side="top", fill="x")
            # Radiobutton(operate_field_left_bottom_frame, text='300%',
            #             variable=Data.percent_reduce, value=300, anchor='w').pack(side="top", fill="x")

        elif operate == "像素化":
            Data.pixel_ratio = IntVar()
            Data.pixel_ratio.set(33)
            Label(operate_field_left_bottom_frame, text='选择需要的像素化程度(建议预览查看效果):',
                   anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='10%',
                        variable=Data.pixel_ratio, value=10, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='20%',
                        variable=Data.pixel_ratio, value=20, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='25%',
                        variable=Data.pixel_ratio, value=25, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='33%',
                        variable=Data.pixel_ratio, value=33, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='50%',
                        variable=Data.pixel_ratio, value=50, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='75%',
                        variable=Data.pixel_ratio, value=75, anchor='w').pack(side="top", fill="x")

        elif operate == "黑白化":
            Data.convert_method = IntVar()
            Data.convert_method.set(0)
            Label(operate_field_left_bottom_frame, text='黑白化的方式:',
                  anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='灰色图',
                        variable=Data.convert_method , value=0, anchor='w').pack(side="top", fill="x")
            Radiobutton(operate_field_left_bottom_frame, text='非黑即白',
                        variable=Data.convert_method , value=1, anchor='w').pack(side="top", fill="x")

        # 打包新的frame
        operate_field_left_bottom_frame.pack(side="bottom", fill="both", expand="yes")

    # 下拉菜单选择操作
    operater_combobox.bind("<<ComboboxSelected>>", update_operater)

    # 获取当前选择的操作,返回(操作名,参数列表)
    def get_operate():
        operate = operater_combobox.get()
        if operate == "无操作":
            return operate, None
        elif operate == "旋转":
            return operate, Data.rotation.get()
        elif operate == "翻转":
            return operate, Data.flip.get()
        elif operate == "缩放":
            return operate, [int(Data.height_entry.get()), int(Data.width_entry.get()), Data.resize_method.get()]
        elif operate == "正方形裁剪":
            return operate, Data.square_cut.get()
        elif operate == "百分比缩放":
            return operate, Data.percent_reduce.get()
        elif operate == "像素化":
            return operate, Data.pixel_ratio.get()
        elif operate == "黑白化":
            return operate, Data.convert_method.get()

    # 保存操作框架-----------------------------------------------------------------------
    # 左侧:输出描述,覆盖原文件,自定义输出路径,浏览
    # 右侧:预览按钮,保存按钮
    output_desc_label = Label(save_field_frame, text="输出配置:", anchor='w')

    Data.output_method = IntVar()
    Data.output_method.set(1)
    output_method_0 = Radiobutton(save_field_frame, text='覆盖原图片文件',
                variable=Data.output_method, value=0, anchor='w')
    output_method_1 = Radiobutton(save_field_frame, text='输出到指定路径:',
                variable=Data.output_method, value=1, anchor='w')
    # 输出路径文本框
    Data.output_path = StringVar()
    Data.output_path.set(Data.running_path.replace('\\','/') + '/output')
    output_path_label = Label(save_field_frame, textvariable=Data.output_path, anchor='w', bg='white')
    # 选择路径按钮
    def choose_output_path():
        output_path = askdirectory()
        if output_path:
            Data.output_path.set(output_path)
    choose_output_path_button = Button(save_field_frame, text='修改输出路径', command=choose_output_path)

    # 预览按钮
    # 图像预览区operate_field_right_bottom_frame
    def preview_img():
        # 针对图像预览区进行操作
        # 首先摧毁旧frame里面的所有组件
        for widget in operate_field_right_top_frame.winfo_children():
            widget.destroy()
        for widget in operate_field_right_bottom_frame.winfo_children():
            widget.destroy()
        # 为frame配备新组件
        # 如果选择列表为空
        if not file_listbox.size():
            ori_desc_label = Label(operate_field_right_top_frame, text='原始图像(以选中的第一张为例):', anchor='nw')
            preview_desc_label = Label(operate_field_right_top_frame, text='修改预览(以选中的第一张为例):', anchor='nw')
            # 原始图片标签与预览图片显示标签,带有边框
            ori_img_label = Label(operate_field_right_bottom_frame, text='暂无图像', relief="raised")
            preview_img_label = Label(operate_field_right_bottom_frame, text='暂无图像', relief="raised")
        # 取出选中的第一张图片作为预览图片,没有选中则取出列表中的第一张
        else:
            select_list = file_listbox.curselection()
            # 如果选择的图片不为空
            if select_list:
                ori_img_path = file_listbox.get(file_listbox.curselection()[0])
            # 否则取出列表第一个
            else:
                ori_img_path = file_listbox.get(0)
            # 原始图像
            Data.ori_img = load_image(ori_img_path)
            # 用于显示的原始图像
            Data.ori_img_display = image_to_display(Data.ori_img)
            # 预览图像
            try:
                Data.preview_img = change_img(Data.ori_img, get_operate())
            except:
                messagebox.showinfo('长宽数据错误', f'请输入正确的长度与宽度!')
                Data.preview_img = Data.ori_img
            finally:
                # 用于显示的预览图像
                Data.preview_img_display = image_to_display(Data.preview_img)

                ori_desc_label = Label(operate_field_right_top_frame,
                    text=f'原始图像(以选中的第一张为例)  高度:{Data.ori_img.size[1]} 宽度:{Data.ori_img.size[0]}', anchor='nw')
                preview_desc_label = Label(operate_field_right_top_frame,
                    text=f'修改预览(以选中的第一张为例)  高度:{Data.preview_img.size[1]} 宽度:{Data.preview_img.size[0]}', anchor='nw')

                ori_img_label = Label(operate_field_right_bottom_frame, image=Data.ori_img_display, relief="raised")
                preview_img_label = Label(operate_field_right_bottom_frame, image=Data.preview_img_display, relief="raised")

        # 图像预览区pack
        ori_desc_label.pack(side="left", fill="x", expand="yes")
        preview_desc_label.pack(side="right", fill="x", expand="yes")
        ori_img_label.pack(side="left", fill="both", expand="yes")
        preview_img_label.pack(side="right", fill="both", expand="yes")

    preview_button = Button(save_field_frame, text='预览修改效果', command=preview_img)

    # 对图片操作后保存
    # 针对gif进行分解处理
    def save_new_image(img_path, current_operate, save_path):
        # 如果不是gif图片
        if splitext(img_path)[1].lower() != ".gif":
            change_img(load_image(img_path), current_operate).save(save_path)
        # 如果是gif
        else:
            # 使用PIL获取gif的fps
            fps = 1000 / Image_open(img_path).info['duration']
            # 使用imageio库读出gif图片
            gif_img = mimread(img_path)
            # 将gif帧拆解分别处理
            gif_img = map(lambda x: change_img(fromarray(x), current_operate), gif_img)
            # 保存图片
            mimsave(save_path, gif_img, 'GIF', fps=fps)

    # 保存按钮
    def save_img():
        if not file_listbox.size():
            messagebox.showinfo('无法保存', f'尚未选择任何图片文件')
        elif operater_combobox.get() == "无操作":
            messagebox.showinfo('无法保存', f'未选择任何要执行的操作')
        else:
            current_operate = get_operate()
            # 是否可以保存
            can_save = True
            if operater_combobox.get() == "缩放":
                can_save = False
                # 测试书写的高宽是否正常
                try:
                    select_list = file_listbox.curselection()
                    # 如果选择的图片不为空
                    if select_list:
                        ori_img_path = file_listbox.get(file_listbox.curselection()[0])
                    # 否则取出列表第一个
                    else:
                        ori_img_path = file_listbox.get(0)
                    Data.ori_img = load_image(ori_img_path)
                    Data.preview_img = change_img(Data.ori_img, current_operate)
                except:
                    messagebox.showinfo('无法保存', f'高度与宽度数据填写有误!')
                else:
                    # 检验合格
                    can_save = True

            # 检查没问题,可以进行保存
            if can_save:
                # 获取保存模式
                output_method = Data.output_method.get()
                # 覆盖原文件输出
                if output_method == 0:
                    # 进行一次确认提示
                    if askokcancel('保存确认',
                                   '修改图片将覆盖图片原文件,该操作不可撤销,确认要对所有选择的图片文件执行该操作吗?'):
                        # 对所有原始图像操作并保存
                        for img_path in file_listbox.get(0, 'end'):
                            save_new_image(img_path, current_operate, img_path)
                        # 提示完成
                        messagebox.showinfo('保存完成', f'所有修改后的图片都已经覆盖原文件')
                elif output_method == 1:
                    # 进行一次确认提示
                    if askokcancel('保存确认',
                                   f'即将输出所有修改后的图片到指定文件夹{Data.output_path.get()},确认要对所有选择的图片文件执行该操作吗?'):
                        output_path = Data.output_path.get()
                        # 如果路径不存在,则创建输出文件夹
                        if not exists(output_path):
                            mkdir(output_path)
                        # 对所有原始图像操作并保存
                        for img_path in file_listbox.get(0, 'end'):
                            new_path = output_path + "/" + path_split(img_path)[1]
                            save_new_image(img_path, current_operate, new_path)

                        # 提示完成
                        messagebox.showinfo('保存完成', f'所有修改后的图片都已经输出到{Data.output_path.get()}')

    save_button = Button(save_field_frame, text='保存修改图像', command=save_img)



    # 信息框架-----------------------------------------------------------------------
    info_label = Label(info_field_frame, text=f"编写者: starvapour    版本: {Config.version}", anchor='e')


    # 组件pack-----------------------------------------------------------------------
    # 文件选择区pack
    file_scrolbar.pack(side="right", fill="y")
    file_listbox.pack(side="left", fill="both", expand=True)

    file_field_label.pack(side="left")
    clear_file_button.pack(side="right")
    delete_file_button.pack(side="right")
    add_file_button.pack(side="right")

    file_choice_describe_label.pack(side="left", fill="x")

    file_field_top_frame.pack(fill="x", expand="yes")
    file_field_middle_frame.pack(fill="x", expand="yes")
    file_field_bottom_frame.pack(fill="x", expand="yes")

    # 操作选择区pack
    operate_field_right_top_frame.pack(side="top", fill="x")
    operate_field_right_bottom_frame.pack(side="bottom", fill="both", expand="yes")

    operate_desc_label.pack(side="left", fill="y", expand="yes")
    operater_combobox.pack(side="right", fill="y", expand="yes")

    operate_field_left_top_frame.pack(side="top", fill="x")

    operate_field_left_frame.pack(side="left", fill="y")
    operate_field_right_frame.pack(side="right", fill="both", expand="yes")

    # 保存操作区pack
    output_desc_label.pack(side="left", fill="y")
    output_method_0.pack(side="left", fill="y")
    output_method_1.pack(side="left", fill="y")
    output_path_label.pack(side="left", fill="both")
    choose_output_path_button.pack(side="left", fill="y")

    save_button.pack(side="right", fill="y")
    preview_button.pack(side="right", fill="y")

    # 信息区pack
    info_label.pack(side="right", fill="y")

    # 主框架pack
    file_field_frame.pack(fill="x", expand="yes")
    operate_field_frame.pack(fill="both", expand="yes")
    save_field_frame.pack(fill="x", expand="yes")
    info_field_frame.pack(fill="x", expand="yes")

    root_frame.pack(fill="both", expand="yes")

    root.mainloop()


if __name__ == '__main__':
    main()

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值