Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

tkinter.Button 'command' parameter not set correctly

I use Tkinter for show database search results and show data with a for loop, and I set the command parameter [for widget tkinter.Button] with lambda func, but when I click on a data for full show, instead of running show func with correct parameters, I run show func with last data.
My Code:

import tkinter
from tkinter import Button, ttk

def show_results(result: dict):
    def show(text):
        w = tkinter.Tk()
        w.geometry('500x450')
        result_lb = ttk.Label(master=w, text=text)
        result_lb.place(x=5, y=5)
        w.mainloop()

    win = tkinter.Tk()
    win.geometry('500x800')
    x = 5
    y = 5
    for res in result:
        r = result[res]
        print(r)
        cmd = lambda: show(r)
        Button(master=win, text=f"    {r}    ", command=cmd).place(x=x, y=y)
        y += 25
    win.mainloop()


show_results({'0': ['jack', '18', 'newyork'], '1': ['alex', '20', 'texas']})

Also I use tkinter.ttk.Button, but this bug not solved.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The bug is due to the fact that the lambda finds the value of r at the point that the button is called, and, as the for loop has already finished, the value would be the last index, or 1. This explained more clearly in this post here: tkinter creating buttons in for loop passing command arguments (pointed out by @001)

When you use that lambda to define your function, the open_this call doesn’t get the value of the variable i at the time you define the function. Instead, it makes a closure, which is sort of like a note to itself saying "I should look for what the value of the variable i is at the time that I am called". Of course, the function is called after the loop is over, so at that time i will always be equal to the last value from the loop.

To fix this, we need to do (also from the same answer) cmd = lambda row=r: show(row), here defining the value of row to r, but not when cmd is called, instead, when cmd is assigned.

A simple example of this is this:

import tkinter
from tkinter import Button, ttk


def show(text):
    w = tkinter.Tk()
    w.geometry('500x450')
    result_lb = ttk.Label(master=w, text=text)
    result_lb.place(x=5, y=5)
    w.mainloop()

def show_results(result: dict):


    win = tkinter.Tk()
    win.geometry('500x800')
    x = 5
    y = 5
    for key, value in result.items():
        r = value
        print(key, value)
        cmd = lambda row=r: show(row)
        Button(master=win, text=f"    {r}    ", command=cmd).place(x=x, y=y)
        y += 25
    win.mainloop()

testvar = {'0': ['jack', '18', 'newyork'],
           '1': ['alex', '20', 'texas']}

show_results(testvar)

The reason that this works is because the variable row is getting defined when the lambda evaluates (probably not the right word for it though), not when the lambda is called.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading