* 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...
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to