简体   繁体   中英

Python/Tkinter - Identify object on click

I am trying to create a program that changes the object colour on click from white to black or from white to black depending of the previous colour. I would want the program change the colour only if the object is a rectangle. How can I make the this happen?

Here is my code:

import tkinter as tk

root = tk.Tk()

cv = tk.Canvas(root, height=800, width=800)
cv.pack()

def onclick(event):
    item = cv.find_closest(event.x, event.y)

    current_color = cv.itemcget(item, 'fill')

    if current_color == 'black':
        cv.itemconfig(item, fill='white')

    else:
        cv.itemconfig(item, fill='black')


cv.bind('<Button-1>', onclick)

cv.create_line(50, 50, 60, 60, width=2)

cv. create_rectangle(80, 80, 100, 100)

root.mainloop()

In this code the program changes the fill colour for any object. I would want it to change it only for rectangles.

Thanks for the help.

Here are three common solutions to this problem:

Using the item type

You can ask the canvas for the type of the object:

item_type = cv.type(item)
if item_type == "rectangle":
    # this item is a rectangle
else:
    # this item is NOT a rectangle

Using tags

Another solution is to give each item one or more tags. You can then query the tags for the current item.

First, include one or more tags on the items you want to be clickable:

cv. create_rectangle(80, 80, 100, 100, tags=("clickable",))

Next, check for the tags on the item you're curious about, and check to see if your tag is in the set of tags for that item:

tags = cv.itemcget(item, "tags")
if "clickable" in tags:
    # this item has the "clickable" tag
else:
    # this item does NOT have the "clickable" tag

Create bindings on tags

A third option is to attach the bindings to the tags rather than the canvas as a whole. When you do this, your function will only be called when you click on item with the given tag, eliminating the need to do any sort of runtime check:

cv.tag_bind("clickable", "<1>", onclick)

The solution of BPL is fine. Here is another solution that doesn't require any global variable. There's a system of tags in Tkinter which could be compared with "class" from HTML/CSS. It enables us to associate a list of tags to a graphic object. See item specifiers .

Here is how it would be:

import tkinter as tk


def onclick(event):
    global rectangles
    item = cv.find_closest(event.x, event.y)
    if 'rect' in cv.gettags(item):
        current_color = cv.itemcget(item, 'fill')

        if current_color == 'black':
            cv.itemconfig(item, fill='white')
        else:
            cv.itemconfig(item, fill='black')

rectangles = []

root = tk.Tk()
cv = tk.Canvas(root, height=800, width=800)
cv.pack()
cv.bind('<Button-1>', onclick)

id_a = cv.create_line(50, 50, 60, 60, width=2)
id_b = cv.create_rectangle(80, 80, 100, 100, tags=('rect'))

root.mainloop()

I think it's a bit more object oriented in some way. ;)

What about this solution:

import tkinter as tk


def onclick(event):
    global rectangles
    item = cv.find_closest(event.x, event.y)
    if item[0] in rectangles:
        current_color = cv.itemcget(item, 'fill')

        if current_color == 'black':
            cv.itemconfig(item, fill='white')
        else:
            cv.itemconfig(item, fill='black')

rectangles = []

root = tk.Tk()
cv = tk.Canvas(root, height=800, width=800)
cv.pack()
cv.bind('<Button-1>', onclick)

id_a = cv.create_line(50, 50, 60, 60, width=2)
id_b = cv.create_rectangle(80, 80, 100, 100)
rectangles.append(id_b)

root.mainloop()

The main idea of this is you add the items id you want to change color to a global array (global variables are discouraged though). Then when you click with the mouse, the itemcget function will give you the id from the closest item, so you need to check whether this id is inside of the array of your rectangle items.

Based on this accepted answer you need to tag_bind your rectangles. Also, you should be assigning IDs to each object you create. If you need to do this repeatedly, you can wrap the process in a loop or functions, but the base theory won't change. GL with tkinter!

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