You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter开发YouTube下载器填充Combobox时回调异常如何修复

Tkinter版YouTube下载器Combobox填充异常修复

问题现象

开发YouTube下载器时,输入视频链接后尝试把视频画质、音频音质信息填充到ttk.Combobox组件,程序抛出Exception in Tkinter callback异常。
异常运行截图
原始问题代码如下:

import tkinter
import tkinter.ttk
from tkinter import *
from tkinter import filedialog
from os import path
import pafy
from pafy import *

class Youtube():
    def __init__(self):
        self.label()
        self.entry()
        self.button()
        self.combobox()
        self.data=[]

    def label(self):
        Label(window, text='URL', font='bold').grid(row=0, column=0, padx=100)
        Label(window, text='Destination', font='bold').grid(row=2, column=0, padx=100)
        Label(window, text='File Name', font='bold').grid(row=4, column=0, padx=100)
        Label(window, text='Video Quality', font='bold').grid(row=6, column=0, pady=20)
        Label(window, text='Audio Quality', font='bold').grid(row=7, column=0, pady=5)
        Label(window, text='Download Type', font='bold').grid(row=8, column=0, pady=15)
        Label(window, text='Combine AV', font='bold').grid(row=8, column=1)

    def entry(self):
        global een
        global en
        en=Entry(window, width=35, borderwidth=10)
        en.grid(row=1, column=0, padx=100, pady=5)
        een=Entry(window, width=35, borderwidth=10)
        een.grid(row=3, column=0, padx=100, pady=5)
        Entry(window, width=35, borderwidth=10).grid(row=5, column=0, padx=100, pady=5)

    def button(self):
        Button(window, text='i', height=1, width=3, command=self.information).grid(row=1, column=1)
        Button(window, text='d', height=1, width=3, command=self.destination).grid(row=3, column=1)
        Button(window, text='Download', font='bold').grid(row=12, column=0, padx=150, pady=10, columnspan=3)

    def combobox(self):
        tkinter.ttk.Combobox(window,  width=15).grid(row=6, column=1)
        tkinter.ttk.Combobox(window,  width=15).grid(row=7, column=1, pady=5)
        tkinter.ttk.Combobox(window, values=['Audio only', 'Vido only', 'Audio and vido'],
                             width=15).grid(row=9, column=0)
        tkinter.ttk.Combobox(window, values=['Yes', 'No'], width=15).grid(row=9, column=1, pady=20)
        tkinter.ttk.Progressbar(window, orient=HORIZONTAL, length=300,
                                mode='determinate').grid(row=11, column=0, padx=200, pady=10, columnspan=3)

    def destination(self):
        path = str(filedialog.askdirectory())
        een.insert(tkinter.END, path)

    def information(self):
       global link
       link = en.get()
       self.video()
       self.audio()

    def video(self):
        video_quality_list = []
        self.data = pafy.new(link)
        video_streams = self.data.videostreams
        for video_quality in video_streams:
            video_quality_list.append(video_quality)
        tkinter.ttk.Combobox(window, values=video_quality_list, width=15).grid(row=6, column=1, pady=5)

    def audio(self):
        audio_quality_list = []
        self.data = pafy.new(link)
        audio_streams = self.data.audiostreams
        for audio_quality in audio_streams:
            audio_quality_list.append(audio_quality)
        tkinter.ttk.Combobox(window, values=audio_quality_list, width=15).grid(row=7, column=1)

window = Tk()
mywin = Youtube()
window.title('Youtube Downloader')
window.geometry("700x600+10+10")
window.mainloop()

报错原因

  • 核心问题:直接把pafy返回的Stream类实例传入Combobox的values参数,ttk.Combobox仅支持字符串类型的选项值,渲染非字符串对象时会直接触发Tkinter回调异常。
  • 逻辑问题:初始化界面时已经创建了对应位置的画质、音质选择下拉框,获取流信息后又重复创建同位置的Combobox,会引发组件布局冲突、引用丢失问题。
  • 冗余请求:video()和audio()方法各自调用一次pafy.new(link)拉取视频信息,重复发起网络请求,既拖慢速度也容易触发接口访问限制。
  • 细节问题:存在全局变量滥用、选项文本拼写错误(Vido应为Video)的问题。

