一、什么是图像编辑器
图像编辑器是一种用于修改和增强图像的软件工具,在数字化时代中,图像编辑器越来越重要。
通过使用图像编辑器软件,我们可以对图像进行剪切、旋转、色彩调整、滤镜添加、文字添加等操作,让我们更好的表达和传递信息。
二、为什么使用 Tkinter
在 Python 中,有很多 GUI 工具包可供使用,如 Qt、wxPython 等,但是,Tkinter 是 Python 默认内置的,因此使用 Tkinter 不需要安装任何额外的库。
Tkinter 是一个功能强大、简单易用、跨平台的 GUI 工具包,它支持不同平台的标准 GUI 工具。
虽然 Tkinter 本身可能显得有些陈旧,但是它非常灵活,可以应用于许多应用程序的开发。
三、图像编辑器开发思路
我们想要开发的图像编辑器需要具备以下几个基本功能:
- 打开、保存图片
- 缩放、旋转图片
- 添加文字
- 添加滤镜和边框
四、打开、保存图片
使用 Tkinter 自带的组件,我们可以轻松创建一个文件选择对话框,使用 Pillow 库读取和保存图片,如下:
from tkinter import filedialog from PIL import Image, ImageTk def open_image(): file_path = filedialog.askopenfilename() image = Image.open(file_path) photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo def save_image(): file_path = filedialog.asksaveasfilename(defaultextension='.jpg') image = Image.frombytes('RGB', canvas.size(), canvas.postscript(colormode='color')) image.save(file_path)
五、缩放、旋转图片
我们可以使用 Tkinter 中的 Canvas 组件来显示图片,并使用鼠标滚动事件实现缩放功能,使用鼠标拖动事件实现旋转功能。
from math import cos, sin def scale(delta): x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) s = delta/120 if image_id: canvas.scale(image_id, x, y, s, s) def rotate(event): global angle x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) if image_id: angle += 5 angle %= 360 rad = angle/180 * pi c, s = cos(rad), sin(rad) canvas.coords(image_id, *rotate_points(x, y, c, s)) def rotate_points(x, y, c, s): xr = x-c*x+s*y yr = y-s*x+c*y return xr-50, yr-50, xr+50, yr-50, xr+50, yr+50, xr-50, yr+50
六、添加文字
我们可以使用 Tkinter 自带的 Text 组件和 Canvas 组件来添加文字,使用鼠标点击事件添加文字,并可以使用右键菜单修改文字字体和大小。
from tkinter import font, Menu class Textbox: def __init__(self, x, y): self.text = tk.Text(canvas, height=1, font='TkDefaultFont 12', bd=0, highlightthickness=0) self.text.insert('0.0', 'Text') self.text.focus_set() self.text.bind('', self.submit) self.id = canvas.create_window(x, y, window=self.text, anchor='nw') def submit(self, event): text = self.text.get('1.0', 'end-1c') canvas.delete(self.id) self.draw_text(event, text) def draw_text(self, event, text): x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) font_name = font.nametofont('TkDefaultFont').actual()['family'] font_size = font.nametofont('TkDefaultFont').actual()['size'] canvas.create_text(x, y, text=text, font=(font_name, font_size), tags='text') def font_setting(): tag = canvas.gettags(canvas.find_withtag('current'))[0] if tag == 'text': font_size = font.nametofont('TkDefaultFont').actual()['size'] new_size = simpledialog.askinteger('大小', '输入新字号', initialvalue=font_size) font.nametofont('TkDefaultFont').configure(size=new_size)
七、添加滤镜和边框
使用 Pillow 库中的 ImageFilter 可以添加图像滤镜,使用 ImageOps 可以添加图像边框。
from PIL import ImageFilter, ImageOps def add_filter(): global image image = image.filter(ImageFilter.CONTOUR) photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo def add_border(): global image image = ImageOps.expand(image, border=20, fill='white') photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo
八、完整代码示例
下面是一个完整的图像编辑器代码示例:
from tkinter import * from tkinter import filedialog, simpledialog, font from PIL import Image, ImageTk, ImageFilter, ImageOps from math import cos, sin, pi root = Tk() root.title('Image Editor') canvas = Canvas(root, bg='white') canvas.pack(fill=BOTH, expand=True) image = None angle = 0 image_id = None def open_image(): global image, image_id file_path = filedialog.askopenfilename() image = Image.open(file_path) photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo image_id = canvas.find_withtag('image')[0] def save_image(): file_path = filedialog.asksaveasfilename(defaultextension='.jpg') image = Image.frombytes('RGB', canvas.size(), canvas.postscript(colormode='color')) image.save(file_path) def scale(delta): x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) s = delta/120 if image_id: canvas.scale(image_id, x, y, s, s) def rotate(event): global angle x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) if image_id: angle += 5 angle %= 360 rad = angle/180 * pi c, s = cos(rad), sin(rad) canvas.coords(image_id, *rotate_points(x, y, c, s)) def rotate_points(x, y, c, s): xr = x-c*x+s*y yr = y-s*x+c*y return xr-50, yr-50, xr+50, yr-50, xr+50, yr+50, xr-50, yr+50 def add_filter(): global image image = image.filter(ImageFilter.CONTOUR) photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo def add_border(): global image image = ImageOps.expand(image, border=20, fill='white') photo = ImageTk.PhotoImage(image) canvas.create_image(0, 0, image=photo, anchor='nw') canvas.image = photo class Textbox: def __init__(self, x, y): self.text = Text(canvas, height=1, font='TkDefaultFont 12', bd=0, highlightthickness=0) self.text.insert('0.0', 'Text') self.text.focus_set() self.text.bind('', self.submit) self.id = canvas.create_window(x, y, window=self.text, anchor='nw') def submit(self, event): text = self.text.get('1.0', 'end-1c') canvas.delete(self.id) self.draw_text(event, text) def draw_text(self, event, text): x, y = canvas.canvasx(event.x), canvas.canvasy(event.y) font_name = font.nametofont('TkDefaultFont').actual()['family'] font_size = font.nametofont('TkDefaultFont').actual()['size'] canvas.create_text(x, y, text=text, font=(font_name, font_size), tags='text') def font_setting(): tag = canvas.gettags(canvas.find_withtag('current'))[0] if tag == 'text': font_size = font.nametofont('TkDefaultFont').actual()['size'] new_size = simpledialog.askinteger('大小', '输入新字号', initialvalue=font_size) font.nametofont('TkDefaultFont').configure(size=new_size) menu = Menu(root) root.config(menu=menu) file_menu = Menu(menu, tearoff=0) menu.add_cascade(label="文件", menu=file_menu) file_menu.add_command(label="打开", command=open_image) file_menu.add_command(label="保存", command=save_image) edit_menu = Menu(menu, tearoff=0) menu.add_cascade(label="编辑", menu=edit_menu) edit_menu.add_command(label="滤镜", command=add_filter) edit_menu.add_command(label="添加边框", command=add_border) text_menu = Menu(menu, tearoff=0) menu.add_cascade(label="文本", menu=text_menu) text_menu.add_command(label="添加文字", command=lambda: Textbox(event.x, event.y)) text_menu.add_command(label="修改字体", command=font_setting) canvas.bind(" ", rotate) canvas.bind(" ", lambda event : scale(-120)) canvas.bind(" ", lambda event : scale(120)) root.mainloop()