user2063
user2063

Reputation: 995

Passing variable through lambda into functions Tkinter

My problem with the following code is passing 'i' (just a simple range of numbers but changes according to number_boxes) through lambda to callback in order to have seperate functionality of each box created.

I have tried reading tutorials and attempted various things in my code but it either doesn't work or I get errors 'lambda() requires only 2 arguments, 3 given' etc. I believe I would need to make 'i' a list but I still get this particular error..

I have commented on the code where the problems arise. I need to return the values inside each box as well as overwrite the text.

Thank you.

self.clicked = [] # In my __init__ definition
self.numbers = [StringVar() for i in xrange(self.number_boxes) ]   # Create Stringvar for each box

for i in xrange(self.number_boxes): # For each number, create a box

        self.clicked.append(False) # Not clicked in yet
        self.box.append(Entry(self.frame_table,bg='white',borderwidth=0, width=10, justify="center", textvariable=self.numbers[i], fg='grey')) # Textvariable where I enter a value to get after, need to do for each box (use box[i] but cannot for append)
        self.box[i].grid(row=row_list,column=column+i, sticky='nsew', padx=1, pady=1) 
        self.box[i].insert(0, "Value %g" % float(i+1))
        self.box[i].bind("<Button-1>", lambda event, index=i : self.callback(event, index)) # Need to pass the 'i's' to callback but having lambda difficulties

for i in self.numbers: 
        i.trace('w',lambda index=i: self.numberwritten(index) ) # Need for each box again here

def numberwritten(self, index): # A way of combining numberwritten and callback?
    po = self.box[index].get() # Get the values in each box
    print po

def callback(self, event, index):
        if (self.clicked[index] == False): # When clicked in box, overwrite default text with value and change colour to black, not grey
            self.box[index].delete(0, END)
            self.box[index].config(fg='black')
                self.clicked[index] = True

UPDATE: Current problem: Need to pass all values of 'i' to callback and not just one but how to put list into lambda?

Error:

Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
TypeError: lambda() takes at most 1 argument (3 given)

Upvotes: 2

Views: 3310

Answers (2)

gary
gary

Reputation: 4255

Your code is very close, however this line:

self.box[i].bind("<Button-1>", lambda self, event,i : self.callback(event, data))

should be

self.box[i].bind("<Button-1>", lambda event, index=i: self.callback(event, index))

The object index is just an arbitrary label to which we assign the value of i. Note that Python throws an error if we pass i, rather than a variable name. And we don't need to pass self; it is passed implicitly by its use in the function call: self.callback().

My only other comment is that you should turn clicked into a list, so that you can keep track of which objects the user selects. You can form this exactly the way box is formed. Good luck.


Here are some tips on turning clicked into a list, since I think this is current issue you are having. Edit: Changed several lines per J.F. Sebastian's comments.

# Create an empty list based on the number of boxes created 
# above. This could go in your class's __init__ function, 
# or it could be declared globally, depending on the scope
# of self.box and the loop which determines its length.
self.clicked = ([False] * len(self.box))

# Finally, in your callback, check one member of the list,
# depending on which box was clicked.
def cb(self, event, index):
    if not self.clicked[index]:
        self.box[index].delete(0, END)
        self.box[index].config(fg='black')
        self.clicked[index] = True
    # Print the value of each of the widgets stored in the box list.
    for i, j in enumerate(self.box):
        print("%ith element: %s" % (i, j.get()))  

Upvotes: 2

Don Question
Don Question

Reputation: 11614

updated: it seems your lambda is completly superflous. Remove it altogether.

try:

self.box[i].bind("<Button-1>",self.callback)

instead of:

lambda self, event, i: self.callback(event, i)

update:

While i was typing my answer you seemd to have worsened the situation, maybe a little clarification of the lambda-idiom would help you more then my solution above. ;-)

lambda-functions are also known as anonymous-function, for they don't need to have a name.

compare the following examples:

>>>ulist = 'a1 s3 d2 f4 k6 w9'.split()

def normal_sort(L,R):
    result = int(L[1]) - int(R[1])
    return result

>>> sorted(ulist, normal_sort)
['a1', 'd2', 's3', 'f4', 'k6', 'w9']

the same as a lambda-function:

sorted(ulist, lambda L, R: int(L[1])-int(R[1]))
['a1', 'd2', 's3', 'f4', 'k6', 'w9']

The lambda-function has

  1. no name
  2. no argument parenthesis
  3. no return statement, because the evaluation of it's single statement gets passed back as the return value.

You may name a lambda-function by assigning it to a name:

lambda_sort = lambda L, R: int(L[1])-int(R[1])

but then it may be already useful to switch to a normal function.

Upvotes: 0

Related Questions