Home > OS >  Tkinter window is not responding despite using after() method
Tkinter window is not responding despite using after() method

Time:01-12

I'm implementing after() method to my code after finding out tkinter windows are not multithread-friendly. Back then I couldn't pop up my Toplevel with threading.Thread(), but seems like after() method is not working either. When you pressed the button, the Toplevel window (splash_screen) still gets laggy, you can't ever move nor resize it and it eventually freezes. I'm not sure why does it behave like this? Any ideas?

Thanks in advance!

import time
from tkinter import *
from PIL import Image, ImageTk


def splash_screen():
    global is_top_level_ready
    is_top_level_ready = False

    splash_screen = Toplevel(screen)

    frame = Frame(splash_screen, width=427, height=250)
    frame.place(x=0, y=0)

    label1 = Label(splash_screen, text='Please wait...')
    label1.place(x=122, y=120)

    image_a = ImageTk.PhotoImage(Image.open('dot_light.png'))
    image_b = ImageTk.PhotoImage(Image.open('dot_dark.png'))

    while True:
        if is_top_level_ready == False:
            l1 = Label(splash_screen, image=image_a).place(x=180, y=165)
            l2 = Label(splash_screen, image=image_b).place(x=215, y=165)
            l3 = Label(splash_screen, image=image_b).place(x=250, y=165)
            l4 = Label(splash_screen, image=image_b).place(x=285, y=165)
            splash_screen.update_idletasks()
            time.sleep(0.2)

            l1 = Label(splash_screen, image=image_b).place(x=180, y=165)
            l2 = Label(splash_screen, image=image_a).place(x=215, y=165)
            l3 = Label(splash_screen, image=image_b).place(x=250, y=165)
            l4 = Label(splash_screen, image=image_b).place(x=285, y=165)
            splash_screen.update_idletasks()
            time.sleep(0.2)

            l1 = Label(splash_screen, image=image_b).place(x=180, y=165)
            l2 = Label(splash_screen, image=image_b).place(x=215, y=165)
            l3 = Label(splash_screen, image=image_a).place(x=250, y=165)
            l4 = Label(splash_screen, image=image_b).place(x=285, y=165)
            splash_screen.update_idletasks()
            time.sleep(0.2)

            l1 = Label(splash_screen, image=image_b).place(x=180, y=165)
            l2 = Label(splash_screen, image=image_b).place(x=215, y=165)
            l3 = Label(splash_screen, image=image_b).place(x=250, y=165)
            l4 = Label(splash_screen, image=image_a).place(x=285, y=165)
            splash_screen.update_idletasks()
            time.sleep(0.2)

        else:
            splash_screen.destroy()
            break

    the_toplevel = Toplevel(screen)
    the_toplevel.minsize(width=650, height=415)

def checkloop():
    global is_splash_screen_ready
    if is_splash_screen_ready:
        splash_screen()
    else:
        screen.after(100, checkloop)

def splash_is_go():
    global is_splash_screen_ready
    is_splash_screen_ready = True


screen = Tk()

global is_splash_screen_ready
is_splash_screen_ready = False

button = Button(screen, text="Button", command=splash_is_go)
button.pack(pady=30, padx=30)

screen.after(100, checkloop)
screen.mainloop()

Image sources:

Dot Light Dot Dark

CodePudding user response:

Using .after() in this way does not make the while loop (and time.sleep()) non-blocking. You need to use .after() to replace the while loop and time.sleep():

import tkinter as tk
from PIL import ImageTk

def splash_screen():
    splash_win = tk.Toplevel(screen)

    frame = tk.Frame(splash_win)
    frame.pack(padx=100, pady=50)

    label1 = tk.Label(frame, text='Please wait...')
    label1.pack(pady=(0,10))

    image_light = ImageTk.PhotoImage(file='dot_light.png')
    image_dark = ImageTk.PhotoImage(file='dot_dark.png')

    dots = []
    for i in range(4):
        dots.append(tk.Label(frame, image=image_dark if i else image_light))
        dots[-1].pack(side="left")

    def update(i=0):
        dots[i].config(image=image_dark)
        i = (i   1) % 4
        dots[i].config(image=image_light)
        splash_win.after(200, update, i)

    splash_win.after(200, update) # start the update loop
    splash_win.grab_set() # make it like a modal window

