Python GUI 开发实例: 用 tkinter 创建图像编辑器

一、什么是图像编辑器

图像编辑器是一种用于修改和增强图像的软件工具,在数字化时代中,图像编辑器越来越重要。

通过使用图像编辑器软件,我们可以对图像进行剪切、旋转、色彩调整、滤镜添加、文字添加等操作,让我们更好的表达和传递信息。

二、为什么使用 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()

原创文章,作者:GSQS,如若转载,请注明出处:https://www.506064.com/n/133906.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
GSQSGSQS
上一篇 2024-10-04 00:02
下一篇 2024-10-04 00:02

相关推荐

  • 如何查看Anaconda中Python路径

    对Anaconda中Python路径即conda环境的查看进行详细的阐述。 一、使用命令行查看 1、在Windows系统中,可以使用命令提示符(cmd)或者Anaconda Pro…

    编程 2025-04-29
  • Python计算阳历日期对应周几

    本文介绍如何通过Python计算任意阳历日期对应周几。 一、获取日期 获取日期可以通过Python内置的模块datetime实现,示例代码如下: from datetime imp…

    编程 2025-04-29
  • Python中引入上一级目录中函数

    Python中经常需要调用其他文件夹中的模块或函数,其中一个常见的操作是引入上一级目录中的函数。在此,我们将从多个角度详细解释如何在Python中引入上一级目录的函数。 一、加入环…

    编程 2025-04-29
  • Python周杰伦代码用法介绍

    本文将从多个方面对Python周杰伦代码进行详细的阐述。 一、代码介绍 from urllib.request import urlopen from bs4 import Bea…

    编程 2025-04-29
  • Python列表中负数的个数

    Python列表是一个有序的集合,可以存储多个不同类型的元素。而负数是指小于0的整数。在Python列表中,我们想要找到负数的个数,可以通过以下几个方面进行实现。 一、使用循环遍历…

    编程 2025-04-29
  • 蝴蝶优化算法Python版

    蝴蝶优化算法是一种基于仿生学的优化算法,模仿自然界中的蝴蝶进行搜索。它可以应用于多个领域的优化问题,包括数学优化、工程问题、机器学习等。本文将从多个方面对蝴蝶优化算法Python版…

    编程 2025-04-29
  • Python清华镜像下载

    Python清华镜像是一个高质量的Python开发资源镜像站,提供了Python及其相关的开发工具、框架和文档的下载服务。本文将从以下几个方面对Python清华镜像下载进行详细的阐…

    编程 2025-04-29
  • python强行终止程序快捷键

    本文将从多个方面对python强行终止程序快捷键进行详细阐述,并提供相应代码示例。 一、Ctrl+C快捷键 Ctrl+C快捷键是在终端中经常用来强行终止运行的程序。当你在终端中运行…

    编程 2025-04-29
  • Python程序需要编译才能执行

    Python 被广泛应用于数据分析、人工智能、科学计算等领域,它的灵活性和简单易学的性质使得越来越多的人喜欢使用 Python 进行编程。然而,在 Python 中程序执行的方式不…

    编程 2025-04-29
  • Python字典去重复工具

    使用Python语言编写字典去重复工具,可帮助用户快速去重复。 一、字典去重复工具的需求 在使用Python编写程序时,我们经常需要处理数据文件,其中包含了大量的重复数据。为了方便…

    编程 2025-04-29

发表回复

登录后才能评论