Getting the sender widget's name in function (Tkinter)

H

Harlin Seritt

I have the following script. Two widgets call the same function. How
can I tell inside of the called function which button called it?:

def say_hello():
print 'hello!'
print widget['text']

root = Tk()
button1 = Button(root, text='Button 1', command=say_hello)
button1.pack()
button2 = Button(root, text='Button 2', command=say_hello)
button2.pack()
root.mainloop()

Thanks,

Harlin Seritt
 
T

tiissa

Harlin said:
I have the following script. Two widgets call the same function. How
can I tell inside of the called function which button called it?:

As far as I know you can't (but I can be proven wrong).
You may try to define a class to solve this (not tested):

####
class say_hello:
def __init__(self, text):
self.text=text
def __call__(self)
print 'Hello!'
print self.text

root = Tk()
button1 = Button(root, text='Button 1', command=say_hello('Button 1'))
button1.pack()
button2 = Button(root, text='Button 2', command=say_hello('Button 2'))
button2.pack()
root.mainloop()
####
 
I

infidel

from Tkinter import Tk, Button

def say_hello(event):
print 'hello!'
print event.widget['text']

root = Tk()
button1 = Button(root, text='Button 1')
button1.bind('<Button-1>', say_hello)
button1.pack()
button2 = Button(root, text='Button 2')
button2.bind('<Button-1>', say_hello)
button2.pack()
root.mainloop()
 
E

Eric Brunel

from Tkinter import Tk, Button

def say_hello(event):
print 'hello!'
print event.widget['text']

root = Tk()
button1 = Button(root, text='Button 1')
button1.bind('<Button-1>', say_hello)
button1.pack()
button2 = Button(root, text='Button 2')
button2.bind('<Button-1>', say_hello)
button2.pack()
root.mainloop()

Unfortunately, making a binding to <Button-1> on Button widgets does not have the same behavior as setting their 'command' option. The binding will fire when the button is *pressed*; the command will be called when the button is *released*. So, binding to <ButtonRelease-1> instead of <Button-1> make things a little better, but still does not have the same effect, since ButtonPress and ButtonRelease events are balanced: the widget getting the ButtonRelease event is always the same as the one getting the ButtonPress event. So if the mouse button is pressed inside the Button, then the mouse pointer goes out of it, and then the mouse button is released, the Button will still get the ButtonRelease event and fire the binding. This is not the normal behavior for a button and this is not the behavior you get via the 'command' option (just try it...).

So having a different function for each button or using tiissa's solution is definitely better.

HTH
 
C

Cameron Laird

from Tkinter import Tk, Button

def say_hello(event):
print 'hello!'
print event.widget['text']

root = Tk()
button1 = Button(root, text='Button 1')
button1.bind('<Button-1>', say_hello)
button1.pack()
button2 = Button(root, text='Button 2')
button2.bind('<Button-1>', say_hello)
button2.pack()
root.mainloop()

Unfortunately, making a binding to <Button-1> on Button widgets does not
have the same behavior as setting their 'command' option. The binding
will fire when the button is *pressed*; the command will be called when
the button is *released*. So, binding to <ButtonRelease-1> instead of
<Button-1> make things a little better, but still does not have the same
effect, since ButtonPress and ButtonRelease events are balanced: the
widget getting the ButtonRelease event is always the same as the one
getting the ButtonPress event. So if the mouse button is pressed inside
the Button, then the mouse pointer goes out of it, and then the mouse
button is released, the Button will still get the ButtonRelease event
and fire the binding. This is not the normal behavior for a button and
this is not the behavior you get via the 'command' option (just try
it...).

So having a different function for each button or using tiissa's
solution is definitely better.
.
.
.
Without unraveling my own confusion about who has said what to whom, does
everyone realize that Tkinter bind()ings inherently can access the widgets
which generate their events? Please refer to Table 7-2 in <URL:
http://www.pythonware.com/library/tkinter/introduction/events-and-bindings.htm >
(and thank Fredrik, once again, for his marvelous work in putting this
material online).
 
T

tiissa

Cameron said:
Without unraveling my own confusion about who has said what to whom, does
everyone realize that Tkinter bind()ings inherently can access the widgets
which generate their events?

I don't know about everyone, but I can assume that's definitively the
case of infidel (who precisely based the solution you quoted on this)
and Eric Brunel.

But that may not be the topic at hand. Indeed, the main point is that,
according to Eric, bind() and command don't behave in the exact same way.

And the OP asked about having a reference on the widget using the
command callback (that, contrary to event-binded callbacks, don't get
passed any argument).


So far, the OP is proposed the choice to either use the event/bind
mecanism or use different callbacks for his different buttons (either
with the method I proposed or not).
 
I

infidel

Here's a slight variation of tiissa's solution that gives the callable
a reference to the actual widget instead of just it's name:

from Tkinter import Tk, Button

class say_hello:
def __init__(self, widget):
self.widget = widget
def __call__(self):
print 'Hello,', self.widget['text']

def run():
root = Tk()
b1 = Button(root, text='Button 1')
b1.configure(command=say_hello(b1))
b1.pack()
b2 = Button(root, text='Button 2')
b2.configure(command=say_hello(b2))
b2.pack()
root.mainloop()

run()
 
C

Cameron Laird

.
.
.
So far, the OP is proposed the choice to either use the event/bind
mecanism or use different callbacks for his different buttons (either
with the method I proposed or not).

Thanks, Tissa. Is there general understanding that "use different
callbacks ..." can be implemented as "parametrize the same callback
with a widget-specific value"?
 
T

tiissa

Cameron said:
Is there general understanding that "use different
callbacks ..." can be implemented as "parametrize the same callback
with a widget-specific value"?

Tough questions thou ask! Again I can't answer about general
understanding. ;)

However, having myself proposed such a solution in this very thread (and
hinted about it in the above sentence), I do hope most people (at least
those interested in this issue) will be aware of this kind of trick
(without any restriction on the actual implementation). :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top