Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the name of a widget in Tkinter?

Tags:

python

tkinter

I have a common callback for a range of buttons. Once the callback is called, I want to identify the caller by its previously assigned name. But I could not figure out in the Tkinter documentation how I could do this. Any ideas?

My program is approx.

def callback(event):
    event.widget['text'] # 'name' does not work so 
                         # I have to use the label of the button now, but it is a dirty solution.

root.bind("<Button-1>", cb_button)

I need the names because I load the GUI from JSON using pytkgen.

UPDATE:
Justin's solution looks nice but why does the following code print always 9 regardless of which button is clicked?

def cb_button(i):
    print i

buttons = [('btn'+str(i), i) for i in range(10)]
for (b, i) in buttons:
    root.button(b, lambda: cb_button(i))
like image 361
Barney Szabolcs Avatar asked Oct 03 '22 22:10

Barney Szabolcs


1 Answers

The winfo_name() method of the widget returns the name of the widget. It is usually an 8 digit number expressed as a string, ie: '40123211' If you have an event object, evt, then use: evt.widget.winfo_name()

In the body of the question you're asking something different: "How can I tell which button invoked a common callback function"

Use a lambda function to pass a unique value to a common callback:

Based on the update to your question, I think I understand the problem. You have a JSON file that is used to create a Tkinter interface via pytkgen. In this JSON file are definitions for several buttons, each of which has been given a unique name. When assigning commands to these buttons, they're all given the same callback, but the callback needs to know which button initiated the call, and you want to do that via the name. Is that correct?

If so, I'm guessing that you're currently creating the callback assignments like this (very generic example, assuming that root is the interface root returned by calling tkgen.gengui.TkJson with the path to your JSON file):

root.button("name1", callback)
root.button("name2", callback)
...

This isn't giving you the name that you want, though. One way to pass the name is to create a lambda that passes the button name to the callback function. The callback assignments would then look something like this:

root.button("name1", lambda:callback("name1"))
root.button("name2", lambda:callback("name2"))
...

Then your callback definition could look like this:

def callback(name):
    if name == "name1":
        # Do something in here
    elif name == "name2":
        # Do something else in here
    ...

UPDATE: If you're creating the buttons inside a loop, the lambda definition will need to be modified to store the desired loop variable(s) as keyword defaults. Otherwise the final value of the loop variable(s) will be applied to all buttons.

def cb_button(i):
    print i

buttons = [('btn'+str(i), i) for i in range(10)]
for (b, i) in buttons:
    root.button(b, lambda x=i: cb_button(x))

Add an attribute to the widget object

Another solution is to attributes to the button widget object can you inspect in the callback function. (This example using Tkinter)

but1 = Tkinter.Button(root, text="Sync", width=10)
but1.bind("<Button-1>", doButton)
but1.myId = "this"

but2 = Tkinter.Button(root, text="Sync", width=10)
but2.bind("<Button-1>", doButton)
but2.myId = "that"
but2.otherStuff = "anything"

def doButton(evt):
  if evt.widget.myId == "this":
    print("This")
  else:
    print("That "+evt.widget.otherStuff)
like image 84
Justin S Barrett Avatar answered Oct 07 '22 19:10

Justin S Barrett