Python Closing Toplevel Window Error - python

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.

Related

Python tkinter, labels, and function order

This seems so simple but I can't figure out what I need to do to remedy. I have a tkinter project and on a button press, a function runs that takes several seconds. I want a "loading..." type message while the function is running so it's obvious it's actually working and not crashed. I figured a label would be easy enough and on the first line of the function, have label1.set('loading') but I suppose because of the way functions work, the label doesn't set until the function is done running--which is not helpful.
I made a second short function
def update_status(message):
label1.set(message)
and for the button in tkinter, used command=lambda:[update_status('loading'),search()] in hopes that the update_status() function would run first, alter the label, and then the second search() function that takes upwards of 30 seconds would run. But I get the same effect.
What's the simplest way finish running the update_status() function--thereby updating my label acting as the "status", and THEN run the time consuming search() function?
I'm not opposed to something more complicated like a loading window or something similar, but just wanted something simple (I have not even googled any type of loading window--I'm mostly hung up on how to get 2 functions to run on a button click in a sequential order).
Hey I do not think you need 2 functions to do what you want. You simply have to update your root so that the label is directly updated.
Here is an example:
import tkinter as tk
import time
def update_status(message1, message2):
var.set(message1)
root.update()
time.sleep(5)
var.set(message2)
if __name__ == '__main__':
root = tk.Tk()
root.title("Wait for function")
var = tk.StringVar()
var.set('Waiting for input')
label1 = tk.Label(root, textvariable=var)
label1.pack()
Button1 = tk.Button(root, text="Wait", command=lambda:update_status('loading', 'done'))
Button1.pack()
root.mainloop()

Problem with binding 2 functions to a button in tkinter

First of all this is my code:
button_1 = Button(
image=button_image_1,
borderwidth=0,
highlightthickness=0,
command=lambda:[get_to_main_when_clicked(),delete_red_border()],
relief="flat"
)
As you can see I binded 2 functions to this button. To the first one: If a specific condition is true, then the function lets an image appear. The second one then should wait 3 seconds and should delete the appeared image. The only really weird problem is, that it no matter what I do, first executes the delete_red_border() function. It waits 3 seconds, then its trying to delete an image that couldn't be defined and globalized, because the get_to_main_when_clicked() function wasn't executed. How can I solve this?
PS: The specific condition is true.
Don't do this. Create a function specifically for this button. A function is much easier to understand and debug than a lambda, especially a complex lambda. You can then put any logic you want inside the function.
def do_something():
get_to_main_when_clicked()
delete_red_border()
button_1 = Button(..., command=do_something)
I found the solution for it. The problem was, that it didn't refreshed/updated the window after finishing the first function. That was solved by one line of code:
window.update()

I made a button that opens Google. I have fixed the problem but am wondering why it worked

I am making a small program (first actually) with Tkinter. This program is supposed to have the logos for websites and when pressed opens the website.
google_url = 'https://www.google.com'
def OpenUrl():
webbrowser.open_new(google_url)
button1 = tk.Button(root, command=OpenUrl)
This code functions (after packing/importing everything)
Now this was the code before:
button1 = tk.Button(root, command=webbrowser.open_new('https://www.google.com'))
I am new to Python so I really want to understand why this change worked. Was it Python interpreter that did something? Now, this isn't all of the code that I wrote so if you think I should show that then ask me but this was the difference that allowed the code to work. (Open Google when the button was pressed. Before, when the code was ran, it opened google but didn't open tkinter)
Hooray for your first project!
Your first attempt didn't work because the command arg must be function. webbrowser.open_new('https://www.google.com') is a function that has already been called and thusly evaluated. It now equals open_new()s return value (which is nothing).
The contents of OpenUrl are not evaluated until the buttons is clicked, as a function only runs when called. A lambda (an unnamed, in-line function) would work as well:
button1 = tk.Button(root, command= lambda: webbrowser.open_new('https://www.google.com'))
Happy coding!

Tkinter bind not working when passed root.destroy directly

I'm fairly new to python and tkinter. I'm working with python 2.7 and tkinter 8.5.
I'm trying to exit my app when the escape key is pressed, and I ran into some odd behaviour:
When I pass root.destoy as the argument to bind(), the app does nothing:
root = Tk()
...
root.bind('<Escape>', root.destroy)
But if I define a function that calls root.destroy() and pass that as an argument to bind, everything works as expected:
def exit_app():
root.destroy()
root.bind('<Escape>', exit_app)
It also works if I pass a lambda like this:
root.bind('<Escape>', lambda f: root.destroy())
Can anyone explain what's happening here?
Thanks
When you bind a command to an event, that command is passed an argument which is is an object that represents the event. root.destroy does not accept any arguments, therefore it is throwing an error instead of running.
That is why your lambda works: your lambda accepts an argument (oddly, named f).
You claim in your question that it works with this exact function definition:
def exit_app():
root.destroy()
I find that impossible to believe, for the same reason described above.

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