使用Thread后仍无法实时更新CustomTkinter中Label的问题求助
解决CustomTkinter实时更新下载进度不生效的问题
问题场景
尝试通过self.error_label实时显示YouTube视频的下载进度,虽然使用了Thread线程开启下载任务,但只有下载完成时才会显示“Download 100%”,中间的20%、48%等进度完全不更新。相关代码如下:
import customtkinter from pytube import YouTube import re from threading import Thread class App(customtkinter.CTk): def __init__(self): super().__init__() self._set_appearance_mode("System") self.geometry("720x480") self.title('PyTube') # Progress bar self.progressbar = customtkinter.CTkProgressBar(self, width=500) self.progressbar.set(0) self.progressbar.pack() #Error label self.error_label = customtkinter.CTkLabel(self, text='Welcome!') self.error_label.pack() #User input box ................ def progress_callback(self, stream, chunk, bytes_remaining): total_size = stream.filesize bytes_downloaded = total_size - bytes_remaining percentage = bytes_downloaded / total_size * 100 self.error_label.configure(text=f'Download {percentage}%') def youtube_video_download(self, link, res): yt = YouTube(link, on_progress_callback=self.progress_callback) vid = yt.streams.filter(progressive=True, resolution=res).first() vid.download() def Download(self): if self.link_validate(self.user_input.get()): if self.tabview.get() == 'Video': if self.video_quality.get() != '': Thread(target=self.youtube_video_download, args=(self.user_input.get(), self.video_quality.get(), )).start() else: self.error('Video quality is not selected!', 'red') if __name__=="__main__": app = App() app.mainloop()
问题原因
Pytube的on_progress_callback回调函数是在子线程中执行的,而CustomTkinter(以及所有Tkinter衍生库)的UI组件只能在主线程中更新。直接在子线程里调用self.error_label.configure()会导致UI更新被阻塞,只有当子线程任务完成后,所有积压的UI更新才会一次性执行,所以看不到中间进度。
解决方案
使用CustomTkinter的self.after()方法,将UI更新的任务提交到主线程的事件循环中执行。修改后的progress_callback方法如下:
def progress_callback(self, stream, chunk, bytes_remaining): total_size = stream.filesize bytes_downloaded = total_size - bytes_remaining percentage = bytes_downloaded / total_size * 100 # 用after将UI更新操作提交到主线程 self.after(0, lambda: self.error_label.configure(text=f'Download {round(percentage, 1)}%'))
如果需要同时更新进度条,也可以把进度条的更新逻辑放进after的lambda里:
def progress_callback(self, stream, chunk, bytes_remaining): total_size = stream.filesize bytes_downloaded = total_size - bytes_remaining percentage = bytes_downloaded / total_size * 100 # 同时更新标签文本和进度条进度 self.after(0, lambda: ( self.error_label.configure(text=f'Download {round(percentage, 1)}%'), self.progressbar.set(percentage / 100) ))
self.after(0, func)的作用是让主线程在当前事件循环的空闲时间立即执行func,确保所有UI操作都在主线程完成,这样就能实时显示下载进度了。
内容的提问来源于stack exchange,提问作者Rohit Dalal
相关产品推荐
相关产品推荐

