What's the importance of lambda functions in tkinter? - python

Can anyone explain to me the importance of the lambda function when creating interface with Tkinter?
I am building a super simple interface to get familiar with Tkinter and I wanted to make so that when I press the "Return" key on keyboard, it would have the same effect as dragging the mouse to the "Submit" button on the screen.
I had some problems with it because nothing seemed to work. That's what I was doing:
self.master.bind("<Return>", self.concluir_return)
Where self.concluir_return is the function responsible for making what I want after pressing the "Submit" button. But it was giving me a TypeError:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: concluir_return() takes 1 positional argument but 2 were given
Which I don't understand at all, since only one argument was being given (self)
But all was solved when I looked into web and modified the line of code with the lambda function.
self.master.bind("<Return>", lambda event: self.concluir_return())
It works perfectly, but I don't quite understand what is happening. I would appreciate if someone could explain.
Also, I hope this is not too generic of a question.

Can anyone explain to me the importance of the lambda function when creating interface with Tkinter?
Arguably, they aren't important at all. They are just a tool, one of several that can be used when binding widgets to functions.
The problem with the binding in your question is due to the fact that when you use bind to bind an event to a function, tkinter will automatically pass an event object to that function you must define a function that accepts that object.
This is where lambda comes in. The command needs to be a callable. One form of a callable is simply a reference to a function such as the one you're using (eg: command=self.concluir_return). If you don't want to modify your function to accept the parameter you can use lambda to create an anonymous function -- a callable without a name.
So, for your specific case, you can define a lambda that accepts the argument, and then the lambda can call your function without the argument.
But all was solved when I looked into web and modified the line of code with the lambda function.
self.master.bind("<Return>", lambda event: self.concluir_return())
This works because the code is effectively the same as if you did this:
def i_dont_care_what_the_name_is(event):
self.concluir_return()
self.master.bind("<Return>", i_dont_care_what_the_name_is)
As you can see, lamda isn't required, it's just a convenient tool that lets you create a simple function on the fly that calls another function.

The bind method takes two arguments, sequence and handler, and will call f(event) when the specified event occurs.
In your case, concluir_return wasn't expecting any argument other than self, so your code raised an error when it was called with event.
The lambda function you used is the equivalent of:
def f(event):
return concluir_handler()
so it bypasses the problem by just ignoring the event argument.
Another way of doing this would be to add an argument to concluir_return.

Related

Understanding Python Lambda behavior with Tkinter Button [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
I would like to understand how a button is working using lambda.
I have the following Python code:
from tkinter import *
def comando_click(mensagem):
print(mensagem)
menu_inicial = Tk()
menu_inicial.geometry("500x250+200+200")
botao = Button(menu_inicial, text = "Executar", command=comando_click("Nova_Mensagem"))
botao.pack()
menu_inicial.mainloop()
But my button doesn't work when I click on it, it only shows the print once in the console when I run the code, I added some prints here in the question:
Problem Picture one
Well it seems that when I use the Lambda function in the button it works and I really would like to know why.
Lambda working button Picture one
I just added to the button the lambda :
botao = Button(menu_inicial, text = "Executar", command=lambda:comando_click("Nova_Mensagem"))
Lambda working button Picture two
Why with lambda it works?
It shoudn't work without lambda too since lambda is basically a anonymous function?
I am extremely curious to understand why it works, thank you all for the help :)
Edit: I would like to thank you guys, now I finally understand what was going on and how Python was working. Thank you all very much :D
When you use () with a function name(func(args)), then it is immediately calling/invoking the function while python is executing the line, you do not want that. You want to ONLY call the function when the button is clicked. tkinter will internally call the function for you, all you have to do is give the function name.
Why use lambda? Think of it as a function that returns another function, your code can be lengthened to:
func = lambda: comando_click("Nova_Mensagem")
botao = Button(menu_inicial, text = "Executar", command=func)
func is the function name and if you want to call it, you would say func(). And when you say command=comando_click("Nova_Mensagem") then command has the value returned by command click(because you call the function with ()), which is None and if I'm not wrong, if the given value is None, it will not be called by tkinter. Hence your function is executed just once because of () and as a result of calling the function, you are assigning the value of the function call(None) before the event loop starts processing the events.
Some other methods:
Using partial from functools:
from functools import partial
botao = Button(.....,command=partial(comando_click,"Nova_Mensagem"))
Using a helper function:
def helper(args):
def comando_click():
print(args)
return comando_click
botao = Button(...., command=helper("Nova_Mensagem"))
IMO, lambdas are the easiest way to proceed with calling a function with arguments.
In this code:
command=comando_click("Nova_Mensagem")
you have called the comando_click function, once, and assigned the result (None) to the command argument. Nothing will happen when command is called (in fact you should get a TypeError exception because None is not callable).
In this code:
command=lambda:comando_click("Nova_Mensagem")
you have not actually called comando_click yet -- you have created a new function (using lambda) that will in turn call comando_click when it is called. Every time the button is clicked, your new function will get called.
If the lambda is confusing, you can do the exact same thing with a def like this:
def button_command():
comando_click("Nova_Mensagem")
...
command=button_command # no ()! we don't want to actually call it yet!
The lambda expression is just an alternative to using def when you want to create a small single-use function that doesn't need a name (e.g. you want to make a function that calls another function with a specific argument, exactly as you're doing here).
The issue is that with comando_click("Nova_Mensagem") you are executing the function. So command=None.
In the second case lambda:comando_click("Nova_Mensagem") is returning a lambda, that internally calls comando_click("Nova_Mensagem").
Fix: just put command=comando_click.
If you want to personalize the lambda with arguments you could write something like this:
def handler(args):
def custom_handler():
print(args)
return custom_handler
botao = Button(menu_inicial, text = "Executar", command=handler("my custom string"))