screen = tk.Tk()
button = tk.Button(screen, text="Button", command=splash_screen)
button.pack(pady=30, padx=30)
screen.mainloop()

CodePudding user response:

To resolves your problem I put the popup in a only class started by a threading.Thread() and to avoid the _tkinter.TclError: bad window path name ".!toplevel" error I put the try: command in line 32.

The __init__ runs the popup and do the animation in a funtion in the class that is started in a thread. The edited code:

import threading # For threads
import time
from tkinter import *
from PIL import Image, ImageTk

class App: # The class 
    def __init__(self):
        global is_top_level_ready
        is_top_level_ready = False


        self.splash_screen = Toplevel(screen) # <---

        frame = Frame(self.splash_screen, width=427, height=250)
        frame.place(x=0, y=0)

        label1 = Label(self.splash_screen, text='Please wait...')
        label1.place(x=122, y=120)

        image_a = ImageTk.PhotoImage(Image.open('dot_light.png'))
        image_b = ImageTk.PhotoImage(Image.open('dot_dark.png'))

        App.splash_screen(self, image_a, image_b) # Runs animation



    def splash_screen(self, image_a, image_b):
        while True:
            if is_top_level_ready == False:
                try: # <---
                    l1 = Label(self.splash_screen, image=image_a).place(x=180, y=165)
                    l2 = Label(self.splash_screen, image=image_b).place(x=215, y=165)
                    l3 = Label(self.splash_screen, image=image_b).place(x=250, y=165)
                    l4 = Label(self.splash_screen, image=image_b).place(x=285, y=165)
                    self.splash_screen.update_idletasks()
                    time.sleep(0.2)

                    l1 = Label(self.splash_screen, image=image_b).place(x=180, y=165)
                    l2 = Label(self.splash_screen, image=image_a).place(x=215, y=165)
                    l3 = Label(self.splash_screen, image=image_b).place(x=250, y=165)
                    l4 = Label(self.splash_screen, image=image_b).place(x=285, y=165)
                    self.splash_screen.update_idletasks()
                    time.sleep(0.2)

                    l1 = Label(self.splash_screen, image=image_b).place(x=180, y=165)
                    l2 = Label(self.splash_screen, image=image_b).place(x=215, y=165)
                    l3 = Label(self.splash_screen, image=image_a).place(x=250, y=165)
                    l4 = Label(self.splash_screen, image=image_b).place(x=285, y=165)
                    self.splash_screen.update_idletasks()
                    time.sleep(0.2)

                    l1 = Label(self.splash_screen, image=image_b).place(x=180, y=165)
                    l2 = Label(self.splash_screen, image=image_b).place(x=215, y=165)
                    l3 = Label(self.splash_screen, image=image_b).place(x=250, y=165)
                    l4 = Label(self.splash_screen, image=image_a).place(x=285, y=165)

                    self.splash_screen.update_idletasks()
                    time.sleep(0.2)
                except: # <---
                    break
            else:
                self.splash_screen.destroy()
                break

def checkloop():
    global is_splash_screen_ready
    if is_splash_screen_ready:
        run = App # <----
        threading.Thread(target=run).start() # Runs the popup
    else:
        screen.after(100, checkloop)

def splash_is_go():
    global is_splash_screen_ready
    is_splash_screen_ready = True


screen = Tk()

global is_splash_screen_ready
is_splash_screen_ready = False

button = Button(screen, text="Button", command=splash_is_go)
button.pack(pady=30, padx=30)

screen.after(100, checkloop)
screen.mainloop()
  • Related