0

I raise this question because I just saw this question What are the limitations of callback functions associated with Tkinter traces?.

And I use lambda to add another arguemnt. Here is the code:

from tkinter import *

def callbackfunc(*args, **kwargs):
    print(args,kwargs)

class App(object):
    def __init__(self, master):
        frame = Frame(master)
        frame.pack()

        optionvalue = IntVar(master)
        optionvalue.set(2)
        optionvalue.trace("w",lambda a,b,c,x='test':callbackfunc(x))
        self.optionmenu = OptionMenu(master, optionvalue, 1, 2, 3, 4)
        self.optionmenu.pack()

root = Tk()
app = App(root)
root.mainloop()

my output: ('test',) {} What I want to know is: Why aren't the other 3 arguments outputted if I use lambda?

Community
  • 1
  • 1
laike9m
  • 18,344
  • 20
  • 107
  • 140

2 Answers2

2

The problem is this line:

optionvalue.trace("w",callbackfunc())

Instead of passing callbackfunc to optionvalue.trace, you're calling it -- with no args, hence your output.

You should use this instead:

optionvalue.trace("w", callbackfunc)

with no parenthesis, as in the question you linked.

Community
  • 1
  • 1
Schnouki
  • 7,527
  • 3
  • 33
  • 38
  • I've edited my question,in fact that is what I want to ask.Thank you – laike9m Mar 27 '13 at 13:22
  • Well, you're still calling `callbackfunc` with just `x`, ignoring `a`, `b` and `c`. You should use something like `lambda a, b, c, x="test": callbackfunc(a, b, c, x)` instead. – Schnouki Mar 27 '13 at 13:46
  • `lambda a, b, c, x="test": callbackfunc(a, b, c, x)` defintely works,but if I use `lambda x='test':callbackfunc(x)`,then it raise `TypeError: () takes from 0 to 1 positional arguments but 3 were given` – laike9m Mar 27 '13 at 13:53
  • The contradictory thing is,the `TypeError` tells me that though I write `callbackfunc(x)` ,I actually passed a,b,c in,but I can't get them from `*args` – laike9m Mar 27 '13 at 13:56
  • No, this means that your `lambda` is called (by Tkinter) with 3 arguments, but you only defined it with 1 (optional) arg. Maybe you should just use `optionvalue.trace("w", print)` to check how it's called (`print` is a function in Python 3). – Schnouki Mar 27 '13 at 14:29
  • Yes,what I mean is the 3 arguments a,b,c are passed in implicitly. – laike9m Mar 27 '13 at 16:19
2

Let's look at this line:

optionvalue.trace("w",lambda a,b,c,x='test':callbackfunc(x))

This is conceptually almost identical to doing it this way:

def i_dont_care_what_the_name_is(a,b,c,x='test'):
    callbackfunc(x)
optionvalue.trace("w", i_dont_care_what_the_name_is)

When the trace fires, it calls the lambda with three values -- this is the documented behavior. It doesn't matter if you're using lambda or not, Tkinter will always pass three arguments to the callback.

You've created the lambda to take a fourth optional value, a very common way to use lambda. However, in your lambda, you are explicitly calling callbackfunc and only passing it a single value, x.

It works like this: the callback calls your lambda with three arguments. You've defined your lambda to have an extra forth argument. The lambda then calls your callback, but you only give it one parameter.

So, the answer to the question "Why aren't the other 3 arguments outputted if I use lambda?" is "because you're only passing one of those arguments to your other function from inside the lambda".

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • If I use `lambda x='test':callbackfunc(x)`,then it raise TypeError: `() takes from 0 to 1 positional arguments but 3 were given`.I want to make sure if the reason is: When not using lambda,Python automatically passes in 3 arguemnts to the function/method which should receive these arguments.Then lambda is used,this time it's the `lambda` which takes these arguemnts,and `lambda` requests arguments to be passed in explicitly. Is that the reason? – laike9m Mar 28 '13 at 01:58
  • Neither you nor Schnouki has given explanation about this. – laike9m Mar 28 '13 at 02:01
  • @laike9m: when Tkinter calls the callback, it will always pass three parameters because that is what is designed to do. That is why you get that error if you define the lambda to take only one parameter. The callback must take at least three parameters. This has nothing to do with whether you're using lambda or not -- tkintre has no idea. It will _always_ pass three parameters. You must provide a callback that accepts at least three parameters. It can be a proper function, or it can be a lambda, but regardless, it must take at least three parameters. – Bryan Oakley Mar 28 '13 at 02:15