修复方案

  1. 初始化时将画质、音质选择的Combobox存为类属性,后续更新选项时直接操作已有组件,不重复创建。
  2. 把pafy返回的流对象转换为用户可读的字符串(包含分辨率/码率、格式、编码信息)再传入Combobox。
  3. 仅调用一次pafy.new()拉取视频信息,拿到结果后同时解析视频、音频流列表。
  4. 移除不必要的全局变量,修正选项文本拼写错误。

修复后可运行代码

import tkinter
import tkinter.ttk
from tkinter import *
from tkinter import filedialog
import pafy

class Youtube():
    def __init__(self, root):
        self.window = root
        self.data = None
        self.en = None
        self.een = None
        self.video_combobox = None
        self.audio_combobox = None
        self.label()
        self.entry()
        self.button()
        self.combobox()

    def label(self):
        Label(self.window, text='URL', font='bold').grid(row=0, column=0, padx=100)
        Label(self.window, text='Destination', font='bold').grid(row=2, column=0, padx=100)
        Label(self.window, text='File Name', font='bold').grid(row=4, column=0, padx=100)
        Label(self.window, text='Video Quality', font='bold').grid(row=6, column=0, pady=20)
        Label(self.window, text='Audio Quality', font='bold').grid(row=7, column=0, pady=5)
        Label(self.window, text='Download Type', font='bold').grid(row=8, column=0, pady=15)
        Label(self.window, text='Combine AV', font='bold').grid(row=8, column=1)

    def entry(self):
        self.en = Entry(self.window, width=35, borderwidth=10)
        self.en.grid(row=1, column=0, padx=100, pady=5)
        self.een = Entry(self.window, width=35, borderwidth=10)
        self.een.grid(row=3, column=0, padx=100, pady=5)
        Entry(self.window, width=35, borderwidth=10).grid(row=5, column=0, padx=100, pady=5)

    def button(self):
        Button(self.window, text='i', height=1, width=3, command=self.information).grid(row=1, column=1)
        Button(self.window, text='d', height=1, width=3, command=self.destination).grid(row=3, column=1)
        Button(self.window, text='Download', font='bold').grid(row=12, column=0, padx=150, pady=10, columnspan=3)

    def combobox(self):
        self.video_combobox = tkinter.ttk.Combobox(self.window, width=25)
        self.video_combobox.grid(row=6, column=1)
        self.audio_combobox = tkinter.ttk.Combobox(self.window, width=25)
        self.audio_combobox.grid(row=7, column=1, pady=5)
        tkinter.ttk.Combobox(self.window, values=['Audio only', 'Video only', 'Audio and video'],
                             width=15).grid(row=9, column=0)
        tkinter.ttk.Combobox(self.window, values=['Yes', 'No'], width=15).grid(row=9, column=1, pady=20)
        tkinter.ttk.Progressbar(self.window, orient=HORIZONTAL, length=300,
                                mode='determinate').grid(row=11, column=0, padx=200, pady=10, columnspan=3)

    def destination(self):
        select_path = str(filedialog.askdirectory())
        self.een.insert(tkinter.END, select_path)

    def information(self):
        link = self.en.get().strip()
        if not link:
            return
        self.data = pafy.new(link)
        self.load_video_streams()
        self.load_audio_streams()

    def load_video_streams(self):
        video_quality_list = []
        video_streams = self.data.videostreams
        for stream in video_streams:
            quality_str = f"{stream.resolution} | {stream.extension} | {stream.vcodec[:4]}"
            video_quality_list.append(quality_str)
        self.video_combobox['values'] = video_quality_list
        if video_quality_list:
            self.video_combobox.current(0)

    def load_audio_streams(self):
        audio_quality_list = []
        audio_streams = self.data.audiostreams
        for stream in audio_streams:
            quality_str = f"{stream.bitrate} | {stream.extension} | {stream.acodec[:4]}"
            audio_quality_list.append(quality_str)
        self.audio_combobox['values'] = audio_quality_list
        if audio_quality_list:
            self.audio_combobox.current(0)

if __name__ == "__main__":
    window = Tk()
    mywin = Youtube(window)
    window.title('Youtube Downloader')
    window.geometry("700x600+10+10")
    window.mainloop()

补充:如果运行时遇到pafy拉取视频信息报错,是因为pafy自带的解析逻辑适配旧版YouTube接口,安装yt-dlp后配置pafy后端为yt-dlp即可解决。

内容的提问来源于stack exchange,提问作者Mahshid Saedi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:09:21