Python Tkinter, destroy toplevel after function
Asked Answered
H

2

3

I'm programming some drives with python using Tkinter as GUI. When my machine is running, I'd like to show the user a toplevel window with some information which should close itself after the function completes. This is my minimal example:

from Tkinter import *
import time


def button_1():
     window = Toplevel()
     window.title("info")
     msg = Message(window, text='running...', width=200)
     msg.pack()     
     time.sleep(5.0)
     window.destroy()

master = Tk()
frame = Frame(width=500,height=300)
frame.grid()
button_one = Button(frame, text ="Button 1", command = button_1)
button_one.grid(row = 0, column = 0, sticky = W + E)
mainloop()

The main problem is, that the toplevel window just appears after 5 seconds are over. Any suggestions? Thanks!

Heth answered 15/11, 2017 at 12:15 Comment(1)
I don't understand why do you think your code doesn't do what you want? It closes 'itself' after the function completes, or rather it completes the function as by closing.Englut
B
2

time.sleep(5) is launched before the GUI has time to update, that's why the toplevel only appears after the 5 seconds are over. To correct this, you can add window.update_idletasks() before time.sleep(5) to force the update the display.

But, as Bryan Oakley points out in his answer, the GUI is frozen while time.sleep(5) is executed. I guess that your ultimate goal is not to execute time.sleep but some time consuming operation. So, if you do not want to freeze the GUI but do not know how long the execution will take, you can execute your function in a separated thread and check regularly whether it is finished using after:

import Tkinter as tk
import time
import multiprocessing

def function():
    time.sleep(5)


def button_1():
    window = tk.Toplevel(master)
    window.title("info")
    msg = tk.Message(window, text='running...', width=200)
    msg.pack()
    thread = multiprocessing.Process(target=function)
    thread.start()
    window.after(1000, check_if_running, thread, window)


def check_if_running(thread, window):
    """Check every second if the function is finished."""
    if thread.is_alive():
        window.after(1000, check_if_running, thread, window)
    else:
        window.destroy()


master = tk.Tk()
frame = tk.Frame(width=500,height=300)
frame.grid()
button_one = tk.Button(frame, text ="Launch", command=button_1)
button_one.grid(row = 0, column = 0, sticky = "we")
master.mainloop()
Battlement answered 15/11, 2017 at 12:26 Comment(0)
M
1

A general rule of thumb is that you should never call sleep in the thread that the GUI is running in. The reason is that sleep does exactly what it says, it puts the whole program to sleep. That means that it is unable to refresh the window or react to any events.

If you want to do something after a period of time, the correct way to do that is with after. For example, this will destroy the window after five seconds:

window.after(5000, window.destroy)
Mild answered 15/11, 2017 at 15:14 Comment(1)
Thanks for the answer. This is just a minimal example, I don't call sleep in my real program. I call a much more complex function, which ends after no definite period of time.Heth

© 2022 - 2024 — McMap. All rights reserved.