By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,640 Members | 2,167 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,640 IT Pros & Developers. It's quick & easy.

Arguments for button command via Tkinter?

P: n/a
Recently, I have been needing to do this alot and I can never find a
way around it, the main reason I want to do this is because for example
in the application I am making right now, it creates a grid of buttons
in a loop and they all have the same purpose so they need to call the
same method, within this method they need to change the background
color of the button clicked, I've tried stuff like...

button['command'] = lambda: self.fill(button)

def fill(self, wid):
button['bg'] = '#ff0000'

But that's no good, everytime it I do click a button the lambda method
I have setup keeps getting recreated so all of the button commands
point to the same lambda method, I have also tried appending them to an
array, and calling them from that.

Anyone have any clue as of what to do? Thanks.

Oct 31 '05 #1
Share this Question
Share on Google+
8 Replies


P: n/a
And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.

Oct 31 '05 #2

P: n/a
da****@gmail.com wrote:
And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.

I'm hoping you have simply mis-stated your correct understanding of the
problem, and that what you really propose to do is create a button class
of which each button in your interface is an instance.

Let's suppose you want each button to toggle between two colors, but
that the colors for each button are different. The thing to do is create
a button class that subclasses the button class of your GUI package
(whatever that may be), storing the required color values as instance
attributes.

In wxPython, for example, I would write something like (warning: untested):

import wx

class myButton(wx.Button):

def __init__(self, color1, color2, *args, **kw):
wx.Button.__init__(self, *args, **kw)
self.c1 = color1
self.c2 = color2
self.state = False

def onClick(self, event):
if self.state:
self.SetBackgroundColor(self.c1)
else:
self.SetBaclgroundColor(self.c2)
self.state = not self.state

Then when you create a button with, say,

but1 = myButton(wx.RED, wx.BLUE, ...)

you can associate a click on that button with a bound instance method,
which you'd do in wxPython like:

wx.EVT_BUTTON(parent, but1, but2.onClick)

but similar considerations apply to Tkinter, and you appear to have the
wit to be able to extend the argument to that toolkit.

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Oct 31 '05 #3

P: n/a
Il Mon, 31 Oct 2005 06:23:12 -0800, da****@gmail.com ha scritto:
And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.


This is how I do it: Supposing I have three buttons b1, b2 and b3, and I
want for each button to call the same callback with, as argument, the
button itself:
def common_callback(button):
# callback code here
class CallIt(objetc):
def __init__(function, *args ):
self.function, self.args = function, args
def __call__(self, *ignore):
self.function(button, *self.args)

b1['command']= CallIt(common_callback, b1)
b2['command']= CallIt(common_callback, b2)
b3['command']= CallIt(common_callback, b3)

This way you need only one class (a sort of custom callable) and
its instances gets called by Tkinter and in turn calls your
callback with the proper arguments.

Ciao
-----
FB

Oct 31 '05 #4

P: n/a
"da****@gmail.com" <da****@gmail.com> writes:
Recently, I have been needing to do this alot and I can never find a
way around it, the main reason I want to do this is because for example
in the application I am making right now, it creates a grid of buttons
in a loop and they all have the same purpose so they need to call the
same method, within this method they need to change the background
color of the button clicked, I've tried stuff like...

button['command'] = lambda: self.fill(button)

def fill(self, wid):
button['bg'] = '#ff0000'

But that's no good, everytime it I do click a button the lambda method
I have setup keeps getting recreated so all of the button commands
point to the same lambda method, I have also tried appending them to an
array, and calling them from that.

Anyone have any clue as of what to do? Thanks.


People have pointed out how to do this with a class, but you can do it
with lambdas as well. I suspect the problem you're having is that
python is binding values at call time instead of at definition time,
but it's hard to tell without more of your source code.

If I'm right, one solution is to make the values you want bound at
definition time optional arguments to the lambda, bound to the correct
value then. So you'd do:

button['command'] = lambda b = button: self.fill(b)

The version you used will look up the name button when the lambda is
invoked. My version will look it up when the lambda is defined.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Oct 31 '05 #5

P: n/a
Yah, thats how i learned how to do it, thanks.

Oct 31 '05 #6

