如何使用 python 和 tkinter 创建和自由删除黑色图层的部分

问题描述 投票:0回答:1

使用 python 和 tkinter 库,我想创建一个程序,打开一个图像,然后在其上面绘制一个完全黑色的图层,然后使用鼠标单击+移动,删除该黑色图层的一部分(将其视为一个桌面游戏中的战争迷雾)或恢复部分黑色层。

当我想删除部分黑色图层时,我基本上绘制一个透明的形状,而当我恢复黑色图层时,我只是绘制一个全黑的形状。 这是我当前的实现:

import tkinter as tk
from tkinter import filedialog
from PIL import Image, ImageTk, ImageDraw

class ImageEraserApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Image Eraser App")

        # Open image file dialog
        self.image_path = filedialog.askopenfilename(
            filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")]
        )
        if not self.image_path:
            self.root.quit()

        # Load original image and create initial resized image
        self.original_image = Image.open(self.image_path)
        self.resized_image = self.original_image.copy()
        self.tk_image = ImageTk.PhotoImage(self.resized_image)

        # Create canvas to display image
        self.canvas = tk.Canvas(self.root)
        self.canvas.pack(fill=tk.BOTH, expand=True)
        self.canvas_image = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_image)

        # Initialize black layer for erasing and painting
        self.black_layer = Image.new('RGBA', self.original_image.size, (0, 0, 0, 255))
        self.layer_draw = ImageDraw.Draw(self.black_layer)
        self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)

        # Eraser size
        self.eraser_size = 20

        # Bind mouse events
        self.canvas.bind("<B1-Motion>", self.erase)
        self.canvas.bind("<B3-Motion>", self.paint_black)
        self.canvas.bind("<MouseWheel>", self.adjust_eraser_size)

        # Display initial black layer
        self.black_layer_id = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_black_layer)

    def adjust_eraser_size(self, event):
        # Adjust the eraser size based on mouse wheel scroll
        if event.delta > 0:
            self.eraser_size = min(100, self.eraser_size + 20)
        elif event.delta < 0:
            self.eraser_size = max(20, self.eraser_size - 20)

    def erase(self, event):
        # Draw on the black layer to "erase" it
        self.layer_draw.ellipse([
            (event.x - self.eraser_size, event.y - self.eraser_size),
            (event.x + self.eraser_size, event.y + self.eraser_size)
        ], fill=(0, 0, 0, 0))

        # Update the black layer on the canvas
        self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)
        self.canvas.itemconfig(self.black_layer_id, image=self.tk_black_layer)

    def paint_black(self, event):
        # Draw on the black layer to "paint" it
        self.layer_draw.ellipse([
            (event.x - self.eraser_size, event.y - self.eraser_size),
            (event.x + self.eraser_size, event.y + self.eraser_size)
        ], fill=(0, 0, 0, 255))

        # Update the black layer on the canvas
        self.tk_black_layer = ImageTk.PhotoImage(self.black_layer)
        self.canvas.itemconfig(self.black_layer_id, image=self.tk_black_layer)


if __name__ == "__main__":
    root = tk.Tk()
    app = ImageEraserApp(root)
    root.mainloop()

现在,随着我绘制的形状数量的增加,应用程序变得越来越无响应(如果我尝试在此基础上进行一些调整大小操作/放大/缩小,情况会变得更糟(最容易注意到的时候加载非常大的图像)

我的问题是如何优化它?我尝试实现它的方法是否是解决此类问题的正确方法?

python tkinter optimization canvas layer
1个回答
0
投票
def erase(self, event):
    if self.prev_x and self.prev_y:
        self.mask_draw.line([(self.prev_x, self.prev_y), (event.x, event.y)], fill=(0, 0, 0, 0), width=self.eraser_size * 2)
    self.update_mask(event.x, event.y, self.mask_draw.ellipse, (0, 0, 0, 0))
    self.prev_x = event.x
    self.prev_y = event.y

def paint_black(self, event):
    if self.prev_x and self.prev_y:
        self.mask_draw.line([(self.prev_x, self.prev_y), (event.x, event.y)], fill=(0, 0, 0, 255), width=self.eraser_size * 2)
    self.update_mask(event.x, event.y, self.mask_draw.ellipse, (0, 0, 0, 255))
    self.prev_x = event.x
    self.prev_y = event.y

def reset_prev_pos(self, event):
    self.prev_x = None
    self.prev_y = None

要跟踪过去的鼠标位置,请使用

prev_x
prev_y
变量绘制一条连接前一个坐标和当前坐标的线。

平滑擦除和绘画

erase和paint_black方法使用self.mask_draw.line从上一个位置到当前位置绘制一条线。这确保了两个位置之间的每个点都会受到影响,从而形成平滑的轨迹。 要在释放鼠标按钮后重置之前的位置,请使用

reset_prev_pos
方法,该方法连接到
<ButtonRelease-1>
<ButtonRelease-3>
事件。

这些修改应该会带来更一致的擦除和绘画体验,具有连续的轨迹而不是不连续的部分。

© www.soinside.com 2019 - 2024. All rights reserved.