1

I am trying to make an interface where a user clicks a TkInter button to select a language, and then the button calls a function (with an argument for the specific language) to set the language for the program.

I tried using Lambdas for passing the functions, but that didn't work.

def showLangButtons():
    tk = Tk()
    root = Canvas(tk, width=100, height=100)
    root.pack()
    langButtons = []
    langs = []
    for a in langf:
        langs.append(a)
    for a in sorted(langs):
        langButtons.append(Button(root, text=lang_names[a][a], width=19,
                                  height=2, command = lambda:setLang(a)))
    # This part of the function displays the buttons on a grid
    const = 0
    while const < (len(langButtons))**(1/2)/1.75:
        const += 1
    n = 0
    while n < len(langButtons):
        langButtons[n].grid(row = int(n/const), column = n%const, sticky = W)
        n+=1
    tk.update()

langf is a dictionary which contains the list of supported languages. lang_names is a dictionary which contains the names of each language (indexed by the ISO 639-3 code). setLang() takes a string as its argument, specifically the ISO 639-3 code of the language.

I expect the language to be set corresponding to whichever button the user clicks, but it always sets the language to the last language in the language list. For example, there are currently 2 supported languages: English and Icelandic. Regardless of which button I click, it always sets the language to Icelandic, because it is last in alphabetical order.

1 Answers1

0

You need to force a closure when you use lambda:

command=lambda lang=a: setLang(lang)
Henry Yik
  • 22,275
  • 4
  • 18
  • 40
jottbe
  • 4,228
  • 1
  • 15
  • 31
  • In this code, all of the buttons will do the exact same thing – Bryan Oakley Sep 01 '19 at 20:49
  • Yes, I'm certain. `command=setLang` means every button will call `setLang`. You never redefine what `setLang` is, so all buttons, therefore, do the same thing. Also, `args` isn't a valid option for the `Button` class. – Bryan Oakley Sep 01 '19 at 21:33
  • I don't know what you want me to think about. Nowhere in the question or the answer is `guizero` mentioned, so whatever it does is irrelevant. The bottom line is, you provided an answer that doesn't work. – Bryan Oakley Sep 01 '19 at 21:47
  • You're speaking in riddles, I have no idea what you're talking about. Why do you think my thinking is too simple? How is it "simple" to say that that incorrect code is indeed incorrect? – Bryan Oakley Sep 01 '19 at 21:54
  • Dude. Seriously? Any function that is defined once, never redefined, and called in exactly the same manner will absolutely be called in the same manner every time. By definition, calling it in the same way will result it in being called in the same way. – Bryan Oakley Sep 01 '19 at 22:01
  • You're still speaking in riddles. I agree with you, in that if you explain something, you should explain it in the correct way. You did not do that, which is why I initially down-voted. I didn't do anything by chance here and didn't overlook anything, so I don't know what you mean by that. – Bryan Oakley Sep 01 '19 at 22:11
  • Arguably your method works, but OP's original function merely needs a slight modification. The common way is to use `partial` or `lambda`, for example `command=lambda i=a:setLang(i))`. `i=a` is required to force a closure due to Python's late binding. Your solution is a very indirect way since it requires creating two extra functions just to have `setLang` executed. – Henry Yik Sep 02 '19 at 02:31