Home > database >  How do I detect tags of clicked objects in tkinter Canvas?
How do I detect tags of clicked objects in tkinter Canvas?

Time:12-21

I would like to ask how can be detected the tags in Python tkinter.Canvas(). I have set up a sample program with some basic canvas objects and all of them have been assigned to some tag. What I want is to get that tag value if I click on the object but have no idea how to do it.

For example when I have this code:

import tkinter
canvas = tkinter.Canvas(width=800, height=400)
canvas.pack()

canvas.create_line(20, 100, 150, 350, tags="lines")
canvas.create_line(50, 20, 300, 20, tags="lines")
canvas.create_oval(200, 250, 300, 350, fill="yellow", tags="ovals")
canvas.create_oval(400, 250, 500, 350, fill="blue", tags="ovals")

for j in range(4):
    for i in range(10):
        canvas.create_rectangle(i * 70   10, j * 60   10, i * 70   60, j * 60   50, fill="lightblue", tags=f"square_{j}_{i}")

def click(coords):
    print(coords.x, coords.y)

canvas.bind("<Button-1>", click)

I would like to get these values such as lines, ovals, square_5_4, square_7_1 etc. Does anyone have an idea how to do it please? O:)

CodePudding user response:

You can just loop over all the item IDs on the canvas, get their tags and bind a function to clicking on that object (if you need that all of the objects (created before the for loop) on the canvas to do this):

import tkinter as tk


root = tk.Tk()

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

canvas.create_line(20, 100, 150, 350, tags="lines")
canvas.create_line(50, 20, 300, 20, tags="lines")
canvas.create_oval(200, 250, 300, 350, fill="yellow", tags="ovals")
canvas.create_oval(400, 250, 500, 350, fill="blue", tags="ovals")

for j in range(4):
    for i in range(10):
        canvas.create_rectangle(i * 70   10, j * 60   10, i * 70   60, j * 60   50, fill="lightblue", tags=f"square_{j}_{i}")


# loop over all items on the canvas, get their first tag (for simplicity) and bind that tag
# to clicking on the object and make that call the function that will print the tag name
# use a default value so that the correct tag is printed
for item_id in canvas.find_all():
    tag = canvas.gettags(item_id)[0]
    canvas.tag_bind(tag, '<Button-1>', lambda _, t=tag: print(t))

root.mainloop()

CodePudding user response:

As @Matiiss suggest in a comment, you can use a Canvas object's tag_bind() method to bind callback functions to specific events such as button 1 clicks to them. The code below — based on yours — illustrates how something like that being done.

It creates anonymous lambda functions that call a generic pick() click event handling function and passes it the original event object plus the tag name currently associated with the object.

import tkinter

def pick(event, tag):
    print(f'tag: {tag!r}, coords: {event.x}, {event.y}')

canvas = tkinter.Canvas(width=800, height=400)
canvas.pack()

canvas.create_line(20, 100, 150, 350, tags='lines')
canvas.create_line(50, 20, 300, 20, tags='lines')
canvas.tag_bind('lines', '<Button-1>', lambda event: pick(event, 'lines'))

canvas.create_oval(200, 250, 300, 350, fill='yellow', tags='ovals')
canvas.create_oval(400, 250, 500, 350, fill='blue', tags='ovals')
canvas.tag_bind('ovals', '<Button-1>', lambda event: pick(event, 'ovals'))

for j in range(4):
    for i in range(10):
        tag = f'square_{j}_{i}'
        canvas.create_rectangle(i*70   10, j*60   10, i*70   60, j*60   50,
                                fill='lightblue', tags=tag)
        canvas.tag_bind(tag, '<Button-1>', lambda event, tag=tag: pick(event, tag))

tkinter.mainloop()

CodePudding user response:

You don't need to loop through all your items in order to get the tags of your canvas items. Any item on canvas that is clicked is assigned with "current" tag.

You just need to use canvas.find_withtag along with canvas.gettags or pass "current" directly to gettags and it will return you all the tag associated with the canvas item.

import tkinter


canvas = tkinter.Canvas(width=800, height=400)
canvas.pack()

canvas.create_line(20, 100, 150, 350, tags="lines")
canvas.create_oval(400, 250, 500, 350, fill="blue", tags="ovals")

for j in range(4):
    for i in range(10):
        canvas.create_rectangle(i * 70   10, j * 60   10, i * 70   60, j * 60   50, fill="lightblue", tags=f"square_{j}_{i}")

def click(event):

    #currently_clicked = canvas.find_withtag("current")
    #if currently_clicked:
    print(canvas.gettags("current")) # the first index will contain your desired output
   
canvas.bind("<Button-1>", click)
canvas.mainloop()
  • Related