Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question about Tkinter

0 views
Skip to first unread message

Benjamin Wu

unread,
Aug 10, 2001, 3:52:24 AM8/10/01
to
When I use Tkinter, I met a question as following:
I create three buttons and thire callback function
is the same, how to know which button active callback
function when one of they pressed?

Anyone can help me?


Alex Martelli

unread,
Aug 10, 2001, 4:46:46 AM8/10/01
to
"Benjamin Wu" <ch...@gnuchina.org> wrote in message
news:9l03s7$6q9f0$1...@ID-54723.news.dfncis.de...

Use different callables for the callbacks -- if it must be
the same function, curry the function with different
preset arguments -- see for example
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549
about how to do currying in Python.


Alex

Cameron Laird

unread,
Aug 10, 2001, 9:44:58 AM8/10/01
to
In article <9l07a...@enews4.newsguy.com>,
.
.
.
Good reference. Note that, in practice, it's entirely
natural to define a tiny factory method which ensures
the correspondence between, for example, button and data.
In the recipe above, factor out the data 'A'.

Confession time: in principle, GUI coders also can code
their own bindings and use %W substitution to compute
the widget which received the event. Does that work in
Tkinter? I've never used it, I can't find it documented,
and I'm already late for a meeting so can't experiment
myself just now. Fredrik, do the %-s work right?

Even if they do, I'd still curry.
--

Cameron Laird <cla...@NeoSoft.com>
Business: http://www.Phaseit.net
Personal: http://starbase.neosoft.com/~claird/home.html

Grant Edwards

unread,
Aug 10, 2001, 12:46:15 PM8/10/01
to
In article <9l07a...@enews4.newsguy.com>, Alex Martelli wrote:
> "Benjamin Wu" <ch...@gnuchina.org> wrote in message
>> When I use Tkinter, I met a question as following:
>> I create three buttons and thire callback function
>> is the same, how to know which button active callback
>> function when one of they pressed?
>>
>> Anyone can help me?
>
> Use different callables for the callbacks -- if it must be
> the same function, curry the function with different
> preset arguments -- see for example
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549
> about how to do currying in Python.

A slightly different way is to use lamgda. It's best for
simple cases where you just want to pass a few arguments that
are calcualted at run-time. Here's an excerpt from an 8-queens
demo program that binds a callback to each button (space on a
chessboard):

1 for y in range(size):
2 f = Frame(self.__frame)
3 for x in range(size):
4 if (x+y) % 2:
5 bg = "#bbb"
6 else:
7 bg = "#eee"
8 b = Button(f,
9 bitmap='@%s' % bitmapfile,
10 highlightthickness=0,
11 relief=FLAT,
12 background=bg,
13 activebackground="#fff",
14 foreground=bg)
15 b.bind("<Button-1>",lambda event,xx=x,yy=y: board.toggleSpace(xx,yy))
16 b.bind("<Any-Enter>", self.__noop)
17 b.bind("<Any-Leave>", self.__noop)
18 b.bind("ButtonRelease-1>", self.__noop)
19 b.pack(side=LEFT)
20 self.__button[y][x] = b
21 f.pack(side=BOTTOM)


Line 15 is where the interesting bit happens.

When a <Button-1> is pressed over any of the 64 spaces on the
chessboard, the anonymous function defined by the lamba is
called. That function receives one parameter called "event"
from Tk, and has two default parameters "xx" and "yy" whose
default values are set when the binding is done. Since Tk only
passes the single "event" parameter, "xx" and "yy" will have
the values specified by "x" and "y" when the lambda operation
took place.

The anonymous callback function will then call
board.toggleSpace() passing it the index values. The "event"
parameter that is passed to the anonymous callback function is
ignored.

Curry is probably a more readable solution to a non-Schemer. ;)

--
Grant Edwards grante Yow! Hey!! Let's watch
at the' ELEVATOR go UP and
visi.com DOWN at th' HILTON HOTEL!!

Alex Martelli

unread,
Aug 10, 2001, 4:20:08 PM8/10/01
to
"Grant Edwards" <gra...@visi.com> wrote in message
news:slrn9n842d...@grante.comtrol.com...
...

> > Use different callables for the callbacks -- if it must be
...

> A slightly different way is to use lamgda. It's best for
> simple cases where you just want to pass a few arguments that

I disagree with your claim that it's best.

> 15 b.bind("<Button-1>",lambda event,xx=x,yy=y:
board.toggleSpace(xx,yy))

