Python基于tkinter canvas实现图片裁剪功能

Python基于tkinter canvas实现图片裁剪功能的攻略如下:

1. 准备工作

在使用canvas进行图片裁剪之前,我们需要导入必要的库,包括tkinterPIL(Python Imaging Library,用于处理图片的库)。在命令行中输入以下代码进行安装:

pip install tkinter
pip install pillow

之后,我们需要在Python代码中导入这两个库:

from tkinter import *
from PIL import Image, ImageTk

2. 加载图片

首先,我们需要加载待裁剪的图片。在这里,我们可以使用PIL库中的Image类来加载图片。在加载图片时,我们需要把图片的路径传给Image类,并调用open方法来读取图片。读取完成后,我们可以使用ImageTk类将图片转化为Tkinter所支持的格式。

# 加载图片
img_path = "example.jpg"
img = Image.open(img_path)

# 将图片转换为Tkinter所支持的格式
tk_img = ImageTk.PhotoImage(img)

3. 创建canvas并展示图片

接下来,我们需要创建一个Canvas对象,并在其中展示图片。我们可以利用Canvascreate_image方法在画布中插入图片。

# 创建Canvas
canvas_width = 400
canvas_height = 400
canvas = Canvas(root, width=canvas_width, height=canvas_height)
canvas.pack()

# 在Canvas中插入图片
canvas.create_image(0, 0, anchor=NW, image=tk_img)

注意到这里我们的tk_img是前文中从PIL.Image转换而来的。anchor参数是表示图片的锚点,NW意味着左上角为起点。

现在打开运行程序,您可以看到一幅图片展示在界面中。

4. 裁剪图片

具体实现图片的裁剪,这里我们需要利用鼠标来绘制矩形,选取需要裁剪的区域。当鼠标按下之后,我们需要记录下鼠标的位置,并将长方形的起点设为这个位置。当鼠标拖动的时候,我们不断地更新长方形的大小和位置。最后当鼠标释放的时候,我们得到一个完整的长方形。接下来我们需要截取长方形内的像素,并保存为新的图片。

这里以第一个示例为例。我们需要在鼠标操作过程中实时绘制矩形。同时,为方便起见,我们定义了crop_image函数,并绑定到了“裁剪”按钮的事件上。

# 鼠标按下时记录起点,同时新绘制一个长方形
def on_button_press(event):
    # 保存起点位置
    global start_x, start_y
    start_x = canvas.canvasx(event.x)
    start_y = canvas.canvasy(event.y)

    # 创建一个新的矩形
    global cur_rect
    cur_rect = canvas.create_rectangle(
        start_x, start_y, 1, 1, outline='red', width=2)

# 鼠标拖动时更新长方形大小
def on_move_press(event):
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)

    # 长方形的区域
    canvas.coords(cur_rect, start_x, start_y, cur_x, cur_y)

# 鼠标释放时结束裁剪,并保存图片
def on_button_release(event):
    global start_x, start_y

    # 计算长方形的左上角和右下角坐标
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)
    x0 = min(start_x, cur_x)
    y0 = min(start_y, cur_y)
    x1 = max(start_x, cur_x)
    y1 = max(start_y, cur_y)

    # 裁剪图片
    cropped_img = img.crop((x0, y0, x1, y1))
    cropped_tk_img = ImageTk.PhotoImage(cropped_img)

    # 在新的窗口中展示裁剪后的图片
    new_window = Toplevel(root)
    new_window.title("Crop")
    new_canvas = Canvas(new_window, width=x1-x0, height=y1-y0)
    new_canvas.pack()
    new_canvas.create_image(0, 0, anchor=NW, image=cropped_tk_img)

    # 保存裁剪后的图片
    cropped_img.save("cropped.jpg")

同时,我们还需要在程序中绑定鼠标事件,并且创建一个按钮事件来触发裁剪操作:

# 绑定鼠标事件
canvas.bind("<ButtonPress-1>", on_button_press)
canvas.bind("<B1-Motion>", on_move_press)
canvas.bind("<ButtonRelease-1>", on_button_release)

# 创建一个“裁剪”按钮事件
def crop():
    on_button_release(None)
crop_button = Button(root, text="Crop", command=crop)
crop_button.pack()

5. 完整代码

第一个示例的完整代码如下:

from tkinter import *
from PIL import Image, ImageTk

# 加载图片
img_path = "example.jpg"
img = Image.open(img_path)

# 将图片转换为Tkinter所支持的格式
tk_img = ImageTk.PhotoImage(img)

# 创建Canvas
canvas_width = 400
canvas_height = 400
canvas = Canvas(root, width=canvas_width, height=canvas_height)
canvas.pack()

# 在Canvas中插入图片
canvas.create_image(0, 0, anchor=NW, image=tk_img)

# 鼠标按下时记录起点,同时新绘制一个长方形
def on_button_press(event):
    # 保存起点位置
    global start_x, start_y
    start_x = canvas.canvasx(event.x)
    start_y = canvas.canvasy(event.y)

    # 创建一个新的矩形
    global cur_rect
    cur_rect = canvas.create_rectangle(
        start_x, start_y, 1, 1, outline='red', width=2)

