基于Python PIL实现简单图片格式转化器

时间:2024-01-27 18:34:38

基于Python PIL实现简单图片格式转化器

1、简介

*提示:阅读本文,默认你对Python有一定了解,并且安装有PIL,对tkinter有一定使用基础。文中所有代码皆在Python3版本上实现,请务必注意*

Pyhton PIL库提供了许多图片处理功能,理论上可以借助此完成图片格式转换功能,在配合Python tkinter库绘制前端页面,基本上可以实现一个简单图片格式转换器

2、前期资料准备

2.1逻辑支持

2.1.1如何实现图片格式转换?

​ Python PIL库提供了许多图片处理功能,现在我们只需要其中一个功能:图片格式转换支持我们实现我们的图片格式转换器。具体如下:

from PIL import Image	# 引入PIL Image提供图片格式转换功能
file_path = \'D:/test/test.png\' #测试图片
photo = Image.open(file_path)
photo.save(\'D:/test/new_test.gif\')	# save会根据后缀名转换为特定格式

注意:在转换为.jgp格式图片时,需要将图片模式转换为RGB模式,即在save语句之前加上:photo = photo.convert(\'RGB\') 更多参考PIL save语句说明

​ 当然,这里我们必须声明一点:PIL提供的格式转换支持是有限的,具体参考PIL支持图片格式

2.1.2如何保存需要大小的图片?

​ 具体参考:

from PIL import Image	# 引入PIL Image提供图片格式转换功能
file_path = \'D:/test/test.png\' #测试图片
photo = Image.open(file_path)
photo = photo.resize((200,300)) # 将图片大小转换为(width,height)200x300大小
photo.save(\'D:/test/new_test.gif\')	# save会根据后缀名转换为特定格式

​ 到此,逻辑支持部分基本上够我们实现后端的图片格式转换了。

2.2前端页面支持

​ 前端窗口大致上如下图所示:

​ 这里强调一下基本的页面构成:图片预览、转换格式下拉选择(当然,这个格式是可控的,我们可以添加自己需要的格式,前提是在PIL支持的格式转换范围)、图片选择按钮、转换大小选择和保存图片按钮。

​ 当然,需要强调的一点,在图片选择和保存的过程中,我们添加了tkinter中的messagebox组件来提示用户可能出现的错误操作。

​ 下面将简单的介绍一下容易出现错误的组件。

2.2.1预览图片

​ 我们通过一个Label组件实现预览需要转换的图片,为Label中image属性添加为要加载的图片。这里,我们使用PIL中的ImageTk.PhotoImage来加载预览图片,而不tkinter.PhotoImage,主要目的是为了使用Image来裁剪图片来保证预览图片大小一致,防止图片显示不完全。具体参考下面的例子:

import tkinter
from PIL import Image, ImageTk

file_path = \'D:/test/test.png\'

root = tkinter.Tk()

photo = ImageTk.PhotoImage(Image.open(file_path).resize((200,300)))	# 预览图片大小为200x300

previewPhoto = tkinter.Label(root, image = photo).pack()

root.mainloop()

​ 这里,由于需要不断切换Label的image属性,可能会遇到Python tkinter之PhotoImage图片显示问题

2.2.2下拉框组件

​ 下拉框组件需要使用带ttk中的Combobox来实现,基本使用可参考:

import tkinter
from tkinter import ttk

root = tkinter.Tk()

comboBox = tkinter.ttk.Combobox(root, value = [\'png\',\'jpg\'])
comboBox.current(0)	# 当前显示第一个 
comboBox.pack()

root.mainloop()

​ 将会产生下面的效果:

2.2.3图片文件选择

​ 通过tkinter.filedialog中的文件选择框组件:askopenfilename()返回打开的图片文件的文件路径,具体参考:

import tkinter.filedialog

file_path = tkinter.filedialog.askopenfilename(title = \'选择文件\')
print(file_path)

title参数设置文件选择框显示时的窗体标题

2.2.4 保存文件到目标路径

​ 通过tkinter.filedialog中的文件保存框组件:asksaveasfilename()返回保存的图片文件的文件路径,具体参考:

import tkinter.filedialog

file_savepath = tkinter.filedialog.asksaveasfilename(title = \'保存文件\',filetypes = ((\'JPG\',\'.jpg\'),(\'PNG\',\'.png\')))
print(file_savepath)

title参数设置文件选择框显示时的窗体标题,filetypes参数设置了保存文件时提供的文件格式下拉选项,当前设置会出现如下文件格式选项:

注意:当前只是前端页面提供的保存页面,实际保存文件还是在后端实现

3、组装完成所有需求

​ 前面的分析已经够我们组装出我们的需求了,现在具体组装代码如下:

import tkinter.filedialog, tkinter.messagebox, PIL
import tkinter, os
from PIL import Image, ImageTk
from tkinter import ttk

class PFC:
    \'\'\'
        Picture Format Conversion
        图片格式转换器
    \'\'\'
    
    title = \'图片格式转换器\'
    quit_all = True
    filetypes = [\'png\',\'gif\',\'jpg\']
    def __init__(self, master, quit_all=None, title=None, filetypes=None):
        if title is None: title = self.title
        if quit_all is None: quit_all = self.quit_all
        if filetypes is None: filetypes = self.filetypes
        
        self.__master = master
        self.__top = tkinter.Toplevel(self.__master)
        self.__top.geometry(\'%dx%d\'%(300,300))
        self.__top.title(title)
        self.__top.resizable(0,0)

        self.__preview_photo = ImageTk.PhotoImage(Image.new(\'RGB\',(92,48),(255,255,255)))
        self.__source = None

        self.__default_width = tkinter.StringVar()
        self.__default_width.set(\'width\')
        self.__default_height = tkinter.StringVar()
        self.__default_height.set(\'height\')
        
        self.__showPhoto = tkinter.Label(self.__top, bg=\'white\', width = 300, height = 200, image = self.__preview_photo, borderwidth = 13, text = \'图片\')
        tkinter.Button(self.__top, text = \'选择文件\', width = 10, bg = \'#fff\', command = self.__openFile).place(x = 200, y = 230)
        tkinter.Label(self.__top, text =\'转换格式:\').place(y = 235)
        self.__savefiletype = ttk.Combobox(self.__top, width = 14,  value = filetypes)
        tkinter.Label(self.__top, text=\'转换大小:\').place(y = 270)
        self.__savefilewidth = tkinter.Entry(self.__top, width = 6, textvariable = self.__default_width)
        tkinter.Label(self.__top, text = \'X\').place(x = 112, y = 270)
        self.__savefileheight = tkinter.Entry(self.__top,  width = 6, textvariable = self.__default_height)
        tkinter.Button(self.__top, text = \'保存图片\', width = 10, bg = \'#fff\', command = self.__saveFile).place(x = 200, y = 265)

        self.__showPhoto.pack()
        self.__savefiletype.place(x = 60, y = 235)
        self.__savefilewidth.place(x = 60, y = 270)
        self.__savefileheight.place(x = 133, y = 270)
        self.__savefiletype.current(0)    
        
        self.__top.protocol(\'WM_DELETE_WINDOW\', lambda:self.quit(flag=quit_all))

    def __getPreviewSize(self):
        return (180, int(180/self.__source.size[0]*self.__source.size[1]))

    def __openFile(self):
        filename = tkinter.filedialog.askopenfilename(title=\'选择文件\')
        if filename != \'\':
            try:
                self.__source = Image.open(filename)
            except:
                tkinter.messagebox.showerror(\'资源错误\',\'打开文件错误,请确保打开图片文件\')
            else:
                self.__preview_photo =  ImageTk.PhotoImage(self.__source.resize(self.__getPreviewSize()))
                self.__showPhoto[\'image\'] = self.__preview_photo
                self.__default_width.set(str(self.__source.size[0]))
                self.__default_height.set(str(self.__source.size[1]))

    def __saveFile(self):
        if None != self.__source:
            try:
                savesize = (int(self.__savefilewidth.get()),int(self.__savefileheight.get()))
            except:
                tkinter.messagebox.showerror(\'类型错误\', \'输入中含有非数字字符\')
            else:
                filetype = self.__savefiletype.get()
                savefilename = tkinter.filedialog.asksaveasfilename(title = \'保存文件\',filetypes=[(filetype.upper(),\'.\'+filetype)])
                self.__source = self.__source.resize(savesize,Image.ANTIALIAS)
                if filetype == \'jpg\':
                    self.__source = self.__source.convert(\'RGB\')
                if savefilename !=\'\':
                    try :
                        self.__source.save(savefilename+\'.\'+filetype)
                    except:
                        tkinter.messagebox.showerror(\'保存失败\',\'图片转换失败\')
                    else:
                        tkinter.messagebox.showinfo(\'保存成功\',\'图片转换成功\')
        else:
            tkinter.messagebox.showwarning(\'文件为空\', \'请先选择一个文件\')
            
    def quit(self, flag, event=None):
        if flag:
            self.__master.quit()
        else:
            self.__top.destroy()
        
            
if __name__ == "__main__":
    root = tkinter.Tk()
    root.withdraw()
    pfc = PFC(root)
    root.mainloop()

​ 在布局上偷了点巧:完全依靠绝对定位(对tkinter布局我也不行)。运行后的效果图如下:

​ 此工具为个人一时兴起所作,个人代码水平有限,查阅了许多资料,如有不妥望见谅。