[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Tkinter Button Command Taking Only Newest Reference In Callback Parameter

"Thanks" * INFINITY

hum weirdly it worked ... not yet understood why!

Here is a post on it:

Thanks for all!

On Mon, Jul 8, 2019 at 5:40 PM Calvin Spealman <cspealma at redhat.com> wrote:

> This is a really common scoping issue to get tripped up on. I'll try to
> help you understand and work around it.
> for i, icon in enumerate(self.icons):
>     print('// entering', icon.program)
>     print('icons ... ... ... ...')
>     pretty(icon.info, icon.program)
>     x = Button(root, text=icon.info['name'] +'\n' + icon.info['version'],
>        image=icon.photo,
>         compound=LEFT,
>         command=lambda: self.button_exec(icon))
> On the first line you enter a for loop where each iteration assigns a
> different pair of values to the names "i" and "icon".
> On the last line you create a lambda, an anonymous function, which you
> pass as the command callback for the button. Of course, no code inside the
> lambda is actually executed at this point. It is only after the entire for
> loop has completed, later when the user sees the buttons and clicks on one
> of them, that one of the lambda callbacks for the appropriate button will
> be executed.
> Now, when this happens, what is the value of "icon"? This involves a
> closure, which is how we describe the effect of the lambda (or any
> function) being created inside the scope of another function. The scope
> here which contains the "icon" variable is kept after the for loop and its
> container function is complete, because the lambda you create holds on to
> it in order to resolve variable names from. So all the lambda functions
> read the "icon" variable from this same scope, and at the time the loop
> completes that variable is assigned to the LAST value from the loop. That's
> why all the buttons get the same icon.
> There is a simple work around, however, called "rebinding". It is a trick
> you can do with default values for lambda or any function. Simply pass the
> icon as a default value for a parameter to the lambda, and the value at
> that time for one iteration of the loop will be bound to the callback you
> create.
> Just change this
>     lambda: self.button_exec(icon)
> to this
>     lambda icon=icon: self.button_exec(icon)
> I hope both the explanation and the example help!
> On Mon, Jul 8, 2019 at 1:59 AM Abdur-Rahmaan Janhangeer <
> arj.python at gmail.com> wrote:
>> Greetings,
>> I'm doing this project:
>> https://github.com/Abdur-rahmaanJ/cmdlaunch/blob/master/cmdlaunch/cmdlaunch.py
>> Everything works fine, except for one thing.
>> On line 60, the button is taking an object in it's command parameter
>> If you run the file, you'll see that all the different names are written
>> on
>> the gui just the callback is takest the newest icon object for all three
>> buttons
>> I know just a reference issue but it's been **bugging** me.
>> Thanks All
>> --
>> Abdur-Rahmaan Janhangeer
>> Mauritius
>> --
>> https://mail.python.org/mailman/listinfo/python-list
> --
> cspealma at redhat.com  M: +1.336.210.5107
> [image: https://red.ht/sig] <https://red.ht/sig>
> TRIED. TESTED. TRUSTED. <https://redhat.com/trusted>

Abdur-Rahmaan Janhangeer
http://www.pythonmembers.club | https://github.com/Abdur-rahmaanJ