2

I run into small problem trying to create 0-9 number buttons. I want to define 10 buttons for numbers from 0 to 9 in single loop. Each of them is supposed to add its value to self.user_input = tk.StringVar() which will be printed in label. Clicking 5 button, 7 button and then 0 button will give output 570. I try to use lambda to create command for each button, but instead of getting different values I have 9 everywhere. Here is my code:

import tkinter as tk
import tkinter.ttk as ttk

class Gui(tk.Tk):
    def __init__(self):
        super().__init__()
        self.user_input = tk.StringVar()
        tk.Label(self, textvariable=self.user_input).grid()
        self.create_buttons()

    def create_buttons(self):
        for x in range(10):
            ttk.Button(self, text=x, command=lambda: self.user_input.set(self.user_input.get() + str(x))).grid()

app = Gui()
app.mainloop()

How can I fix code above to make it work as expected (description up)?

7
  • Are you sure the code you posted is faulty ? It show the 9 different buttons on my machine (Ubuntu 19.10, python 3.7.5) Commented Nov 22, 2019 at 9:55
  • @Sayse Okay thanks, I will add create_buttons method to this class, but it will still include lambdas. How can I correct it? I want to have buttons from 0 to 9 where each of them update one label which refers to StringVar Commented Nov 22, 2019 at 9:56
  • 1
    Change lambda: ... to lambda x=x: .... Commented Nov 22, 2019 at 10:13
  • 1
    @acw1668 Thank you, it solved my problem. Can you add it as answer and describe a little to me why it is working like that or link to some lecture. I will accept it as answer to this question. Commented Nov 22, 2019 at 10:19
  • 1
    @Ethr "why it is working ": Read Python and Tkinter lambda function Commented Nov 22, 2019 at 11:12

1 Answer 1

2

Put the bulk of your code in a proper function. A best practice is that a lambda for a widget callback should only ever call a single function. Complex lambdas are difficult to read and difficult to debug.

The second part of the solution is to create a closure. The trick for that is to make the variable you're passing in bound to the lambda as a default argument.

The callback looks something like this:

def callback(self, number):
    new_value = self.user_input.get() + str(number)
    self.user_input.set(new_value)

Defining each button looks something like this:

def create_buttons(self):
    for x in range(10):
        button = ttk.Button(self, text=x, command=lambda number=x: self.callback(number))
        button.grid()

Pay particular attention to number=x as part of the lambda definition. This is where the current value of x is bound to the number parameter inside the lambda.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.