# 鼠标拖动时更新长方形大小
def on_move_press(event):
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)

    # 长方形的区域
    canvas.coords(cur_rect, start_x, start_y, cur_x, cur_y)

# 鼠标释放时结束裁剪,并保存图片
def on_button_release(event):
    global start_x, start_y

    # 计算长方形的左上角和右下角坐标
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)
    x0 = min(start_x, cur_x)
    y0 = min(start_y, cur_y)
    x1 = max(start_x, cur_x)
    y1 = max(start_y, cur_y)

    # 裁剪图片
    cropped_img = img.crop((x0, y0, x1, y1))
    cropped_tk_img = ImageTk.PhotoImage(cropped_img)

    # 在新的窗口中展示裁剪后的图片
    new_window = Toplevel(root)
    new_window.title("Crop")
    new_canvas = Canvas(new_window, width=x1-x0, height=y1-y0)
    new_canvas.pack()
    new_canvas.create_image(0, 0, anchor=NW, image=cropped_tk_img)

    # 保存裁剪后的图片
    cropped_img.save("cropped.jpg")

# 绑定鼠标事件
canvas.bind("<ButtonPress-1>", on_button_press)
canvas.bind("<B1-Motion>", on_move_press)
canvas.bind("<ButtonRelease-1>", on_button_release)

# 创建一个“裁剪”按钮事件
def crop():
    on_button_release(None)
crop_button = Button(root, text="Crop", command=crop)
crop_button.pack()

root.mainloop()

6. 第二个示例:添加撤销操作

第二个示例在第一个示例的基础上,添加了撤销操作。在进行裁剪操作时,用户可以利用Ctrl+Z快捷键撤销上一次操作。我们需要利用一个栈来记录所有的裁剪历史,并在进行撤销时取出最后一次裁剪。与第一个示例相比,这里引入了两个新的全局变量:cropped_img_stackcropped_tk_img_stack,分别用于记录裁剪历史。

示例代码如下:

from tkinter import *
from PIL import Image, ImageTk
import copy

# 加载图片
img_path = "example.jpg"
img = Image.open(img_path)

# 将图片转换为Tkinter所支持的格式
tk_img = ImageTk.PhotoImage(img)

# 创建Canvas
canvas_width = 400
canvas_height = 400
canvas = Canvas(root, width=canvas_width, height=canvas_height)
canvas.pack()

# 在Canvas中插入图片
canvas.create_image(0, 0, anchor=NW, image=tk_img)

# 记录裁剪历史的栈
cropped_img_stack = []
cropped_tk_img_stack = []
MAX_HISTORY = 10  # 最多记录10次历史记录

# 鼠标按下时记录起点,同时新绘制一个长方形
def on_button_press(event):
    # 保存起点位置
    global start_x, start_y
    start_x = canvas.canvasx(event.x)
    start_y = canvas.canvasy(event.y)

    # 创建一个新的矩形
    global cur_rect
    cur_rect = canvas.create_rectangle(
        start_x, start_y, 1, 1, outline='red', width=2)

# 鼠标拖动时更新长方形大小
def on_move_press(event):
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)

    # 长方形的区域
    canvas.coords(cur_rect, start_x, start_y, cur_x, cur_y)

# 鼠标释放时结束裁剪,并保存图片
def on_button_release(event):
    global start_x, start_y

    # 计算长方形的左上角和右下角坐标
    cur_x = canvas.canvasx(event.x)
    cur_y = canvas.canvasy(event.y)
    x0 = min(start_x, cur_x)
    y0 = min(start_y, cur_y)
    x1 = max(start_x, cur_x)
    y1 = max(start_y, cur_y)

    # 裁剪图片
    cropped_img = img.crop((x0, y0, x1, y1))
    cropped_tk_img = ImageTk.PhotoImage(cropped_img)

    # 在新的窗口中展示裁剪后的图片
    new_window = Toplevel(root)
    new_window.title("Crop")
    new_canvas = Canvas(new_window, width=x1-x0, height=y1-y0)
    new_canvas.pack()
    new_canvas.create_image(0, 0, anchor=NW, image=cropped_tk_img)

    # 保存裁剪后的图片
    cropped_img.save("cropped.jpg")

    # 记录裁剪历史
    cropped_img_stack.append(copy.deepcopy(img))
    cropped_tk_img_stack.append(copy.deepcopy(tk_img))
    if len(cropped_img_stack) > MAX_HISTORY:
        cropped_img_stack.pop(0)
        cropped_tk_img_stack.pop(0)

    # 更新原始图片为裁剪后的图片
    global img, tk_img
    img = cropped_img
    tk_img = cropped_tk_img
    canvas.delete("all")
    canvas.create_image(0, 0, anchor=NW, image=tk_img)

# 撤销操作
def undo(event):
    if len(cropped_img_stack) == 0:
        return

    # 取出上一次的裁剪历史并展示
    global img, tk_img
    img = cropped_img_stack.pop()
    tk_img = cropped_tk_img_stack.pop()
    canvas.delete("all")
    canvas.create_image(0, 0, anchor=NW, image=tk_img)

