1

I am a new Python user. I try to make a serie of entries identified by labels and gets the values inserted. The callback functions seem well done but it's allways the third entry's value which I reach. I am Python/Linux user, version 2.7.6 It seem to be lambda's declaration issue, can you help me ?

import Tkinter as tk
class c1(tk.Frame):
    def __init__(self, master=None):
        r = 0
        tk.Frame.__init__(self, master)
        self.grid()
        self.master.title('Test three binds')
        self.master.geometry('300x200+400+400')
        self.ents = {}
        for i in ['aaa', 'bbb', 'ccc'] :
            r += 1
            self.ents[i] = c2()
            self.ents[i].label = tk.Label(text = i)
            self.ents[i].label.grid (row = r, column = 0)
            self.ents[i].entry = tk.Entry()
            self.ents[i].entry.grid (row = r, column = 1)
            self.ents[i].val = tk.StringVar()
            self.ents[i].val.set(i)
            self.ents[i].entry["textvariable"] = self.ents[i].val
            self.ents[i].entry.bind('<Key-Return>', lambda X : self.verif(self.ents[i]))
    def verif(self, event) :
        print event.val.get()

class c2 :
    pass

mm = c1()
for ii in  mm.ents :
    print mm.ents[ii].val.get()

mm.mainloop()

2 Answers2

2

One problem is this line of code:

self.ents[i].entry["textvariable"] = self.ents[i].val

The textvariable attribute must be set to an instance of StringVar (or one of the other tkinter variables), not the value of such an instance. You need to remove .val.

The other problem is that you need to bind (as in: create a closure) the value of i at the time you create the lambda. You can do that like this:

self.ents[i].entry.bind('<Key-Return>', lambda event, i=i : self.verif(self.ents[i]))

However, if all you need in self.verif is the value from the entry widget, you can reduce the complexity of your code by removing the use of the textvariable altogether, as well as the use of lambda since the event contains a reference to the widget itself. Both textvariables and lambda are rarely truly required.

self.ents[i].entry.bind('<Key-Return', self.verif)
...
def  verif(self, event):
    print event.widget.get()

On a final note, tkinter Entry widgets have built-in verification support. Take a look at this answer for more information: https://stackoverflow.com/a/4140988/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thank you for your answer, val is needed to set values of Entrys, your second tip solves the issue : great. I need (I think so) this complication because the c2 instances include more data, not show here, found by 'aaa', 'bbb', 'ccc' index. Thanks again. – Jacek Lempicki Jun 03 '16 at 13:57
0

You are correct, it comes from a lambda declaration issue.

The i in your loop is not actually changing, as lambdas don't closure the parameter. As such, you can make a simple adjustment to this one line from the for loop to remedy this issue:

self.ents[i].entry.bind('<Key-Return>', lambda event, i = i: self.verif(self.ents[i]))

Changing the lambda to use i = i allows you to capture the current value of i for each iteration.

Community
  • 1
  • 1
maccartm
  • 2,035
  • 14
  • 23