I'd code this (assuming no nested scope, just as you're assuming):
def togglethis(event, xx=x,yy=y):
board.toggleSpace(xx,yy)
b.bind("<Button-1>", togglethis)

> Line 15 is where the interesting bit happens.

We do agree on this. But the lambda is obscuring it a bit:-).

> called. That function receives one parameter called "event"
> from Tk, and has two default parameters "xx" and "yy" whose
> default values are set when the binding is done. Since Tk only
> passes the single "event" parameter, "xx" and "yy" will have
> the values specified by "x" and "y" when the lambda operation
> took place.

Same for my favourite solution, except that there is no lambda
form -- the local def takes lambda's place.

> Curry is probably a more readable solution to a non-Schemer. ;)

I am or used to be enough of a schemer that our common-lisp
friend recently caught me out as an adorer of tail recursion, but
I still dislike _Python's_ lambda, with all of its limits and issues.

A local def appears to me to be a preferable solution in just
about every case. Giving the callable a somewhat helpful name
is not a big problem, and generally the splitting of a statement
into two that the def forces you to do is no big issue either.

The def lends itself to easier refactoring if you ever need more
than an expression there (again not an earth-shaking issue,
but more important to me than the above ones).


Alex

Grant Edwards

unread,
Aug 10, 2001, 11:51:11 PM8/10/01
to
On Fri, 10 Aug 2001 22:20:08 +0200, Alex Martelli <ale...@yahoo.com> wrote:
> ...
>> > Use different callables for the callbacks -- if it must be
> ...
>> A slightly different way is to use lamgda. It's best for
>> simple cases where you just want to pass a few arguments that
>
>I disagree with your claim that it's best.

My phrasing was bad. What I meant was that of the various
situations where one might use lambda the simplest cases are
the best candidates for lambda usage. I wasn't claiming that
lambda is the best option compared to curry or local defs.


>> 15 b.bind("<Button-1>",lambda event,xx=x,yy=y: board.toggleSpace(xx,yy))
>
>I'd code this (assuming no nested scope, just as you're
>assuming):
>
> def togglethis(event, xx=x,yy=y):
> board.toggleSpace(xx,yy)
> b.bind("<Button-1>", togglethis)

That's definitely better. For some reason (undoubtedly due to
restrictions in other languages) I often forget that I can use
a "def" inside the body of a function.

In this case, it's probably a lambda because this program was
translated from Scheme, and it was already a lambda. :)

>Same for my favourite solution, except that there is no lambda
>form -- the local def takes lambda's place.
>
>> Curry is probably a more readable solution to a non-Schemer.
>> ;)
>
>I am or used to be enough of a schemer that our common-lisp
>friend recently caught me out as an adorer of tail recursion,

Yup, I was recentlry caught with my Scheme history showing when I
assumed that recursion was a preferred flow-control mechanism
in CL.

>but I still dislike _Python's_ lambda, with all of its limits
>and issues.

It's obscure _and_ crippled. One or the other could be
tolerated. I've been tripped up by lambda's scoping issues
more than once (but local def's have the same problem -- at
least for now).

>A local def appears to me to be a preferable solution in just
>about every case.

I can't really come up with an example where a lamba is
superior to a local def for anything that's not completely
trivial. The only place a lamba seems like a good option is
applying a trivial function using map() or filter(). But, that
usage has been made largely redundant with list comprehensions.

But -- since there's no such thing as tuple-comprehensions,
you still need filter() if you're working with tuples, so maybe
lambda still has some utility is situations like:

t2 = filter(lambda x: x>0, t1)

On a completely different topic, I don't quite understand why,
when passed a tuple, map() returns a list and filter() returns
a tuple. I was quite surprised that they do different things.

--
Grant Edwards grante Yow! NEWARK has been
at REZONED!! DES MOINES has
visi.com been REZONED!!

Alex Martelli

unread,
Aug 11, 2001, 5:17:00 AM8/11/01
to
"Grant Edwards" <gra...@visi.com> wrote in message
news:slrn9n9auf...@tuxtop.visi.com...

> On Fri, 10 Aug 2001 22:20:08 +0200, Alex Martelli <ale...@yahoo.com>
wrote:
> > ...
> >> > Use different callables for the callbacks -- if it must be
> > ...
> >> A slightly different way is to use lamgda. It's best for
> >> simple cases where you just want to pass a few arguments that
> >
> >I disagree with your claim that it's best.
>
> My phrasing was bad. What I meant was that of the various
> situations where one might use lambda the simplest cases are
> the best candidates for lambda usage. I wasn't claiming that
> lambda is the best option compared to curry or local defs.

