在qthread中停止长时间运行的进程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在qthread中停止长时间运行的进程相关的知识,希望对你有一定的参考价值。
我正在尝试使用youtube-dl
模块从youtube下载视频。我创建了一个简单的GUI来做一点工作,我需要当用户点击开始按钮时线程将被调用并下载开始并使用emit
方法发送数据,当此数据到达read
类中的Main
函数时,线程必须在调用后停止来自GUI的stop
函数,我尝试使用exec_()
在qthread中创建事件循环并使用exit
停止线程,我也尝试使用terminate
但GUI冻结。
我使用的代码是:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from youtube_dl import *
class Worker(QThread):
data = pyqtSignal(object)
def __init__(self):
super(Worker, self).__init__()
self.flag = True
def sendHook(self, data, status = {'status':'downloading'}):
self.data.emit(data)
def stop(self):
self.quit()
self.exit()
def run(self):
self.y = YoutubeDL({'progress_hooks':[self.sendHook]})
self.y.download(['https://www.youtube.com/watch?v=LKIXbNW-B5g'])
self.exec_()
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
layout = QVBoxLayout()
self.l = QLabel("Hello")
b = QPushButton("Start!")
b.pressed.connect(self.connectthread)
layout.addWidget(self.l)
layout.addWidget(b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
def read(self, data):
self.thread.stop()
def connectthread(self):
self.thread = Worker()
self.thread.data.connect(self.read)
self.thread.start()
app = QApplication([])
window = MainWindow()
app.exec_()
谢谢先进,我希望我已经很好地解释了这个问题。
通过在你的工作者的self.exec_()
方法中调用run()
,你可以在下载完成后在这个线程上启动一个新的事件循环,然后这个事件循环就会继续运行。你不需要这里的事件循环,你只需要一个单独的事件循环,如果你想使用他们的moveToThread()
方法将QObjects移动到它来将它们与主事件循环分离,但这里不需要,你没有做任何需要Qt事件循环的东西。这也是为什么调用stop()
或exit()
什么都不做,它只影响事件循环。阻止这个线程的唯一方法是它的terminate()
方法,这也有点工作:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from youtube_dl import *
class Worker(QThread):
data = pyqtSignal(object)
def __init__(self):
super(Worker, self).__init__()
self.flag = True
def sendHook(self, data, status = {'status':'downloading'}):
self.data.emit(data)
def stop(self):
self.terminate()
print("QThread terminated")
def run(self):
self.y = YoutubeDL({'progress_hooks':[self.sendHook]})
self.y.download(['https://www.youtube.com/watch?v=LKIXbNW-B5g'])
print("finished")
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
layout = QVBoxLayout()
self.l = QLabel("Hello")
b = QPushButton("Start!")
b.pressed.connect(self.connectthread)
layout.addWidget(self.l)
layout.addWidget(b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.thread = None
self.show()
def read(self, data):
print("read:", data)
def connectthread(self):
if self.thread is not None:
# already running
self.thread.stop()
self.thread = None
return
self.thread = Worker()
self.thread.data.connect(self.read)
self.thread.start()
app = QApplication([])
window = MainWindow()
app.exec_()
这里我改变了你的程序,所以第一次单击按钮时工作程序启动,第二次终止线程,依此类推。
然而,以这种方式终止线程是危险的并且是沮丧的。 Python线程通常需要合作才能停止,因为通过设计它们没有办法被中断。在这种情况下它只能起作用,因为PyQt控制着线程。
遗憾的是,没有办法优雅地停止youtube-dl下载,请参阅this related issue以获取更多信息。一般来说,无法保证杀死调用download()
的线程实际上会停止下载。 YoutubeDL支持具有不同下载程序的插件系统。例如,要下载hls流,将启动外部ffmpeg
(或avconv
)进程,该进程不会通过终止工作线程来停止。对于在内部使用其他线程或进程的下载程序或使用ffmpeg
执行的后处理步骤也是如此。
如果您希望能够安全地停止下载,则必须使用单独的过程,这样您就可以使用SIGINT
信号(与按Ctrl-C相同)告诉它停止。
以上是关于在qthread中停止长时间运行的进程的主要内容,如果未能解决你的问题,请参考以下文章