删除Tkinter画布上的图像部分,暴露下面的另一个图像 [英] Erase part of image on Tkinter canvas, exposing another image underneath

查看:934
本文介绍了删除Tkinter画布上的图像部分,暴露下面的另一个图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(Python 2.7)。我有一个Tkinter帆布有两个图像,既是画布的高度和宽度,所以他们覆盖整个窗口。一个图像在另一个的顶部。我想使用鼠标,能够擦除顶部图像的一部分,无论我想要什么,从而暴露底部的图像。这是可能吗?

(Python 2.7). I have a Tkinter canvas with two images that are both the height and width of the canvas, so they cover the whole window. One image is on top of the other. I want to, using the mouse, be able to erase part of the top image wherever I want, thus exposing the bottom image. Is this possible?

我很好奇如何实现下面的Home.erase方法绑定到Tkinter运动事件。

I'm curious in how to implement the Home.erase method below which is bound to a Tkinter motion event.

# -*- coding: utf-8 -*-

import io
from PIL import Image, ImageTk
import Tkinter as tk

#Image 2 is on top of image 1.
IMAGE1_DIR = "C:/path_to_image/image1.png"
IMAGE2_DIR = "C:/path_to_image/image2.png"

def create_image(filename, width=0, height=0):
    """
    Returns a PIL.Image object from filename - sized
    according to width and height parameters.

    filename: str.
    width: int, desired image width.
    height: int, desired image height.

    1) If neither width nor height is given, image will be returned as is.
    2) If both width and height are given, image will resized accordingly.
    3) If only width or only height is given, image will be scaled so specified
    parameter is satisfied while keeping image's original aspect ratio the same. 
    """
    with open(filename, "rb") as f:
        fh = io.BytesIO(f.read())

    #Create a PIL image from the data
    img = Image.open(fh, mode="r")

    #Resize if necessary.
    if not width and not height:
        return img
    elif width and height:
        return img.resize((int(width), int(height)), Image.ANTIALIAS)
    else:  #Keep aspect ratio.
        w, h = img.size
        scale = width/float(w) if width else height/float(h)
        return img.resize((int(w*scale), int(h*scale)), Image.ANTIALIAS)


class Home(object):
    """
    master: tk.Tk window.
    screen: tuple, (width, height).
    """
    def __init__(self, master, screen):
        self.screen = w, h = screen
        self.master = master

        self.frame = tk.Frame(self.master)
        self.frame.pack()
        self.can = tk.Canvas(self.frame, width=w, height=h)
        self.can.pack()

        #Photos will be as large as the screen.
        p1 = ImageTk.PhotoImage(image=create_image(IMAGE1_DIR, w, h))
        p2 = ImageTk.PhotoImage(image=create_image(IMAGE2_DIR, w, h))

        ## Place photos in center of screen.
        ## Create label to retain a reference to image so it doesn't dissapear.

        self.photo1 = self.can.create_image((w//2, h//2), image=p1)        
        label1 = tk.Label(image=p1)
        label1.image = p1

        #On top.
        self.photo2 = self.can.create_image((w//2, h//2), image=p2)
        label2 = tk.Label(image=p2)
        label2.image = p2

        #Key bindings.
        self.master.bind("<Return>", self.reset)
        self.master.bind("<Motion>", self.erase)

    #### Key Bindings ####
    def reset(self, event):
        """ Enter/Return key. """
        self.frame.destroy()
        self.__init__(self.master, self.screen)

    def erase(self, event):
        """
        Mouse motion binding.
        Erase part of top image (self.photo2) at location (event.x, event.y),
        consequently exposing part of the bottom image (self.photo1).
        """
        pass


def main(screen=(500, 500)):
    root = tk.Tk()
    root.resizable(0, 0)
    Home(root, screen)

    #Place window in center of screen.
    root.eval('tk::PlaceWindow %s center'%root.winfo_pathname(root.winfo_id()))

    root.mainloop()


if __name__ == '__main__':
    main()


推荐答案

感谢@martineau的建议!这是工作代码。

Thanks to @martineau for the suggestions! Here is the working code.

from PIL import Image, ImageTk
import Tkinter as tk

#Image 2 is on top of image 1.
IMAGE1_DIR = "C:/path/image1.PNG"
IMAGE2_DIR = "C:/path/image2.PNG"

#Brush size in pixels.
BRUSH = 5
#Actual size is 2*BRUSH

def create_image(filename, width=0, height=0):
    """
    Returns a PIL.Image object from filename - sized
    according to width and height parameters.

    filename: str.
    width: int, desired image width.
    height: int, desired image height.

    1) If neither width nor height is given, image will be returned as is.
    2) If both width and height are given, image will resized accordingly.
    3) If only width or only height is given, image will be scaled so specified
    parameter is satisfied while keeping image's original aspect ratio the same. 
    """
    #Create a PIL image from the file.
    img = Image.open(filename, mode="r")

    #Resize if necessary.
    if not width and not height:
        return img
    elif width and height:
        return img.resize((int(width), int(height)), Image.ANTIALIAS)
    else:  #Keep aspect ratio.
        w, h = img.size
        scale = width/float(w) if width else height/float(h)
        return img.resize((int(w*scale), int(h*scale)), Image.ANTIALIAS)


class Home(object):
    """
    master: tk.Tk window.
    screen: tuple, (width, height).
    """
    def __init__(self, master, screen):
        self.screen = w, h = screen
        self.master = master

        self.frame = tk.Frame(self.master)
        self.frame.pack()
        self.can = tk.Canvas(self.frame, width=w, height=h)
        self.can.pack()

        self.image1 = create_image(IMAGE1_DIR, w, h)
        self.image2 = create_image(IMAGE2_DIR, w, h)        

        #Center of screen.
        self.center = w//2, h//2
        #Start with no photo on the screen.
        self.photo = False

        #Draw photo on screen.
        self.draw()

        #Key bindings.
        self.master.bind("<Return>", self.reset)
        self.master.bind("<Motion>", self.erase)

    def draw(self):
        """
        If there is a photo on the canvas, destroy it.
        Draw self.image2 on the canvas.
        """            
        if self.photo:
            self.can.delete(self.photo)
            self.label.destroy()

        p = ImageTk.PhotoImage(image=self.image2)
        self.photo = self.can.create_image(self.center, image=p)
        self.label = tk.Label(image=p)
        self.label.image = p

    #### Key Bindings ####
    def reset(self, event):
        """ Enter/Return key. """
        self.frame.destroy()
        self.__init__(self.master, self.screen)

    def erase(self, event):
        """
        Mouse motion binding.
        Erase part of top image (self.photo2) at location (event.x, event.y),
        consequently exposing part of the bottom image (self.photo1).
        """        
        for x in xrange(event.x-BRUSH, event.x+BRUSH+1):
            for y in xrange(event.y-BRUSH, event.y+BRUSH+1):
                try:
                    p = self.image1.getpixel((x, y))
                    self.image2.putpixel((x, y), p)
                except IndexError:
                    pass

        self.draw()



def main(screen=(500, 500)):
    root = tk.Tk()
    root.resizable(0, 0)
    Home(root, screen)

    #Place window in center of screen.
    root.eval('tk::PlaceWindow %s center'%root.winfo_pathname(root.winfo_id()))

    root.mainloop()


if __name__ == '__main__':
    main()

这篇关于删除Tkinter画布上的图像部分,暴露下面的另一个图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