Tkinter从网络拉取数据时加载动画/进度条不生效如何解决
问题根源
Tkinter 为单线程事件驱动模型,你当前将网络请求这类阻塞IO操作和UI控制逻辑都放在主线程的get()方法中执行,网络请求过程会直接卡住Tkinter的主事件循环,进度条的刷新渲染逻辑无法得到调度,直到所有数据拉取完成后才会一次性更新UI,因此看不到加载动画的运行效果。
修复方案
将耗时的网络请求逻辑拆分到独立子线程运行,避免阻塞主线程的UI渲染,网络请求完成后通过Tkinter原生的after()方法通知主线程执行进度条停止、海报渲染的UI操作(Tkinter所有UI相关操作都不允许在子线程直接调用)。
修改后完整代码
from tkinter import * from tkinter import ttk import io import threading from PIL import Image, ImageTk import requests from urllib.request import urlopen class Root(Tk): def __init__(self): super().__init__() # 窗口初始化 self.geometry("720x1000") self.canvas = Canvas(width=720, height=1000) self.canvas.place(relx=0, rely=0) self.button = Button(self, text="Get The Posters", command=self.get) self.button.pack() self.movie_list = ["Tenet", "Memento", "Inception"] self.poster_list = [] self.x = 0 def fetch_posters(self): # 子线程执行耗时的网络请求逻辑 for i in self.movie_list: url = "http://www.omdbapi.com/?apikey=73a4d84d&t={}".format(i) response = requests.get(url) data = response.json() pic_url = data["Poster"] my_page = urlopen(pic_url) my_picture = io.BytesIO(my_page.read()) pil_img = Image.open(my_picture).resize((200,296)) tk_img = ImageTk.PhotoImage(pil_img) self.poster_list.append(tk_img) # 请求完成后通知主线程更新UI self.after(0, self.show_posters) def show_posters(self): # 主线程执行UI更新逻辑 self.pb.stop() self.label.destroy() self.pb.destroy() # 渲染海报 x = 0 for n in range(len(self.poster_list)): x += 30 if n == 1 or n == 2: x += 200 self.canvas.create_image(x, 100, image=self.poster_list[n], anchor=NW) def get(self): # 主线程处理UI初始化 self.button.destroy() self.label = Label(text="loading") self.label.place(relx=0.08, rely=0.65) self.pb = ttk.Progressbar(orient='horizontal', mode='indeterminate', length=480) self.pb.place(relx=0.08, rely=0.7) self.pb.start() # 启动子线程拉取数据 threading.Thread(target=self.fetch_posters, daemon=True).start() root = Root() root.mainloop()
内容的提问来源于stack exchange,提问作者newfile.py
相关产品推荐
相关产品推荐