# 绑定鼠标事件
canvas.bind("<ButtonPress-1>", on_button_press)
canvas.bind("<B1-Motion>", on_move_press)
canvas.bind("<ButtonRelease-1>", on_button_release)

# 绑定快捷键事件
root.bind("<Control-z>", undo)

# 创建一个“裁剪”按钮事件
def crop():
    on_button_release(None)
crop_button = Button(root, text="Crop", command=crop)
crop_button.pack()

root.mainloop()

在这个示例中,我们使用了copy库的deepcopy函数来复制图片对象。在python中,对象之间的赋值(=img)通常会产生两个指向同一位置的指针,因此当修改指针指向对应位置时,会同时影响到被指向的两个指针。这里使用deepcopy函数可以完整地复制对象本身,而不是产生新的指针。

最后,我们利用root.bind将快捷键和对应的函数绑定在一起。

7. 结语

这就是Python基于tkinter canvas实现图片裁剪功能的全部攻略。希望这个教程可以帮助你更好地掌握canvas的基本用法,并能在需要时灵活运用canvas进行图片的裁剪。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python基于tkinter canvas实现图片裁剪功能 - Python技术站

(0)
上一篇 2023年6月13日
下一篇 2023年6月13日

相关文章

  • Python 复平面绘图实例

    先来简单介绍一下“Python 复平面绘图实例”。 Python 复平面绘图实例是一个可以让你在 Python 中使用复平面绘制图像的工具。复平面在数学中是一个非常重要的概念,它可以用来描述复数,也可以用来描述复变函数的性质。通过使用 Python 复平面绘图实例,你可以更加直观地了解复平面的性质,也可以更好地理解复数和复变函数。 下面,我将详细讲解“Pyt…

    python 2023年6月3日
    00
  • Python3 pip3 list 出现 DEPRECATION 警告的解决方法

    当我们在 Python3 环境下使用 pip3 list 命令查询安装的包时,有时会出现以下的警告信息: DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as…

    python 2023年5月14日
    00
  • Python使用asyncio异步时的常见问题总结

    以下是“Python使用asyncio异步时的常见问题总结”的完整攻略,其中包括了asyncio异步编程的常见问题及解决方法,以及两个示例说明。 Python使用asyncio异步时的常见问题总结 在使用asyncio异步编程时,我们可能会遇到一些常见问题。以下是一些常见问题及解方法: 1. 协程阻塞 在使用asyncio异步编程时,我们可能会遇到协程阻塞的…

    python 2023年5月13日
    00
  • 详解Python list和numpy array的存储和读取方法

    以下是详细讲解“详解Python list 和 numpy array 的存储和读取方法”的完整攻略。 在Python中,list和numpy array是两种常用的数据类型,本文将介绍它们的存储和读取方法。 Python list 的存储和读取方法 存储方法 Python list 可以使用pickle模块进行存储例如: import pickle lst…

    python 2023年5月13日
    00
  • python分布式编程实现过程解析

    Python分布式编程实现过程解析 分布式编程是目前互联网应用开发中非常重要的一部分,因为分布式架构可以提高系统的扩展性和可靠性。本篇文章将介绍如何使用Python实现分布式编程,并提供两个示例说明。 分布式编程概述 分布式编程是一种通过多台计算机共同完成一个任务的编程方式。通常情况下,分布式系统包含一个或多个服务器和多个客户端,并且服务器与客户端之间通过网…

    python 2023年5月19日
    00
  • Python实现通过文件路径获取文件hash值的方法

    我来为你详细讲解一下“Python实现通过文件路径获取文件hash值的方法”的完整攻略。 什么是hash值? 在计算机科学中,hash值是一个由特定算法对消息或文件产生的固定长度的数据,通常为16进制的字符串。哈希值可以用于数据的验证、比较和检索等操作,常用于数据加密、密码学、文件比较等领域。 通过文件路径获取文件hash值的方法 Python提供了多种方式…

    python 2023年5月20日
    00
  • python 字符串split的用法分享

    Python中的字符串是除数字外最常见的数据类型之一。在处理字符串时,经常需要将一个长的字符串拆分成多个小的部分。Python提供了split()函数来完成这个功能,本文将详细讲解split()的用法。 什么是split()函数 split()函数是Python字符串中的一个内置函数,用来将一个长的字符串拆分(分割)成多个小的部分,形成一个列表。语法如下: …

    python 2023年6月5日
    00
  • 详解Python的lambda函数用法

    详解Python的lambda函数用法 什么是lambda函数? lambda函数是Python中的匿名函数,它没有名称,并且可以在需要函数的任何位置定义和使用。Lambda函数有很短的语法,通常只包含一个语句,可以在不定义函数的情况下返回值。 lambda函数的语法 lambda arguments: expression lambda函数语法由三部分组成…

    python 2023年6月5日
    00
合作推广
合作推广
分享本页
返回顶部