Tkinter callback arguments
Alf P. Steinbach
alfps at start.no
Sun Nov 1 18:19:51 EST 2009
* MRAB:
> Lord Eldritch wrote:
>> Hi
>>
>> Maybe this is maybe something it has been answered somewhere but I
>> haven't been able to make it work. I wanna pass one variable to a
>> callback function and I've read the proper way is:
>>
>> Button(......, command=lambda: function(x))
>>
>> So with
>>
>> def function(a): print a
>>
>> I get the value of x. Ok. My problem now is that I generate the
>> widgets in a loop and I use the variable to 'label' the widget:
>>
>> for x in range(0,3): Button(......, command=lambda: function(x))
>>
>> so pressing each button should give me 0,1,2.
>>
>> But with the lambda, I always get the last index, because it gets
>> actualized at each loop cycle. Is there any way to get that?
>>
> A lambda expression is just an unnamed function. At the point the
> function is /called/ 'x' is bound to 3, so that's why 'function' is
> always called with 3.
>
> A function's default arguments are evaluated when the function is
> /defined/, so you can save the current value of 'x' creating the
> function (the lambda expression, in this case) with a default argument:
>
> for x in range(0,3):
> Button(......, command=lambda arg=x: function(arg))
>
> The following will also work, although you might find the "x=x" a bit
> surprising/confusing if you're not used to how Python works:
>
> for x in range(0,3):
> Button(......, command=lambda x=x: function(x))
An alternative reusable alternative is to create a button-with-id class.
This is my very first Python class so I'm guessing that there are all sorts of
issues, in particular naming conventions.
And the idea of creating a reusable solution for such a small issue may be
un-pythonic?
But just as an example, in Python 3.x,
<code>
import tkinter
# I guess for Python 2.x do "import Tkinter as tkinter" but haven't tested.
class IdButton( tkinter.Button ):
def __init__( self, owner_widget, id = None, command = None, **args ):
tkinter.Button.__init__(
self, owner_widget, args, command = self.__on_tk_command
)
self.__id = id
self.__specified_command = command
def __on_tk_command( self ):
if self.__specified_command != None:
self.__specified_command( self )
else:
self.on_clicked()
def on_clicked( self ):
pass
def id( self ):
return self.__id
def id_string( self ):
return str( self.id() );
def on_button_click( aButton ):
print( "Button " + aButton.id_string() + " clicked!" )
window = tkinter.Tk()
n_buttons = 3
for x in range( 1, n_buttons + 1 ):
IdButton(
window, id = x, text = "Button " + str( x ), command = on_button_click
).pack()
window.mainloop()
</code>
Cheers,
- Alf
PS: Now I see that I've used camelCase once. Oh well...
More information about the Python-list
mailing list