How to get the tag of a shape when clicked
Asked Answered
E

3

6

Lets say I have created a canvas in a Tkinter app.

In the canvas, I have drawn several rectangles.

What I want in essence, is to know in which rectangle the user has clicked, that is my main concern, but this is what I have tried and what I think might be the solution.

This is my layout:

for x in range(1,6):
        for y in range(1,7):     
            tableNumber = y + 6*(x-1)
            w.create_rectangle((table.xSeparation + table.width) * y - table.width,
                               (table.ySeparation + table.height) * x -  table.height,
                               (table.xSeparation + table.width)*y,
                               (table.ySeparation + table.height) * x,

                               fill="brown", tags="table" + str(tableNumber))

            w.tag_bind("table" + str(tableNumber), '<Button-1>', do)

So this just creates rectangles in a rows and columns layout.

Each rectangle has a specific tag in the form of table1,table2 to table30.

When I click on the rectangle, the do function executes.

Let's say do was something like:

def do(event):
    print "click"

Not I get a "click" in the console for every click in the rectangle.

I would like to be able to get the tag of the clicked rectangle to be able to do something like this:

def do(event):
    print str(theTagOfTheClickedRectangle)

So I checked on what events I can call which are:

  • widget
  • x, y
  • x_root, y_root
  • char
  • keysym
  • keycode
  • num
  • width, height
  • type

None of the seem to pin point what rectangle I have clicked.

I thought about adding more parameters to the do function here:

w.tag_bind("table" + str(tableNumber), '<Button-1>', do(event, tag))

But that doesn't seem to work fine, but maybe I'm not doing the do right:

do(event, tag): #this doesn't work at all!!
    print event.x
    print tag

I hope the question is clear, if I got the tag in the same manner as I can get the 'x' and 'y' of the event, that would be very comfortable.

The only actual solution I can now think of, is working out the math of 'event.x' and 'event.y' and calculate, based on the coordinates of the click, on which rectangle the user has clicked, this seems overcomplicated for what I'm trying to do, though I've done it before and it obviously works.

I hope the question is clear, other wise please ask for any clarification.

Note that I'm not tied to any of this code, rather, I'm looking for a solution for this problem, the most efficient will work even if means not working with tags, or another type of widget or whatever is the easier.

Equiponderance answered 29/9, 2011 at 19:2 Comment(0)
S
10

You can use something like this to reference the clicked-on object:

event.widget.find_withtag("current")

The tag "current" is special and represents the top-most object under the mouse.

Spiller answered 29/9, 2011 at 23:0 Comment(3)
Thanks @Bryan! This is a very simple and correct solution! I checked your profile and you seem to know quite a lot about tkinter. May I ask which documentation you use regularly? I love the simplicity of tkinter but I find the documentation on it very incomplete. For example, what you suggested as an answer, was never mentioned in any of the documentations I've used. Thank you very much!Equiponderance
@Trufa: the definitive site for information related to the tk toolkit are the official man pages at tcl.tk/man/tcl8.5/TkCmd/contents.htm. Those are specific to the Tcl language, but it's a very easy mental transformation to adapt the information to tkinter. For tkinter-specific information, go to effbot.org/tkinterbook (and the "current" tag is mentioned on that site FWIW). One final site with some useful information is tkdocs.com which has coverage of python, perl, tcl and ruby.Spiller
thanks for it! I did not not know about the Tcl one, I will check it out, the other ones are the ones that usually come up in the google searches. I can't believe I've missed the "current" in listbox, probably because I did not know what I was looking for. Anyway thanks again!!Equiponderance
S
4
w.tag_bind("table" + str(tableNumber), '<Button-1>', do(event, tag))

should be changed to

tag = "table" + str(tableNumber)
callback = lambda event, tag=tag: do(event, tag)
w.tag_bind(tag, '<Button-1>', callback)

The lambda creates a function with the tag name as the default value of the second argument. Tkinter calls that function with just one argument, the event, and the second argument uses the default value.

Seasick answered 29/9, 2011 at 19:12 Comment(3)
That is freaking awesome!! It works like a charm and it is exactly what I was looking for! Thank you so much! The only thing else I would ask if you can explain me a little bit what you did there in this line callback = lambda event, tag=tag: do(event, tag), I'm a little bit confused by that lambda function. Thank you again very much!Equiponderance
It's equivalent to def callback(event, t=tag): return do(event, t).Seasick
I changed the accepted answer to bryan's mainly because of simplicity and because it is the one I'll be using, even so, once again, thanks for your answer, and it does work as expected.Equiponderance
P
0

The accepted answer is correct and resolve the OP's problem. My point here is to answer the question "How to get tag of shape" because I googled it, found this post and no one answer that part of question. After we get tag of item we can use.

canvas.itemconfig(id)

From tkinter-docs shown that canvas.itemconfig is similar to widget.config.

After I tried it, it return dictionary with attributes as keys and value of 5-tuple (should be (option name, option database key, option database class, default value, current value)). If you only interest in tag you cam simply call

canvas.itemconfig(id,attr)

For example, if we want tags of item,

canvas.itemconfig(id,'tags')

Which return that 5-tuple right away and equivalent to

canvas.itemconfig(id)[attr]

Note: the value of canvas.itemconfig(id,'tags') has type of String if it has multiple tags, the tags name will separate with space.

Ph answered 2/10, 2023 at 9:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.