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

带ui的简易图片批量处理工具(python)

  • 1. 使用方式
  • 2. 实现的功能
    • 2.1 旋转
    • 2.2 翻转
    • 2.3 缩放
    • 2.4 正方形裁剪
    • 2.5 百分比缩放
    • 2.6 像素化
    • 2.7 黑白化
  • 3. 代码实现

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

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

1. 使用方式

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

2. 实现的功能

2.1 旋转

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

2.2 翻转

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

2.3 缩放

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

2.4 正方形裁剪

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

2.5 百分比缩放

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

2.6 像素化

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

2.7 黑白化

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

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("<>", 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()

你可能感兴趣的:(软件小工具,python,ui)