0

This code

from tkinter import *

root = Tk()
test = StringVar()
test.set('')
passarg = 'hello'
test.trace('w', lambda passed = passarg: checkvar(passed))

testEntry = Entry(root, textvariable = test)
testEntry.pack(fill = X)

root.mainloop()
def checkvar(passedarg, *args):
    print(passedarg)

produces a TypeError: (lambda)() takes 1 positional argument but 3 were given when the callback is called, even though I already use *args in the function definition.

Alternatively, I've tried adding some fake arguments to get to 3, but then the callback doesn't get any of the passed arguments at all:

from tkinter import *

def checkvar(passedarg, *args):
    print(passedarg)
    print(args)

root = Tk()
test = StringVar()
test.set('')
passarg = 'hello'
test.trace('w', lambda passed = passarg, a = 1, b = 2: checkvar(passed, a, b))

testEntry = Entry(root, textvariable = test)
testEntry.pack(fill = X)

root.mainloop()

prints

PY_VAR0
('', 'w')

whenever I write in the entry field.

I need a callback function with arguments for a bigger program, so is there any way to do that?

Clarification: The bigger program has many entry fields of varying max input lengths, all of which are checked to only contain a subset of ASCII characters (with a regex ^[0-9A-Za-z\.\-\+]+$). The underlying idea was that I could have a general validate function that would be passed a tkintervar (to check the characters) and an integer length in a trace, instead of creating a separate function for each length limit.

bqback
  • 55
  • 9
  • 1
    I don't think you can, but I may be proven wrong; the callback goes through a tkinter listener that doesn't seem to be passing the args along. Why do you need args for? You could maybe implement one callback per variable, and assign a default value to key word args? Or you could implement your own class of variables, with a pub/sub that passes args. – Reblochon Masque Sep 15 '19 at 12:20
  • @ReblochonMasque I edited the post to add a clarification for why I need this. I'll try to work something out with a custom class. – bqback Sep 15 '19 at 12:53
  • 1
    @ReblochonMasque: the listener _is_ passing the arguments along. That's precisely the cause of the error: the listener is passing three positional arguments to the callback, the callback (the lambda function) isn't accepting them. – Bryan Oakley Sep 15 '19 at 13:30
  • Cool, thanks @BryanOakley, I learned something. – Reblochon Masque Sep 15 '19 at 16:45

1 Answers1

3

Your lambda is what must accept the arguments and pass them on to your function, since it is the lambda that is being called when the variable changes. The simplest fix to make your code work is to change the lambda to this:

test.trace('w', lambda *args, passed = passarg: checkvar(passed, *args))

You say you're using this for input validation. Are you aware that the entry widget has a built-in feature for entry validation? See Interactively validating Entry widget content in tkinter

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks, I'm aware of `validatecommand`, but I found it a bit too complex for myself. `.trace()` seems easier for now, although I'll try to repeat this using `validatecommand` in my spare time. – bqback Sep 16 '19 at 02:38