如何在使用 pytube 时停止不响应 tkinter?

How to stop not responding tkinter whilst using pytube?

我创建了一个下载 audio/video 的 tkinter 应用程序。它确实工作正常,但是(在这里猜测)因为下载需要 5-10 秒,它会阻塞主循环并导致它说在完成之前没有响应,这不是很好。我知道可以使用线程,但我从未真正使用过它,而且我尝试过的所有示例都无法正常工作。

这是我的完整代码,没有线程:(如果太长而无法阅读,我将只展示某些功能,这可能是

from tkinter import *
from tkinter import messagebox
from pytube import YouTube
import sys, os, threading, time

def btn_clicked():
    print("Button Clicked")


window = Tk()

window.geometry("505x187")
window.title("Youtube Video Downloader")
photo = PhotoImage(file = "icon.png")
window.iconphoto(False, photo)
window.configure(bg = "#3d3d3d")
canvas = Canvas(
    window,
    bg = "#3d3d3d",
    height = 187,
    width = 505,
    bd = 0,
    highlightthickness = 0,
    relief = "ridge")
canvas.place(x = 0, y = 0)



def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
        
    points = [x1+radius, y1,
              x1+radius, y1,
              x2-radius, y1,
              x2-radius, y1,
              x2, y1,
              x2, y1+radius,
              x2, y1+radius,
              x2, y2-radius,
              x2, y2-radius,
              x2, y2,
              x2-radius, y2,
              x2-radius, y2,
              x1+radius, y2,
              x1+radius, y2,
              x1, y2,
              x1, y2-radius,
              x1, y2-radius,
              x1, y1+radius,
              x1, y1+radius,
              x1, y1]

    return canvas.create_polygon(points, **kwargs, smooth=True)

radius = 10
olw = 1
ft = None

ebl = []
eb = round_rectangle(
    320, 37, 320+174, 37+51,
    fill = "#212121",
    radius = radius,
    outline = "")
ebl.append(eb)

dl = []
d = round_rectangle(
    320, 135, 320+174, 135+43,
    fill = "#212121",
    radius = radius,
    outline = "")
dl.append(d)

dt = canvas.create_text(
    370, 148,
    text = "DOWNLOAD",
    fill = "#a0a0a0",
    anchor = "nw",
    font = ("None", int(14.0) - 5))
dl.append(dt)

mptl = []
mpt = round_rectangle(
    320, 97, 320+82, 97+31,
    fill = "#212121",
    radius = radius,
    outline = "")
mptl.append(mpt)

mpfl = []
mpf = round_rectangle(
    412, 97, 412+82, 97+31,
    fill = "#212121",
    radius = radius,
    outline = "")
mpfl.append(mpf)

pb = my_rectangle = round_rectangle(
    11, 10, 11+300, 10+168,
    fill = "#212121",
    radius = radius,
    outline = "")

ebt = canvas.create_text(
    326, 43,
    text = "Video URL:",
    fill = "#a0a0a0",
    anchor = "nw",
    font = ("None", int(13.0) - 5))
ebl.append(ebt)

mptt = canvas.create_text(
    348, 103,
    text = "MP3",
    fill = "#a0a0a0",
    anchor = "nw",
    font = ("None", int(14.0) - 5))
mptl.append(mptt)

mpft = canvas.create_text(
    440, 103,
    text = "MP4",
    fill = "#a0a0a0",
    anchor = "nw",
    font = ("None", int(14.0) - 5))
mpfl.append(mpft)

canvas.create_text(
    320, 13,
    text = "Youtube Video Downloader",
    fill = "#a0a0a0",
    anchor = "nw",
    font = ("None", int(15.0) - 5))

entry0_img = PhotoImage(file = f"img_textBox0.png")
entry0_bg = canvas.create_image(
    406.0, 70.5,
    image = entry0_img)

ebf = Entry(
    bd = 0,
    bg = "#212121",
    fg = "#727272",
    insertbackground = "#727272",
    highlightthickness = 0)

ebf.place(
    x = 326, y = 61,
    width = 160,
    height = 17)


def OnHover(item):
    if item in mptl or mpfl:
        if canvas.itemcget(item, "outline") == "":
            if canvas.itemcget(item, "tag") == "clickset":
                pass
            else:
                canvas.itemconfig(item, outline="#FF7276", width = olw, tag="hoverset")
        else:
            pass

def UnHover(item):
    if item in mptl or mpfl:
        if canvas.itemcget(item, "tag") == "hoverset":
            canvas.itemconfig(item, outline="", width = olw, tag="")
        else:
            pass
def bindhover(l, item):
    for i in l:
        canvas.tag_bind(i, '<Enter>', lambda event: OnHover(item=item))
        canvas.tag_bind(i, '<Leave>', lambda event: UnHover(item=item))

bindhover(mptl, mpt)
bindhover(mpfl, mpf)
bindhover(dl, d)


def OnClick(item):
    global ft
    if item in mptl or mpfl:
        if canvas.itemcget(item, "tag") == "hoverset":
            if item == mpt:
                if canvas.itemcget(mpf, "outline") == "":
                    ft = "mp3"
                else:
                    canvas.itemconfig(mpf, outline="")
                    ft = "mp3"
            if item == mpf:
                if canvas.itemcget(mpt, "outline") == "":
                    ft = "mp4"
                else:
                    canvas.itemconfig(mpt, outline="")
                    ft = "mp4"

            canvas.itemconfig(item, tag="clickset")
        else:
            pass
def bindclick(l, item):
    for i in l:
        canvas.tag_bind(i, '<Button-1>', lambda event: OnClick(item=item))
        
bindclick(mptl, mpt)
bindclick(mpfl, mpf)

def download():
    e = False

    link = ebf.get()
    try:
        link2 = YouTube(link)
    except:
        messagebox.showinfo("Invalid link", "Invalid youtube link")
        e = True
    if e == False:
        if ft == None:
            messagebox.showinfo("Select a file type", "No file type has been selected")
            e = True
        elif ft == 'mp3':
            if e == False:
                video = link2.streams.filter(only_audio=True).first()
                vid = video.download('Downloads')
                print("test")
                base, ext = os.path.splitext(vid)
                new_file = base + '.mp3'
                os.rename(vid, new_file)
        else:
            if e == False:
                video = link2.streams.first()
                vid = video.download('Downloads')
            
        else:
            pass
def bdownload(l, item):
    for i in l:
        canvas.tag_bind(i, '<Button-1>', lambda event: download())    
bdownload(dl, d)

window.resizable(False, False)
window.mainloop()

下载:

def download():
    e = False

    link = ebf.get()
    try:
        link2 = YouTube(link)
    except:
        messagebox.showinfo("Invalid link", "Invalid youtube link")
        e = True
    if e == False:
        if ft == None:
            messagebox.showinfo("Select a file type", "No file type has been selected")
            e = True
        elif ft == 'mp3':
            if e == False:
                video = link2.streams.filter(only_audio=True).first()
                vid = video.download('Downloads')
                print("test")
                base, ext = os.path.splitext(vid)
                new_file = base + '.mp3'
                os.rename(vid, new_file)
        else:
            if e == False:
                video = link2.streams.first()
                vid = video.download('Downloads')
            
        else:
            pass
def bdownload(l, item):
    for i in l:
        canvas.tag_bind(i, '<Button-1>', lambda event: download())    
bdownload(dl, d)

只是想知道如何将其放入线程中?

开始新话题的格式为:

new_thread = threading.Thread(target=..., daemon=True, args=(..., ...))
new_thread.start()

所以试试这个:

def download_worker(player, ft):
    vid = video.download('Downloads')
    if ft == "mp3":
        print("test")
        base, ext = os.path.splitext(vid)
        new_file = base + '.mp3'
        os.rename(vid, new_file)

def download():
    ...
    elif ft == 'mp3':
        if e == False:
            video = link2.streams.filter(only_audio=True).first()
            # Imagine this like: download_worker(video, ft)
            new_thread = threading.Thread(target=download_worker, daemon=True, args=(video, ft))
            new_thread.start()
    else:
        if e == False:
            video = link2.streams.first()
            # Imagine this like: download_worker(video, ft)
            new_thread = threading.Thread(target=download_worker, daemon=True, args=(video, ft))
            new_thread.start()
     # Keep in mind that the `vid` variable doesn't exist here.

在这种情况下,目标函数是 download_worker,参数是:videoft

线程在目标函数结束时自动结束。