Iterating through buttons in tkinter

Question:

I try to create buttons in tkinter using a loop.

def bulkbuttons():
    i = 0
    x = 0
    buttons=[]
    while x < 10:
        buttons.append('button' + str(x))
        x+=1
    while i < 10:
        buttons[i]=Button(window,text=str(i), width=12,borderwidth=1, relief='raised', bg='#134f5c', fg='#FFFFFF', command=(lambda: justprint(i)))
        buttons[i].grid(row=i, column=2)
        i += 1

The justprint function prints i.

def justprint(i):
    print(i)

The buttons show the correct numbers. But the justprint function only prints the last number, 10, no matter which button I click.

What did I do wrong? I want to click a button and then use the number of the button as a parameter for some functions.

Asked By: mn_wy

||

Answers:

You can pass i as a default argument in the lambda function. Alternatively you can use the partial function from functools:

from functools import partial 

def bulkbuttons():
    # Put all formatting related parameters of the Button constructor in a dictionary
    # Using ** will pass dict values to 
    formatting = {'width': 12, 
                  'borderwidth': 1,
                  'relief': 'raised',
                  'bg': '#134f5c', 
                  'fg': '#FFFFFF'}

    for i in range(10):
        buttons[i]=Button(window,
                          text=str(i),
                          **formating,                      
                          command=partial(justprint,i))
        buttons[i].grid(row=i, column=2)

Notes:

  • Your first while loop can be expressed as this pretty and concise list comprehesion:
buttons = ['button' + str(x) for x in range(10)]

Try using this notation when possible, since they will save you typing time and is far more readable.

  • Your second while loop overwrites the list created in the first one, so there is no need to do the first while loop.

  • I took the liberty of placing all your formating related parameters for the Button constructor in a dictionary, you can pass them all at once passing **formating to the constructor. You can place these parameters in your global scope (outside functions) and define models for every type of button, saving you some time and making the code more readable.

  • If you have a fixed number of iteration is a loop, use for i in range(n) instead of a while, it will avoid some infinite loops when you forget the i+=1

Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.