Tkinter 中的非阻塞线程和"停止线程"按钮



我有一个带有文本区域的Tkinter程序,可以在其中粘贴youtube网址列表,旁边有一个按钮,上面写着"下载",它将把网址上的视频下载到我电脑上的一个文件夹中。我正在使用pafy模块下载视频。

我已经设置了它,以便在按下按钮时它会启动一个线程,该线程运行下载视频的功能。我以为通过线程,这将使程序不受影响,但是在我按下按钮后片刻,即使视频下载正常,程序也会被阻止,程序顶部有"未响应"消息并保持这种状态,直到功能停止运行,成功完成下载。换句话说,程序被正在运行的函数/线程阻止,并且有一个小沙漏的东西,在下载完成之前禁用任何其他点击或与程序的交互。

我在"下载"按钮旁边还有一个按钮,上面有"停止",可以在单击下载时停止下载。显然,由于整个程序都被阻止,即使有线程,在视频下载时,也无法单击"停止"按钮。

任何人都可以指出如何在不阻止程序的情况下让线程工作以及如何在这种情况下运行"停止"按钮?代码如下:

import pafy
from threading import Thread
def downloadvideos():
textdata = text1.get('1.0', 'end-1c').splitlines()
downloadlist=[]
downloadlist1 = []
downloaddict = {}
for t in textdata:
print t
downloadlist.append(t)
print downloadlist
try:
url = t
video = pafy.new(url)
best = video.getbest()
title = video.title
best.download(filepath=foldername)
downloaddict[title] = url
except Exception:
sys.exc_clear()
def but1thread():
global thethread
thethread = Thread(target=downloadvideos, name='firstthread')
thethread.daemon = True
thethread.start()
thethread.join()
def but1stop():
thethread.running = False

button1 = Button(frame1, text="Download", activebackground="green", command=but1thread)
button1.grid(row=1, column=0, sticky=W, padx=(10, 0), pady=(20, 0))
button2 = Button(frame1, text="Stop", background="red", command=but1stop)
button2.grid(row=1, column=1)

调用thethread.join()将阻塞tkinter主循环,因此不应调用它。

要停止下载,您可以使用callback选项best.download(...). 在回调函数中,检查是否单击Stop按钮。如果是,则引发异常以停止下载:

def on_downloading(*args):
if not thethread.running:
print 'ncancelling ...'
raise Exception('download cancelled')
def downloadvideos():
textdata = text1.get('1.0', 'end-1c').splitlines()
downloadlist=[]
downloadlist1 = []
downloaddict = {}
thethread.running = True
for t in textdata:
if not thethread.running:
break
print t
downloadlist.append(t)
print downloadlist
try:
url = t
video = pafy.new(url)
best = video.getbest()
title = video.title
best.download(filepath=foldername, callback=on_downloading)
downloaddict[title] = url
except Exception as e:
print e
sys.exc_clear()
print('download completed')
def but1thread():
global thethread
thethread = Thread(target=downloadvideos, name='firstthread')
thethread.daemon = True
thethread.start()
#thethread.join()  # join() will block the tkinter mainloop