如何利用yt-dlp API提供的下载进度更新PyGtk进度条?
解决PyGtk进度条不更新的问题
你的核心问题有两个:
- yt-dlp的
download()方法是阻塞式的,直接在主线程调用会卡住Gtk的UI循环,导致进度条无法刷新 - 额外的
ProgressUpdate线程没必要,反而增加了线程同步的复杂度
下面是修改后的代码,直接利用yt-dlp的进度钩子结合GLib.idle_add来安全更新UI,同时把下载操作放到子线程避免阻塞主线程:
修改后的window.py
from downloader.download import Downloader import gi import threading from db.database import Data gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib class Window(Gtk.Window): def __init__(self): super().__init__(title="Downloader") self.notebook = Gtk.Notebook() self.add(self.notebook) # Init connection with database self.data = Data() self.data.create_connection() # Display page of window self.display_download() self.display_history() self.link = '' # ... 保留你原来的其他代码 ... def _update_progress_bar(self, percent): """安全更新进度条的回调,必须在主线程执行""" self.progress_bar.set_fraction(percent) return False # callback function called when 'download' button is pressed def __on_button_clicked(self, widget): link = self.download_entry.get_text() if link != "": self.progress_bar.set_fraction(0) # 定义下载完成后的回调 def on_download_finished(): self.progress_bar.set_fraction(1.0) # 创建Downloader时传入进度更新回调和完成回调 downloader = Downloader( progress_callback=lambda p: GLib.idle_add(self._update_progress_bar, p), finish_callback=lambda: GLib.idle_add(on_download_finished) ) # 把下载操作放到子线程,避免阻塞主线程 download_thread = threading.Thread(target=downloader.download, args=(link,)) download_thread.start()
修改后的downloader.py
import yt_dlp as dl class Downloader: def __init__(self, progress_callback=None, finish_callback=None): self.status = '' self.percent = None self.progress_callback = progress_callback self.finish_callback = finish_callback # Clean percent value def __clean_percent(self, percent_str): try: # 直接提取数字部分,比遍历更可靠 percent = percent_str.split('%')[0].strip() return float(percent) / 100 except Exception as e: print(e) return 0.0 def my_hook(self, d): if d['status'] == 'downloading': self.percent = self.__clean_percent(d['_percent_str']) if self.progress_callback: self.progress_callback(self.percent) elif d['status'] == 'finished': self.status = 'finished' if self.finish_callback: self.finish_callback() def download(self, link): opts = { 'progress_hooks': [self.my_hook], } with dl.YoutubeDL(opts) as ytdl: ytdl.download([link]) if __name__ == '__main__': d = Downloader() d.download()
关键修改点说明
- 将下载移到子线程:用
threading.Thread把downloader.download()放到后台执行,主线程可以继续处理Gtk的UI事件 - 直接在hook里更新UI:利用yt-dlp的
progress_hooks实时获取进度,通过GLib.idle_add把UI更新任务提交到主线程执行(Gtk要求所有UI操作必须在主线程) - 简化进度解析:替换原来的遍历字符逻辑,直接分割字符串提取百分比,更可靠
- 移除多余的ProgressUpdate线程:不需要额外线程轮询进度,hook已经能实时触发更新
这样修改后,进度条就能实时反映下载进度,而且UI不会再被阻塞。
内容的提问来源于stack exchange,提问作者Nahuel Aguirre
相关产品推荐
相关产品推荐

