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:
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()