Why will my button work, but not my key bind? - python

In this section of the code, I try to set the button's command to goRun, and also bind the Return key to goRun.
def goRun():
Run.runData(ENTRY=symbolEntry.get(), GREATER=greaterEntry.get(), BETWEEN=betweenEntry.get(), LESSER=lesserEntry.get())
#^ "Run" is a seperate class.
button = Button(app, text="Go Data", command=goRun, fg="blue")
button.pack(side="top", pady=2, padx=10)
app.bind('<Return>', goRun)
When I run the app, and press the Return key, I get this error:
TypeError: goRun() takes no arguments (1 given)
But when I press the button, it runs the command and works just fine.
What am I doing wrong?

Assuming the GUI library you are using is tkinter, this is because when a callback is activated by a keypress, a parameter called event is passed. Try modifying your function definition so it is like this:
def goRun(event=None):
So you are getting the error as your function received a parameter with which it doesn't have a clue what to do. With the event=None we are saying "Don't worry about that argument when it comes along, it's nothing."
You get this error with the Return key because it gives this argument, but it works with your Button as it does not send the argument.

Related

Understanding function call for tkinter button command

I am working through a tutorial on tkinter. I adapted some code, with the idea that every time you clicked on the button we'd create a new text label in the row below. What is confusing me is that when I write command=click it works, but when I write command = click() the function is executed a single time at the beginning of the script but not when the button is pressed. The first behaves as I'd expect. The second behaviour confuses me, as I would expect it to raise an error not perform the function once without pressing the button.
from tkinter import *
root = Tk()
position = {"Loc":1}
def click(position=position):
Label(root, text="ta").grid(row=position["Loc"], column=0)
position["Loc"] = position["Loc"] + 1
return
but = Button(root, text="click here to do nothing",command=click())
but.grid(row=0,column=0)
root.mainloop()
Note: I found the answer below more helpful than the answer to the related question here , because it made explicit that arguments are evaluated first before being passed into the function.
When you run a function in Python (like but = Button(root, text="click here to do nothing",command=click())), first all of the arguments get evaluated and then passed to the function. So, you are calling click() and then assigning its return value to the command. If it returns None or something else that Button can handle you will get no error. When you pass just click, you actually tell the function that it is supposed to call that function whenever you click the button.
Some simple code to clarify the issue:
def foo(bar):
print("\tCalling function {} from foo".format(bar))
if bar:
bar()
def fun():
print("\tExecuting fun function")
print("\nPassing function")
foo(fun)
print("\nPassing function return value")
foo(fun())
If you check out the outputs you will notice that they get executed in different order - in the second case, fun is executed first, returns None and than that None value is given to foo as a bar argument.
Passing function
Calling function <function fun at 0x000001A79F8863A0> from foo
Executing fun function
Passing function return value
Executing fun function
Calling function None from foo
As to why there is no error - why would there be?
Both are valid statements. You could have a function that returns other function and pass it to the button that way.

Difference between using or not lambda function in python tkinter button command

