加载时使用 tkinter 在 python 中播放动画 GIF

Play an animated GIF in python with tkinter while loading

我正在尝试播放 GIF 并让它在加载进度条时循环播放 4 秒。我尝试了很多解决方案组合都无济于事。下面是我得到的最接近的。 loadingAnimation() 函数可以很好地运行进度条,但我无法将 gif 部分放在一起(M_95())。理想情况下,希望 GIF 在进度条加载的同时在屏幕中间播放,然后在完成后关闭 GIF window。

import threading
import tkinter

root = tkinter.Tk()
frames = [PhotoImage(file='./myScripts/M-95.gif',format = 'gif -index %i' %(i)) for i in range(10)]

def M_95() :
    # Play GIF (file name = m95.gif) in a 320x320 tkinter window 
    # Play GIF concurrently with the loading animation below
    # Close tkinter window after play 
  
def loadingAnimation(process):
    while process.is_alive() :
        animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
        for i in range(len(animation)):
            sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
            sys.stdout.flush()
            time.sleep(0.4)

loading_process = threading.Thread(target = M_95)
loading_process.start()

loadingAnimation(loading_process) 
loading_process.join()

不建议先在tkinter应用程序的主线程中使用while循环和time.sleep(),因为它会阻塞tkinter mainloop(),然后导致GUI无响应。

建议:

  • 使用 .after() 显示动画 GIF,因为在线程中更新 tkinter 小部件是不安全的
  • 改用 loadingAnimation() 上的线程
import threading
import tkinter
import sys
import time

root = tkinter.Tk()
frames = [tkinter.PhotoImage(file='./myScripts/M-95.gif', format='gif -index %i'%(i)) for i in range(10)]

def center_window(win):
    win.wait_visibility() # make sure the window is ready
    x = (win.winfo_screenwidth() - win.winfo_width()) // 2
    y = (win.winfo_screenheight() - win.winfo_height()) // 2
    win.geometry(f'+{x}+{y}')

def M_95(n=0, top=None, lbl=None):
    # Play GIF (file name = m95.gif) in a 320x320 tkinter window
    # Play GIF concurrently with the loading animation below
    # Close tkinter window after play
    global process_is_alive  # used in loadingAnimation()
    delay = 4000 // len(frames) # make one cycle of animation around 4 secs
    if n == 0:
        root.withdraw()
        top = tkinter.Toplevel()
        lbl = tkinter.Label(top, image=frames[0])
        lbl.pack()
        center_window(top)
        process_is_alive = True
    if n < len(frames)-1:
        lbl.config(image=frames[n])
        lbl.after(delay, M_95, n+1, top, lbl)
    else:
        top.destroy()
        root.deiconify()
        process_is_alive = False


def loadingAnimation():
    animation = ["[■□□□□□□□□□]","[■■□□□□□□□□]", "[■■■□□□□□□□]", "[■■■■□□□□□□]", "[■■■■■□□□□□]", "[■■■■■■□□□□]", "[■■■■■■■□□□]", "[■■■■■■■■□□]", "[■■■■■■■■■□]", "[■■■■■■■■■■]"]
    i = 0
    while process_is_alive:
        sys.stdout.write("\r | Loading..." + animation[i % len(animation)])
        sys.stdout.flush()
        time.sleep(0.4)
        i += 1

M_95() # start GIF animation
threading.Thread(target=loadingAnimation).start()

root.mainloop()

更新:在大约 4 秒内为 GIF 制作一个以上的循环:

def M_95(n=0, top=None, lbl=None):
    # Play GIF (file name = m95.gif) in a 320x320 tkinter window
    # Play GIF concurrently with the loading animation below
    # Close tkinter window after play
    global process_is_alive
    num_cycles = 2
    count = len(frames) * num_cycles
    delay = 4000 // count # make required cycles of animation in around 4 secs
    if n == 0:
        root.withdraw()
        top = tkinter.Toplevel()
        lbl = tkinter.Label(top, image=frames[0])
        lbl.pack()
        center_window(top)
        process_is_alive = True
        lbl.after(delay, M_95, n+1, top, lbl)
    elif n < count-1:
        lbl.config(image=frames[n%len(frames)])
        lbl.after(delay, M_95, n+1, top, lbl)
    else:
        top.destroy()
        root.destroy()
        process_is_alive = False