Reputation: 12544
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))
Upvotes: 5
Views: 10187
Reputation: 4623
A common way is to use a common function, but not exactly the same callback (through lambda
closure, as pointed by Justin's answer).
There are two imperfect alternatives relying on internals of toolkits:
_name
in widgetwidgets
dictionary you can iterate to find back your widget names (name = [k for k, v in root.widgets.iteritems() if v == event.widget][0]
). For better code separation, you can retrieve root from widget with event.widget._nametowidget('.')
It is worth noting these solutions would not work with button command
that does not provide an event to their callback. And binding action to buttons is preferentially done through
command
since it implements the usual behavior of a button (action on release, you can abort by leaving the button...).
Upvotes: 0
Reputation: 646
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)
Upvotes: 5
Reputation: 123
i can't remember how to get the name but you can always list everything by using a
print dir(event)
or
print dir(event.widget)
Upvotes: 0