i have a question when using the Button widget in tkinter. I am new to this.
I noticed that when we use the command in the Button widget, sometimes we call a simple function just like that and sometimes we use lambda function and then we call it. What is the difference?
For example: tk.Button(window, text = "Click Me!", command = myfunction)
tk.Button(win,text="Result",command=lambda: result(en1.get())
Cant we just use it without lambda?
THank you.
Use of lambda:
The parentheses are the main reason that the function gets executed when given as command to a Button without lambda. If the function(which you are passing to the Button as a command) has no parameters(to be passed to itself), then you can simply pass it as a command avoiding the parentheses(). And hence you don't need to use lambda in this case. Like in this Example:command=func.
So using lambda is only necessary when the function has its own parameters(to be passed to itself).Like in this Example:command=lambda:func(a,b,c)
What lambda Does:
When you have to pass arguments to the function itself you have cannot avoid parentheses().
So in the case of buttons, lambda basically delays the execution of the function until the user clicks the button, by creating another function on the spot, which does not get called until the button is actually clicked. Hence the function does not get executed, where it is given as command to the Button.
Any Questions will be answered.

how to invoke button command with enter key

In tkinter, when a button has the focus, you can press the space bar to execute the command associated with that button. I'm trying to make pressing the Enter key do the same thing. I'm certain I've done this in the past, but I can't find the code, and what I'm doing now isn't working. I'm using python 3.6.1 on a Mac.
Here is what I've tried
self.startButton.bind('<Return>', self.startButton.invoke)
Pressing the Enter key has no effect, but pressing the space bar activates the command bound to self.startButton. I've tried binding to <KeyPress-KP_Enter> with the same result.
I also tried just binding to the command I want to execute:
self.startButton.bind('<Return>', self.start)
but the result was the same.
EDIT
Here is a little script that exhibits the behavior I'm talking about.
import tkinter as tk
root = tk.Tk()
def start():
print('started')
startButton.configure(state=tk.DISABLED)
clearButton.configure(state=tk.NORMAL)
def clear():
print('cleared')
clearButton.configure(state=tk.DISABLED)
startButton.configure(state=tk.NORMAL)
frame = tk.Frame(root)
startButton = tk.Button(frame, text = 'Start', command = start, state=tk.NORMAL)
clearButton = tk.Button(frame, text = 'Clear', command = clear, state = tk.DISABLED)
startButton.bind('<Return>', start)
startButton.pack()
clearButton.pack()
startButton.focus_set()
frame.pack()
root.mainloop()
In this case, it works when I press space bar and fails when I press Enter. I get an error message when I press Enter, saying that there an argument was passed, but none is required. When I change the definition of to take dummy argument, pressing Enter works, but pressing space bar fails, because of a missing argument.
I'm having trouble understanding how wizzwizz4's answer gets both to work. Also, I wasn't seeing the error message when I pressed Enter in my actual script, but that's way too long to post.
** EDIT AGAIN **
I was just overlooking the default value of None in Mike-SMT's script. That makes things plain.
Your use of self.startButton.bind('<Return>', self.start) should work fine as long as compensate for the event that the bind will send to the function/method.
Here is a simple example that will work with the enter key as long as the button has focus.
import tkinter as tk
root = tk.Tk()
def do_something(event=None):
print("did something!")
btn = tk.Button(root, text="Do something", command=do_something)
btn.pack()
btn.bind("<Return>", do_something)
#root.bind("<Return>", do_something) will work without the button having focus.
root.mainloop()
This only works when the button has keyboard focus. Also, an argument representing the event object is passed to the callable provided. The former doesn't seem to be the problem, so try:
self.startButton.bind('<Return>', lambda e: self.startButton.invoke())

Python Closing Toplevel Window Error

I wanted this code to create a popup error window that destroys itself after 4 seconds but can also be closed via a button.
def error(self):
top = Toplevel()
top.after(4000, lambda: top.destroy())
center_window(300,100, top)
top.title("Error")
Label(top, text="Please enter a valid code", height=3, width=200).pack()
ok = Button(top, text="OK", command=top.destroy)
ok.pack()
ok.bind("<Return>", lambda a: top.destroy())
ok.focus_set()
I have run the code and it works fine 90% of the time except sometimes it throws this error:
TypeError: <lambda>() takes exactly 1 argument (0 given)
I have done research that says it is Tkinters threading. I am not sure if this is my issue but when I take out this line of code:
top.after(4000, lambda: top.destroy())
It seems to work. If anyone could help me, I have taught myself what I know of python, so I am sure there are holes in my learning. I think I may need to somehow use the main thread of execution to destroy this window, or otherwise create my own custom widget. Any help is appreciated.
When using after or bind, you don't need to use lambda.
Instead, for example, use:
top.after(4000, top.destroy)
which references the function top.destroy directly.
You can directly bind the function to be called instead of using a lambda:
top.after(4000, top.destroy)
...
ok.bind("<Return>", top.destroy)
You would use a lambda, for instance, if you needed to pass arguments to the function; this is not the case here.

TypeError: <lambda>() takes no arguments (1 given)

I am a newbie to python programming and am still trying to figure out the use of lambda. Was worrking on some gui program after much googling i figured that i need to use this for buttons to work as i need it to
THIS WORKS
mtrf = Button(root, text = "OFF",state=DISABLED,command = lambda:b_clicked("mtrf"))
but when i do the same for Scale it does not work
leds = Scale(root,from_=0,to=255, orient=HORIZONTAL,state=DISABLED,variable =num,command =lambda:scale_changed('LED'))
Scale calls the function passed as command with one argument, so you have to use it (although throw it away immediately).
Change:
command=lambda: scale_changed('LED')
to
command=lambda x: scale_changed('LED')
This is presumably because the command is passed an argument that perhaps you don't want. Try changing the lambda from
command=lambda:scale_changed('LED')
to
command=lambda x:scale_changed('LED')
You should consult Tkinter documentation:
Scale widget
command - A procedure to be called every time the slider is moved. This procedure will be passed one argument, the new scale value. If the slider is moved rapidly, you may not get a callback for every possible position, but you'll certainly get a callback when it settles.
Button widget
command - Function or method to be called when the button is clicked.
Change your lambda to
command=lambda new_scale_val: scale_changed('LED')

Categories

Resources