下载文件中的进度条(tkinter)出现问题

Trouble with progressbar(tkinter) in a download file

我从 python 开始为我的妻子开发一个 YouTube video/audio 下载器。它已经可以使用了,但我想通过放置一个可以确定或不确定的下载进度条来改善它的外观。我已经搜索了几个地方,甚至翻过 Whosebug,但我找不到适合自己的解决方案。也许我的代码有点乱,但我正在努力。

因为我做了两个单独的程序,所以我将 post 只做视频下载器。音频包含更多行,只是从 link 更改我想要的文件类型并将其转换为 mp3,因为 tkinter 下载仅 mp4 音频。那我就把两者合二为一

from pytube import YouTube
from tkinter import *
from threading import Thread


def threading():
    t1 = Thread(target=downloader)
    t1.start()


def threading2():
    t1 = Thread(target=downloader2)
    t1.start()


def threading3():
    t1 = Thread(target=downloader3)
    t1.start()


def threading4():
    t1 = Thread(target=downloader4)
    t1.start()


def threading5():
    t1 = Thread(target=downloader5)
    t1.start()




def downloader():
    url = YouTube(str(inp.get()))
    video = url.streams.get_highest_resolution()
    video.download()
    Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)


def downloader2():
    url = YouTube(str(inp2.get()))
    video = url.streams.get_highest_resolution()
    video.download()
    Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)


def downloader3():
    url = YouTube(str(inp3.get()))
    video = url.streams.get_highest_resolution()
    video.download()
    Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)


def downloader4():
    url = YouTube(str(inp4.get()))
    video = url.streams.get_highest_resolution()
    video.download()
    Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)


def downloader5():
    url = YouTube(str(inp5.get()))
    video = url.streams.get_highest_resolution()
    video.download()
    Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)


janela = Tk()
janela.title('God Of YouTube Downloader - No AD Version')
janela.geometry('480x500')
janela.resizable(0, 0)
janela.config(background='#dde')
inp = StringVar()
inp2 = StringVar()
inp3 = StringVar()
inp4 = StringVar()
inp5 = StringVar()
imgLogo = PhotoImage(file='youtube.gif')



imagem_logo = Label(janela, image=imgLogo).grid(column=0, row=0, pady=20)

cole_aqui = Label(janela, text='Cole aqui, seu link!', font='arial 15 bold', background='#dde')
cole_aqui.grid(column=0, row=1, pady=5)

entrada_download = Entry(janela, width=50, textvariable=inp)
entrada_download.grid(column=0, row=2, padx=82, pady=3)

entrada_download2 = Entry(janela, width=50, textvariable=inp2)
entrada_download2.grid(column=0, row=4, padx=82, pady=3)

entrada_download3 = Entry(janela, width=50, textvariable=inp3)
entrada_download3.grid(column=0, row=5, padx=82, pady=3)

entrada_download4 = Entry(janela, width=50, textvariable=inp4)
entrada_download4.grid(column=0, row=6, padx=82, pady=3)

entrada_download5 = Entry(janela, width=50, textvariable=inp5)
entrada_download5.grid(column=0, row=7, padx=82, pady=3)

botao_download = Button(janela, text='Baixar', width=20,
                        command=lambda: [threading(), threading2(), threading3(), threading4(), threading5()])
botao_download.grid(column=0, row=8, pady=20)

identif = Label(janela, text='@God Of Python', font='arial 8 bold').place(x=380, y=470)


janela.mainloop()

我创建了示例,它使用 Label 来显示还有多少字节需要下载。

如果你得到文件长度,那么你可以用百分比来计算它。


YouTube()可以使用on_progress_callbackon_complete_callback在下载过程中执行功能,可以用来显示进度。

这些函数 运行 在新线程中,它们不能直接更新小部件(它会产生错误)所以我使用 Queue() 将这些函数的值发送到主线程。并且主线程使用 root.after() 每 100 毫秒执行一次 update_status()。这个函数从队列中获取信息并更新标签。

每个线程得到 number 稍后发回和 update_status() 要更新的标签

它可以以类似的方式与 Progressbar 小部件一起使用。


为了测试,我直接在代码中添加了几个链接 - 所以我不必在每个 运行.

中手动添加它们

因为我使用for循环和list所以我减少了代码但仍然有5个条目,并且可以同时下载5个文件。如果我使用 range(10) 那么我可以下载 10 个文件。

最初我创建 all_threads 来保留所有线程并使用 is_alive() 检查完成的线程,但现在我不使用此列表。

我试图只保留示例中真正重要的元素 - 所以我删除了字体、颜色、图像。

from pytube import YouTube
import tkinter as tk  # PEP8: `import *` is not preferred
from threading import Thread
from queue import Queue

# --- functions ---

def update_status():
    # check if there are new messages in queue and update correct label
    while not queue.empty():
        number, text = queue.get()
        print('[update_status]:', number, text)
        all_labels[number]['text'] = text

    root.after(100, update_status)  # run again after 100ms
    
def on_progress(number, queue, stream, chunk, bytes_remaining):
    print('[on_progress] bytes_remaining:', bytes_remaining)
    queue.put( (number, bytes_remaining) )

def on_complete(number, queue, stream, file_path):
    print('[on_complete] file_path:', file_path)
    queue.put( (number, file_path) )

def download(url, number, queue):
    print('[download] started:', url)
    queue.put( (number, 'started') )
    
    yt = YouTube(url,
                 on_progress_callback=lambda *args:on_progress(number, queue, *args),
                 on_complete_callback=lambda *args:on_complete(number, queue, *args),
                 )
    video = yt.streams.get_highest_resolution()
    video.download()
    
    all_threads[number] = None  # inform that finished

    print('[download] finished:', url)
    #queue.put( (number, 'finished') )
    
def start_threads():
    for number, (t, entry) in enumerate(zip(all_threads, all_entries)):
        url = entry.get().strip()
        
        if url:
            print('[start_threads]:', number, 'starting')
            t = Thread(target=download, args=(url, number, queue))
            t.start()
        else:
            print('[start_threads]:', number, 'empty')
            t = None
            
        all_threads[number] = t

# --- main ---

all_threads = [None] * 5  # to have all values at start
all_entries = []
all_labels  = []

root = tk.Tk()

queue = Queue()

for n in range(5):
    entry = tk.Entry(root, width=50)
    entry.grid(column=0, row=n)
    all_entries.append(entry)

    label = tk.Label(root, width=15, text='-')
    label.grid(column=1, row=n)
    all_labels.append(label)
    
button = tk.Button(root, text='Start', command=start_threads)
button.grid(column=0, row=n+1, columnspan=2)

# for test I put some links so I don't have to do it manually
all_entries[0].insert('end', 'https://www.youtube.com/watch?v=aqz-KE-bpKQ')
all_entries[1].insert('end', 'https://www.youtube.com/watch?v=bNfYUsDSrOs')
all_entries[2].insert('end', 'https://www.youtube.com/watch?v=W7LWny6c4wI')
all_entries[3].insert('end', 'https://www.youtube.com/watch?v=A8LRxIANzQs')

update_status()

root.mainloop()