Then I completely agree with you! Yes, if you do want to use
lambda, using it in simple cases is much better than using it
in complicated cases.


> >> 15 b.bind("<Button-1>",lambda event,xx=x,yy=y:
board.toggleSpace(xx,yy))
> >
> >I'd code this (assuming no nested scope, just as you're
> >assuming):
> >
> > def togglethis(event, xx=x,yy=y):
> > board.toggleSpace(xx,yy)
> > b.bind("<Button-1>", togglethis)
>
> That's definitely better. For some reason (undoubtedly due to
> restrictions in other languages) I often forget that I can use
> a "def" inside the body of a function.

It's easy to forget it, which is why I keep posting subtle
reminders thereof:-). C and C++ don't let one nest functions,
and while that does simplify the language, sometimes it's
slightly constraining. (Scheme, OTOH, is quite happy with
nested functions -- be they named or unnamed -- indeed,
the named form is defined in term of the unnamed lambda
form, which seems quite a bit better than giving named and
unnamed forms of nested functions different abilities, as
Python does:-).

> In this case, it's probably a lambda because this program was
> translated from Scheme, and it was already a lambda. :)

Ability to transliterate from other languages to Python is
indeed often useful (e.g. for didactical purposes) so it's
good to keep in mind possible structural parallels. Alas,
Python's lambda is so constrained that it's rare one can
use it for transliteration (although when the body is just
an expression, it's indeed possible).


> >I am or used to be enough of a schemer that our common-lisp
> >friend recently caught me out as an adorer of tail recursion,
>
> Yup, I was recentlry caught with my Scheme history showing when I
> assumed that recursion was a preferred flow-control mechanism
> in CL.

So we're in the same boat:-).


> >but I still dislike _Python's_ lambda, with all of its limits
> >and issues.
>
> It's obscure _and_ crippled. One or the other could be
> tolerated. I've been tripped up by lambda's scoping issues
> more than once (but local def's have the same problem -- at
> least for now).

"from __future__ import" is your friend (for either lambda
or local def). But the 'crippled' part doesn't go away (with
lambda -- no such issues with local def).


> >A local def appears to me to be a preferable solution in just
> >about every case.
>
> I can't really come up with an example where a lamba is
> superior to a local def for anything that's not completely
> trivial. The only place a lamba seems like a good option is
> applying a trivial function using map() or filter(). But, that
> usage has been made largely redundant with list comprehensions.

Good point. Yes, that was one important effect of list
comprehensions.


> But -- since there's no such thing as tuple-comprehensions,
> you still need filter() if you're working with tuples, so maybe
> lambda still has some utility is situations like:
>
> t2 = filter(lambda x: x>0, t1)

Yes, looks better than t2 = tuple([x for x in t1 if x>0]), although
the latter may be more readable (not everybody knows that
filter, while returning a list for most kinds of input sequences,
returns a tuple when the argument is a tuple and a string
when the argument is a string).

> On a completely different topic, I don't quite understand why,
> when passed a tuple, map() returns a list and filter() returns
> a tuple. I was quite surprised that they do different things.

It may violate the principle of least astonishment, particularly
since it doesn't generalize to ALL kinds of sequences, just
tuples and strings (for anything else, filter returns a list).


Alex

Fredrik Lundh

unread,
Aug 12, 2001, 7:07:50 AM8/12/01
to
Cameron Laird wrote:
> Confession time: in principle, GUI coders also can code
> their own bindings and use %W substitution to compute
> the widget which received the event. Does that work in
> Tkinter?

Tkinter passes an event instance to the event handler,
and %x codes are mapped to event instance attributes.

see table 7-2 for a list of portable attributes:
http://www.pythonware.com/library/tkinter/introduction/events-and-bindings.htm

in this case, you want the event.widget attribute:

w = SomeWidget()
w.bind(..., callback)

def callback(event):
print event.widget

note that the widget attribute is a real Tkinter widget
object (not a Tk widget name), so you can do things
like:

w1 = SomeWidget()
w1.info = 1, somedata
w1.bind(..., callback)

w2 = SomeWidget()
w2.info = 2, somedata
w2.bind(..., callback)

def callback(event):
print event.widget.info
print event.widget.cget("text")


</F>

<!-- (the eff-bot guide to) the python standard library:
http://www.pythonware.com/people/fredrik/librarybook.htm
-->


0 new messages