简体   繁体   中英

python tkinter canvas put background image in center

all

This is my first time using Tkinter.

My job is to load images, draw bounding box on the objects and give them correct label like this:

文本

Here is my code

class ExampleApp(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        self.x = 0
        self.y = 0
        self.img = ImageTk.PhotoImage(file = "0.png") 
        self.canvas = tk.Canvas(self, width=1080, height=720)
        self.canvas.create_image(20, 20, anchor=NW, image=self.img)
        self.canvas.pack()
        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<Motion>", self.moving)

        self.start_x = None
        self.start_y = None
        self.rect = None


    def right_click(self, event):
        self.canvas.delete(self.canvas.find_closest(event.x, event.y))

    def moving(self, event):
        all_item = self.canvas.find_all()

        for i in all_item[1:]:
            for i in all_item:
                self.canvas.itemconfig(i, width=2.0, outline='red')

            target = self.canvas.find_closest(event.x, event.y)[0]
            self.canvas.itemconfig(target, width=4.0, outline='yellow')

    def on_button_press(self, event):
        self.start_x = event.x
        self.start_y = event.y
        self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, width=2.0, outline='red')

    def on_move_press(self, event):
        curX, curY = (event.x, event.y)
        self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)

    def on_button_release(self, event):
        pass

if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()

What I want is when I move the mouse inside the rectangle, it would turn yellow.

The problem is the rectangle turned yellow only when the mouse was on the edge of the rectangle exactly.

Because the background picture is one of the canvas item too, so the find_closest function will always

return the picture when the mouse is not on the edge of the rectangle.

Anyone has better idea ??

For filled rectangles you would need only

create_rectangle(..., activeoutline="yellow") 

without usinig function moving() .


For empty rectangles you have to get all rectangles and using canvas.bbox(id) get region used by rectangle (x1,y1,x2,y2) and compare with event.x, event.y

To check only rectangles I added tag to rectangles

self.canvas.create_rectangle(..., tag='rect')

and later I can get only rectangles with

all_rect = self.canvas.find_withtag('rect')

def moving(self, event):
    all_rect = self.canvas.find_withtag('rect')

    x = event.x
    y = event.y

    for i in all_rect:
        x1, y1, x2, y2 = self.canvas.bbox(i)

        if x1 <= x <= x2 and y1 <= y <= y2:
            self.canvas.itemconfig(i, width=4.0, outline='yellow')
        else:
            self.canvas.itemconfig(i, width=2.0, outline='red')

Eventually instead of using find_withtag() and bbox() you could use list to keep

(rect_id, (x1,y1,x2,y3), text_id) 

and use it in moving() to find rect and then you could change also text in rect.


Full example code.

I added overlaping rectangles to test if it will change color for two rectangles.

import tkinter as tk
from PIL import ImageTk

class ExampleApp(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)

        self.x = 0
        self.y = 0

        self.img = ImageTk.PhotoImage(file="image.jpg") 
        self.canvas = tk.Canvas(self, width=1080, height=720)
        self.canvas.pack()

        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<Motion>", self.moving)

        self.canvas.create_image(20, 20, anchor='nw', image=self.img)

        self.canvas.create_rectangle(50, 50, 100, 100, width=2.0, outline='red', tag='rect')
        self.canvas.create_rectangle(200, 200, 250, 250, width=2.0, outline='red', tag='rect')
        self.canvas.create_rectangle(75, 75, 225, 225, width=2.0, outline='red', tag='rect')

        self.start_x = None
        self.start_y = None
        self.rect = None

    def right_click(self, event):
        self.canvas.delete(self.canvas.find_closest(event.x, event.y))

    def moving(self, event):
        all_rect = self.canvas.find_withtag('rect')

        x = event.x
        y = event.y

        for i in all_rect:
            x1, y1, x2, y2 = self.canvas.bbox(i)
            if x1 <= x <= x2 and y1 <= y <= y2:
                self.canvas.itemconfig(i, width=4.0, outline='yellow')
            else:
                self.canvas.itemconfig(i, width=2.0, outline='red')

    def on_button_press(self, event):
        self.start_x = event.x
        self.start_y = event.y
        self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, width=2.0, outline='red', tag='rect')

    def on_move_press(self, event):
        curX, curY = (event.x, event.y)
        self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)

    def on_button_release(self, event):
        pass

if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()

EDIT: code which uses list (rect_id, (x1,y1,x2,y3), text_id) to change color of rectange and text. I use on_button_release() to add new rectangle to this list.

import tkinter as tk
from PIL import ImageTk

class ExampleApp(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)

        self.x = 0
        self.y = 0

        self.img = ImageTk.PhotoImage(file="image.jpg") 
        self.canvas = tk.Canvas(self, width=1080, height=720)
        self.canvas.pack()

        self.canvas.bind("<ButtonPress-1>", self.on_button_press)
        self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
        self.canvas.bind("<B1-Motion>", self.on_move_press)
        self.canvas.bind("<Motion>", self.moving)

        self.canvas.create_image(20, 20, anchor='nw', image=self.img)

        self.all_rect = []

        rect_id = self.canvas.create_rectangle(50, 50, 100, 100, width=2.0, outline='red')
        text_id = self.canvas.create_text(55, 57, text='A', fill="red")
        self.all_rect.append((rect_id, (50, 50, 100, 100), text_id))

        rect_id = self.canvas.create_rectangle(200, 200, 250, 250, width=2.0, outline='red')
        text_id = self.canvas.create_text(205, 207, text='B', fill="red")
        self.all_rect.append((rect_id, (200, 200, 250, 250), text_id))

        rect_id = self.canvas.create_rectangle(75, 75, 225, 225, width=2.0, outline='red')
        text_id = self.canvas.create_text(80, 82, text='C', fill="red")
        self.all_rect.append((rect_id, (75, 75, 225, 225), text_id))

        self.start_x = None
        self.start_y = None
        self.rect = None


    def right_click(self, event):
        self.canvas.delete(self.canvas.find_closest(event.x, event.y))

    def moving(self, event):

        x = event.x
        y = event.y

        for rect_id, bbox, text_id in self.all_rect:
            x1, y1, x2, y2 = bbox
            if x1 <= x <= x2 and y1 <= y <= y2:
                self.canvas.itemconfig(rect_id, width=4.0, outline='yellow')
                if text_id:
                    self.canvas.itemconfig(text_id, fill='yellow')
            else:
                self.canvas.itemconfig(rect_id, width=2.0, outline='red')
                if text_id:
                    self.canvas.itemconfig(text_id, fill='red')

    def on_button_press(self, event):
        self.start_x = event.x
        self.start_y = event.y
        self.rect = self.canvas.create_rectangle(self.x, self.y, 1, 1, width=2.0, outline='red')

    def on_move_press(self, event):
        curX, curY = (event.x, event.y)
        self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)

    def on_button_release(self, event):
        curX, curY = (event.x, event.y)

        x1, x2 = sorted((curX, self.start_x)) # I need `x1 <= x2` in `moving()`
        y1, y2 = sorted((curY, self.start_y)) # I need `y1 <= y2` in `moving()`

        self.all_rect.append((self.rect, (x1, y1, x2, y2), None) )

if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM