Mailing List Archive

Callbacks and "callable" objects
A while back I asked how to pass parameterized callbacks to Tkinter.
Now I need to explore how best to do partial resolution of callback
arguments. For example, define values for arguments 1 and 2 at
registration time, but leave argument 3 to be populated by Tkinter.

Probably makes no sense. So let me use an example:

-------------------------------------------------------------------------------

For simple callbacks you can use a lambda or function wrapper:

def SetColor( color ):
print color

wgt1.configure( command = lambda color="red" : SetColor(color) )
wgt1.configure( command = lambda color="blue": SetColor(color) )

One suggestion Thomas Heller offered before was to use a callable object
instead; this gives a much simpler appearance:

def SetColor( color ):
print color

wgt1.configure( command = Call( SetColor, "red" ) )
wgt1.configure( command = Call( SetColor, "blue" ) )

(Call class source attached below in case you're interested).

-------------------------------------------------------------------------------

The wrinkle now is what if SetColor is an event callback (i.e. Tkinter
provides an event argument):

def SetColor( color, event ):
print color

this breaks the callable object form:
wgt1.configure( command = Call( SetColor, "red" ) )

with Tkinter giving:
TypeError: too many arguments; expected 1, got 2

So my quetsion is, can the "callable object" be used? Or do I have to fall
back to a lambda wrapper?

Thanks,

Randall



-------------------------------------------------------------------------------
class Call:
"""Instances of this class store a function as well as a list of arguments.
When they are called, the function will be called together with the
arguments used for creating the instance.
Slightly different than lambda, but nicer syntax."""

def __init__ (self, func, *args):
self.func = func # save the function (or bound method,or ...)
self.args = args # save the arguments to use
def __call__ (self):
apply (self.func, self.args) # call function, using args as arguments.
-------------------------------------------------------------------------------
Callbacks and "callable" objects [ In reply to ]
Randall Hopper <aa8vb@vislab.epa.gov> writes:
> A while back I asked how to pass parameterized callbacks to Tkinter.
> Now I need to explore how best to do partial resolution of callback
> arguments. For example, define values for arguments 1 and 2 at
> registration time, but leave argument 3 to be populated by Tkinter.
>
> Probably makes no sense. So let me use an example:

I don't think that you've described your problem too well here, but I
understand the problem faced in the example well enough.

What you want is a callable object with state indpendent of the
parameters it's passed by whatever's calling it. In your example
below, you want the callback to have a `color' state.

The most Pythonic way of doing this is to define a class like this:

class ColorCallback:
def __init__(self,color):
self.color=color
def callback(self,event):
print self.color

wgt1.configure( command = ColourCallback("blue").callback )

This can be a lot of typing for a measly little callback however,
leading to the dreaded `default argument hack':

def make_color_callback(color):
def _(event,color=color):
print color
return _

This is less typing (& quicker) but it is sneaky. Tim Peters once
said:

> Not at all, although I agree here too <wink>. It's like saying a
> fork is broken just because it's not that handy for jacking up a
> car. That is, Guido implemented the syntax to support default
> arguments, and it works great for that purpose! Using it to fake
> closures is a hack, and the "hey, this is cool!" / "hey, this really
> sucks!" mixed reaction thus follows, much as pain follows a car
> falling on your skull. Stick to stabbing peas, or even teensy
> pea-sized closures, and a fork serves very well.

which sums it up far better than I ever could.

(Pay a visit to
http://starship.python.net/crew/amk/quotations/python-quotes.html
somtime...)

HTH

Michael Hudson
Jesus College
Cambridge
CB5 8BL
Callbacks and "callable" objects [ In reply to ]
Randall Hopper wrote:
>
> A while back I asked how to pass parameterized callbacks to Tkinter.
> Now I need to explore how best to do partial resolution of callback
> arguments. For example, define values for arguments 1 and 2 at
> registration time, but leave argument 3 to be populated by Tkinter.

With a speed penalty of about a factor 2,
Don Beaudry's functor module provides you with the
partial closure functionality which you would like.
Please get the current version from ftp.python.org,
it works with Python 1.5 .

BTW, I think you will go back to the default parameter
trick after a while, since it is faster, which might be
an issue for callbacks.

ciao - chris

--
Christian Tismer :^) <mailto:tismer@appliedbiometrics.com>
Applied Biometrics GmbH : Have a break! Take a ride on Python's
Kaiserin-Augusta-Allee 101 : *Starship* http://starship.python.net
10553 Berlin : PGP key -> http://wwwkeys.pgp.net
PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF
we're tired of banana software - shipped green, ripens at home
Callbacks and "callable" objects [ In reply to ]
Michael Hudson:
(wrapper class)
(default-arg lambda/function wrapper)

Christian Tismer:
(functor module)
(default-arg lambda/function wrapper)

Thanks for the responses. I now have several good ways to approach this.

Randall