Function executes correctly as a tkinter button command but not in the script

This is my first question on StackOverflow. So far lurking was enough to solve all my problems.
I'm a python newbie and I don't fully understand the meaning behind 'self' yet.
I defined a function (not a method. It's not inside a class) as
def pcal_thresh(self):
p_th = p_thresh.get()
print('p_th')
I am trying to use it in 2 separate conditions. First as a command for Tkinter
p_thresh = tk.Scale(calibration, from_=255, to=1, length=int(y_height*1.2), command=pcal_thresh)
Second, inside another function
def confirm():
if not top_distance == 0:
pcal_thresh()
In this exact configuration the function "pcal_thresh()" executes correctly as a Tkinter command, but not inside another function. If I remove 'self' from the declaration, it's the opposite. Works fine when used inside a function, but not as a Tkinter command. What can be the issue here?
self does not have a default value, so even if you don't use it, you still need to provide a value when you call pcal_thresh. As a callback, it receives the new scale value when called.
Either provide a dummy argument
def confirm():
if not top_distance == 0:
pcal_thresh(None)
or provide a default value:
def pcal_thresh(self=None):
p_th = p_thresh.get()
print('p_th')

Why does function run without paramater?

I'm currently learning curses in python, and I found this piece of code online that is confusing me.
import curses
def draw_menu(stdscr):
# do stuff
# if you want more code just let me know
def main():
curses.wrapper(draw_menu)
if __name__ == "__main__":
main()
When I run this I don't get the expected missing 1 required positional argument error, since there is no parameter being passed in the curses.wrapper(draw_menu) line. Is this a curses thing? Any help is greatly appreciated.
A function is a datatype, just as much as strings, integers, and so on.
def my_function(txt):
print(txt)
here type(my_function) # => <class 'function'>
You invoke the code inside the function when you call it with parenthesis : my_function('hello') # => prints hello
Until then you can perfectly pass a function as an argument to another function.
And that last one can call the one you passed giving it some parameters.
Like in your case, I'd guess that curses.wrapper() creates a screen interface that it passes as argument your draw_menu() function.
And you can probably use that screen object to build your curse app.
See this : Python function as a function argument?
There's a big difference between curses.wrapper(draw_menu) and curses.wrapper(draw_menu()). curses.wrapper(draw_menu) calls curses.wrapper and passes the function draw_menu into it as an argument. In contrast, curses.wrapper(draw_menu()) would call draw_menu and pass its return value into curses.wrapper.
curses.wrapper will call the function you pass it. From that link:
Initialize curses and call another callable object, func, which should be the rest of your curses-using application.
E.g., it will call draw_menu when curses is completely initialized.
Here is the signature for curses.wrapper from here.
curses.wrapper(func, /, *args, **kwargs)
It says that you need to give curses.wrapper a function reference argument followed by zero or more arguments and keyword arguments. Your code satisfies those requirements.
Python allows function signatures like this to enable developers a lot of flexibility regarding what can be passed in by the caller.

Can I utilize Pyside clicked.connect to connect a function which has a parameter

I want to have a function in main class which has parameters not only self.
class Ui_Form(object):
def clearTextEdit(self, x):
self.plainTextEdit.setPlainText(" ")
print("Script in Textbox is Cleaned!",)
x will be my additional parameter and I want clearTextEdit to be called by click.
self.pushButton_3.clicked.connect(self.clearTextEdit(x))
it does not allow me to write x as parameter in clicked. Can you help me!
Solution
This is a perfect place to use a lambda:
self.pushButton_3.clicked.connect(lambda: self.clearTextEdit(x))
Remember, connect expects a function of no arguments, so we have to wrap up the function call in another function.
Explanation
Your original statement
self.pushButton_3.clicked.connect(self.clearTextEdit(x)) # Incorrect
was actually calling self.clearTextEdit(x) when you made the call to connect, and then you got an error because clearTextEdit doesn't return a function of no arguments, which is what connect wanted.
Lambda?
Instead, by passing lambda: self.clearTextEdit(x), we give connect a function of no arguments, which when called, will call self.clearTextEdit(x). The code above is equivalent to
def callback():
return self.clearTextEdit(x)
self.pushButton_3.clicked.connect(callback)
But with a lambda, we don't have to name "callback", we just pass it in directly.
If you want to know more about lambda functions, you can check out this question for more detail.
On an unrelated note, I notice that you don't use x anywhere in clearTextEdit. Is it necessary for clearTextEdit to take an argument in the first place?

Python 2.7 : Tkinter, How to use the bind method?

I'm trying to create a Scrabble game with Python. I'd like to display the points that the word worth when the user is typing the word.
I already asked this question as I didn't know what method to use. As I discovered which method to use, and my question is about how to use this method, I think this deserve a new question.
My problem is that I created a function called bind_entry(event) that is supposed to set a label every time the user type a letter. But the function bind_entry(event)doesn't know the label to set and the entry where the word is.
Here is my code :
#this the function creating the label
def create_variabletext_intlabel(root,col,row):
val=IntVar()
label=Label(root,textvariable=val)
label.grid(column=col,row=row)
return val, label
#this is the function creating the entry
def create_entry_string(root,width,col,row,columnspan,rowspan):
val=StringVar()
entry=ttk.Entry(root,width=width,textvariable=val)
entry.grid(column=col,row=row,columnspan=columnspan,rowspan=rowspan)
entry.bind("<Any-KeyPress>",bind_entry)
#Here is my problem, when I call the function bind_entry.
return val, entry
def bind_entry(event):
label.set(m.counting_point(char(event)))
# m.counting_point() is a function counting the word's points
# my problem is that the function doesn't know yet the label.
# I don't know how to call the label.
# I call the function create_entry_string in another file initiating
# all the widget for the GUI
val_entry_word, entry_word =g.create_entry_string(root,15,1,1,1,1)
# I call the function create_variabletext_intlabel in another file
# initiating all the widget for the GUI
val_points,label_points=g.create_variabletext_intlabel(root,1,2)
I just noticed that the function m.counting_points() will count only the letter that is typed by the user. Here I should call val_entry_word.
So here is my question :
As val_entry_word and val_points are created in a function in another file How could I call val_entry_word and val_points in the function bind_entry() ?
Generally, when you need different function calls to share information without passing it explicitly, the best practice is to use a class.
e.g.
class LabelUpdater(object):
def create_variabletext_intlabel(self,root,col,row):
val=IntVar()
self.label=label=Label(root,textvariable=val)
label.grid(column=col,row=row)
return val, label
#this is the function creating the entry
def create_entry_string(self,root,width,col,row,columnspan,rowspan):
val=StringVar()
entry=ttk.Entry(root,width=width,textvariable=val)
entry.grid(column=col,row=row,columnspan=columnspan,rowspan=rowspan)
entry.bind("<Any-KeyPress>",self.bind_entry)
#Here is my problem, when I call the function bind_entry.
return val, entry
def bind_entry(self,event):
self.label.set(m.counting_point(char(event)))
#At this point, I don't understand your code anymore since I don't know what g
#is or how it's create_entry_string method calls your create_entry_string function...
#I'll assume that the module where g's class is defined imports this file...
#If that's how it works, then the following may be ok, although probably not because
#of circular imports...
container=LabelUpdater()
create_variabletext_intlabel=container.create_variabletext_intlabel
create_entry_string=container.create_entry_string
val_entry_word, entry_word =g.create_entry_string(root,15,1,1,1,1) #somehow calls create_variabletext_intlabel which is mapped to container.create_variable_intlabel???
# I call the function create_variabletext_intlabel in another file
# initiating all the widget for the GUI
val_points,label_points=g.create_variabletext_intlabel(root,1,2)
Of course, you could also use globals...(though that is definitely discouraged)
Finally, an idiom that I often use to add additional information in a bind callback is to wrap the callback function in another function...
def myfunc(root):
label=Label(root,text="cow")
label.pack()
return label
#This is the callback we want...
# Q: but how do we pass S?
# A: we need to wrap this call in another -- a perfect use for lambda functions!
def change_label(label,S):
label.config(text=S)
root=Tk()
lbl=myfunc(root)
lbl.bind("<Enter>",lambda e: change_label("Horse"))
lbl.bind("<Leave>",lambda e: change_label("Cow"))

Categories

Resources