P: n/a
Francesco Bochicchio wrote:
Il Mon, 31 Oct 2005 06:23:12 -0800, da****@gmail.com ha scritto:

And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.

This is how I do it: Supposing I have three buttons b1, b2 and b3, and I
want for each button to call the same callback with, as argument, the
button itself:
def common_callback(button):
# callback code here
class CallIt(objetc):
def __init__(function, *args ):
self.function, self.args = function, args
def __call__(self, *ignore):
self.function(button, *self.args)

b1['command']= CallIt(common_callback, b1)
b2['command']= CallIt(common_callback, b2)
b3['command']= CallIt(common_callback, b3)

This way you need only one class (a sort of custom callable) and
its instances gets called by Tkinter and in turn calls your
callback with the proper arguments.

I don't see why this is preferable to having the callback as a bound
method of the button instances. What's the advantage here? It looks
opaque and clunky to me ...

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Oct 31 '05 #7

P: n/a


Steve Holden wrote:
Francesco Bochicchio wrote:
Il Mon, 31 Oct 2005 06:23:12 -0800, da****@gmail.com ha scritto:

And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.


This is how I do it: Supposing I have three buttons b1, b2 and b3, and I
want for each button to call the same callback with, as argument, the
button itself:
def common_callback(button):
# callback code here
class CallIt(objetc):
def __init__(function, *args ):
self.function, self.args = function, args
def __call__(self, *ignore):
self.function(button, *self.args)

b1['command']= CallIt(common_callback, b1)
b2['command']= CallIt(common_callback, b2)
b3['command']= CallIt(common_callback, b3)

This way you need only one class (a sort of custom callable) and
its instances gets called by Tkinter and in turn calls your
callback with the proper arguments.

I don't see why this is preferable to having the callback as a bound
method of the button instances. What's the advantage here? It looks
opaque and clunky to me ...


I'm not sure on the advantage either. I just recently started handling
my buttons with button id's.

def __init__(self, *args, **kwds):
...

b1 = Tk.Button( frame, text=button,
command=self.command(button) )
...

The button label is used as the id above, but a number or code could
also be used.

def command(self, id):
""" Assign a command to an item.
The id is the value to be returned.
"""
def do_command():
self.exit(event=id)
return do_command

In this case it's a dialog button so it calls the exit method which sets
self.return to the button id before exiting.

Cheers,
Ron
Oct 31 '05 #8

P: n/a
Il Mon, 31 Oct 2005 19:23:18 +0000, Steve Holden ha scritto:
Francesco Bochicchio wrote:
Il Mon, 31 Oct 2005 06:23:12 -0800, da****@gmail.com ha scritto:

And yet the stupidity continues, right after I post this I finnally
find an answer in a google search, It appears the way I seen it is to
create a class for each button and have it call the method within that.
If anyone else has any other ideas please tell.

This is how I do it: Supposing I have three buttons b1, b2 and b3, and I
want for each button to call the same callback with, as argument, the
button itself:
def common_callback(button):
# callback code here
class CallIt(objetc):
def __init__(function, *args ):
self.function, self.args = function, args
def __call__(self, *ignore):
self.function(button, *self.args)

b1['command']= CallIt(common_callback, b1)
b2['command']= CallIt(common_callback, b2)
b3['command']= CallIt(common_callback, b3)

This way you need only one class (a sort of custom callable) and
its instances gets called by Tkinter and in turn calls your
callback with the proper arguments.

I don't see why this is preferable to having the callback as a bound
method of the button instances. What's the advantage here? It looks
opaque and clunky to me ...

regards
Steve


I'm not saying that my method is better or even 'preferable'. Just
different.

The reason I came up this approach (years ago) is because I was used to
other toolkits that always pass the widget as argument of the callback. So
this was a fast solution to 'normalize' Tkinter with respect to what I
perceived (and still do) as a limitation. As a bonus, you can also pass to
the callback as many other arguments as you want.
Another reason is that my basic approach to coding GUI is to use a class
for each window. To put the code to handle button callbacks in a separate
class feels to me a bit too much dispersive and potentially cumbersome if
callback code has to interact with other window elements (although in
Python nothing is impossible).

Ciao
-----
FB
why I sometime

Nov 1 '05 #9

This discussion thread is closed

Replies have been disabled for this discussion.