In a class, in a function I am creating a Tkinter Canvas. This function is being called by another class, I would like for the Tkinter window to pop up for 30 seconds and then close itself. I have it call
master.mainloop()
time.sleep(30)
master.destroy()
But I get an error
"elf.tk.call('destroy', self._w)
_tkinter.TclError: can't invoke "destroy" command: application has been destroyed"
So how can I have it close itself?
Don't use time.sleep() with tkinter. Instead, call the function after on the widget you want to close.
Here it is the most simple example:
import tkinter as tk
w = tk.Tk()
w.after(30000, lambda: w.destroy()) # Destroy the widget after 30 seconds
w.mainloop()
The problem here is that mainloop() does not return until the GUI has shut down.
So, 30 seconds after the GUI has shut down and destroyed itself, you try to destroy it. And obviously that fails.
But you can't just move the sleep and destroy calls inside the main loop, because if you sleep in the middle of the main loop, the GUI will freeze up.
So, you need some kind of timer that will not stop the main loop. tkinter includes the after method for exactly that purpose. This answer gives a detailed example of using it.
Related
My tkinter application has a function which has a time.sleep() function in it so anytime I run the function that has the time.sleep() function, the whole application freezes until seconds specified in the time.sleep() function has elapsed. This can be a very bad User experience and I need to fix this.
The function that has the time.sleep() function is a call back function from a button so I tried doing something like this:
from tkinter import *
import threading
root = Tk()
schedule_msg_btn = Button(root, text="Schedule Msg", command=lambda : threading.Thread(target=schedule_msg_func).start())
root.mainloop()
It seems to be doing absolutely nothing.
The code i tried to accomplish my result:
from tkinter import*
import time
root=Tk()
lyrics=StringVar()
lyrics.set("unleash")
l=Label(root,textvariable=lyrics)
l.pack()
def change_text():
lyrics.set("you!!")
print('ses')
time.sleep(6.9)
change_text()
mainloop()
The problems in this are like the window is displayed after 10 sec. If I shift the mainloop() before the time.sleep() function the time.sleep() executes after the app is closed. The reason is simple that I didn't use after() function because the after() function doesn't accepts the sec value in decimal.
Any help is appreciated
The after() function takes the delay argument in milliseconds, where you would pass 6900 instead of 6.9. See: https://www.tcl.tk/man/tcl8.4/TclCmd/after.html
The problem is that mainloop() blocks further execution. You can always use a background thread:
def bg_thread():
time.sleep(6.9)
change_text()
thread = threading.Thread(target=bg_thread)
thread.start()
mainloop()
Threading, of course, brings with it a whole host of challenges. It's a good idea to make sure you know what you're doing when working with threads.
See also:
https://docs.python.org/3/library/threading.html#threading.Thread
Tkinter understanding mainloop
When do I need to call mainloop in a Tkinter application?
import time
def update_label():
///update label text here
label.config(text="New label text")
/// wait for 5 seconds before updating the label
time.sleep(5)
update_label()
I would like to refresh a python Tkinter window every 1 seconds :
animation=TFS(test) #animation is a list, each item in the list is a table with drawing input information.
for item in animation:
Display(item) #display function is creating a Tkinter window with a mainloop
time.sleep(1)
The only solution about closing a mainloop for Tkinter that i found on the internet, is by using .quit() or .destroy() in a button.
But I don't want the user to close the window manually, the window should be closed automatically every x second in order to be able to display a new window with updated information.
Note : the display is made of about 2 hundreds rectangles and labels.
The .destroy() method can be called anywhere in the code, not necessarily when button is pressed. To close the window, it has to be called on Tk object (main window, the first one you created). But, like #Billal BEGUERADJ said, you don't need to close the window to refresh widgets inside it. Just call .config() method on the widget you'd like to modify and pass options you'd like to change as arguments.
The display in the canva is procedural with hundreds of shapes, so the .configure seems hard to used.
So i deleted the .mainloop() in the function Display and replaced it by :
TK.update()
time.sleep(RefreshTime)
TK.destroy()
return
and changed the main code to the following :
animation=TFS(test)
for step in animation:
Display.display(step,RefreshTime)
I have a class Duplicates that checks for duplicates within 40 words.
I have a class Window that creates and runs the main window where i post the result.
I have a class popWindow that creates a Toplevel window when asking user for what to do with a possible double.
My problem is closing the popWindow once a choice is submited.
the version I have that actualy runs and posts an aswer (the text with marked duplicates) uses quit to terminate the window (meaning the popup is still there in the way) or to simply have multiple popups till you are done.
class Duplicates:
def markWord(self):
self.appendMarkedWord(self.word)
self.checked.append(self.word)
self.pop.topLevel_exit()
return ""
class popUpWindow:
temp = Button( self, font = 8,
text = "Allowed this run only",
command = app.newFile.markWord
)
temp.place( x = 178,
y = 55
)
if I instead use .destroy() the window shuts but the program stops running and that is worse.
How do i work around this so it shuts the window but still continues to run the program?
Ok, after many many hours it seemed the real problem was destroy() was not stopping my popUpWindow.mainloop() so I now have altered my exit code to first do quit() and then do destroy(). This is not what i have seen as examples at all and it seems to me that destroy() on toplevel mainloop is not terminating it (destroy() works fine on my root.mainloop).
def topLevel_exit(self):
self.pop.quit()
self.pop.destroy()
If you call destroy() on a toplevel window, it will not stop the application from running. If your application stops, there must be more to your code that what you're telling us. Without question, the right way to get rid of the popup is to call destroy on the instance of the Toplevel.
A way to hide the window and keep the program running would be to use .withdraw() on the window, and .reiconify() to get it back (if needed). Or you could use .destroy() on a Toplevel window. If you need examples just ask, hope this helps you.
The solution for me was:
def topLevel_exit(self):
self.top.quit()
self.top.destroy()
I do not know if this is common praxis but is what I had to do since destroy was not stoping my top.mainloop()
If you use a topLevel window, self.pop.destroy() should still work as you are using mainloop()
Otherwise use quit() or both but in my opinion of all of these, I prefer destroy()
I'm really lost...I open a window with two buttons, and when you click on the button called "REGISTER SOME KEY PRESSES" it runs the script called registerSomeKeyPresses.py, BUUUUT once finished I want to close that execution but keep the first window displaying...it's being impossible for me....
Please, i would reaaaally appreciate any help...
Thanks!
#!/usr/bin/env python
from Tkinter import *
import threading
v0 = Tk()
def finishApplication(): v0.destroy()
def registerSomeKeyPresses():
t = threading.Thread(target=execfile("registerSomeKeyPresses.py"))
t.start()
def waitAndRun(f): v0.after(200, f)
b1=Button(v0,text="TERMINAR APLICACION",command=lambda: finishApplication()).pack()
button_keyPresses=Button(v0,text="REGISTER SOME KEY PRESSES",command=lambda: waitAndRun(registerSomeKeyPresses())).pack()
v0.mainloop()
================ registerSomeKeyPresses.py ===========================
Do several things and last command:
io.quit()
When you destroy the instance of Tk, your programm will (and should) exit. If you want to create and destroy windows, create and destroy an instance of Toplevel while keeping the main window active. If you don't want to see the main window you can hide it.
Also, tkinter and threads don't mix very well. You cannot call any methods on any widgets from another thread. I've heard other people say you can call event_generate from another thread, but I think that's the only tkinter function you can call from another thread.
Edit 1
A second try as a response to your comment:
from Tkinter import *
from subprocess import call
import sys
t = Tk()
def click():
t.iconify()
try:
call([sys.executable, 'script.py'])
finally:
t.deiconify() # if it should close do t.quit() and t.destroy()
b = Button(t, command= click)
b.pack()
t.mainloop()
Old Version
What does that do?
================ registerSomeKeyPresses.py ===========================
v0.quit()
v0.destroy()
io.mainloop()
An other error is:
threading.Thread(target=execfile, args = ("registerSomeKeyPresses.py",))
if you really neeed a thread.
Do never mix tkinter mainloop things with threads. Threads can use event_generate - thats safe.