Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python PyQt5-QThread:在线程仍在运行时销毁_Python_Pyqt_Pyqt5_Qthread - Fatal编程技术网

Python PyQt5-QThread:在线程仍在运行时销毁

Python PyQt5-QThread:在线程仍在运行时销毁,python,pyqt,pyqt5,qthread,Python,Pyqt,Pyqt5,Qthread,我试图弄明白,如果我试图在线程完成后再次运行线程,为什么这段代码会崩溃 第一次单击“启动5个线程”时,它运行良好并完成。但如果我再次点击它。整个程序崩溃,当线程仍在运行时,我得到QThread:destromed错误 此代码是在web上找到的。我正在努力从中学习 import time import sys from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import QA

我试图弄明白,如果我试图在线程完成后再次运行线程,为什么这段代码会崩溃

第一次单击“启动5个线程”时,它运行良好并完成。但如果我再次点击它。整个程序崩溃,当线程仍在运行时,我得到QThread:destromed错误

此代码是在web上找到的。我正在努力从中学习

import time
import sys

from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget


def trap_exc_during_debug(*args):
    # when app raises uncaught exception, print info
    print(args)


# install exception hook: without this, uncaught exception would cause application to exit
sys.excepthook = trap_exc_during_debug


class Worker(QObject):
    """
    Must derive from QObject in order to emit signals, connect slots to other signals, and operate in a QThread.
    """

    sig_step = pyqtSignal(int, str)  # worker id, step description: emitted every step through work() loop
    sig_done = pyqtSignal(int)  # worker id: emitted at end of work()
    sig_msg = pyqtSignal(str)  # message to be shown to user

    def __init__(self, id: int):
        super().__init__()
        self.__id = id
        self.__abort = False

    @pyqtSlot()
    def work(self):
        """
        Pretend this worker method does work that takes a long time. During this time, the thread's
        event loop is blocked, except if the application's processEvents() is called: this gives every
        thread (incl. main) a chance to process events, which in this sample means processing signals
        received from GUI (such as abort).
        """
        thread_name = QThread.currentThread().objectName()
        thread_id = int(QThread.currentThreadId())  # cast to int() is necessary
        self.sig_msg.emit('Running worker #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))

        for step in range(100):
            time.sleep(0.1)
            self.sig_step.emit(self.__id, 'step ' + str(step))

            # check if we need to abort the loop; need to process events to receive signals;
            app.processEvents()  # this could cause change to self.__abort
            if self.__abort:
                # note that "step" value will not necessarily be same for every thread
                self.sig_msg.emit('Worker #{} aborting work at step {}'.format(self.__id, step))
                break

        self.sig_done.emit(self.__id)

    def abort(self):
        self.sig_msg.emit('Worker #{} notified to abort'.format(self.__id))
        self.__abort = True


class MyWidget(QWidget):
    NUM_THREADS = 5

    # sig_start = pyqtSignal()  # needed only due to PyCharm debugger bug (!)
    sig_abort_workers = pyqtSignal()

    def __init__(self):
        super().__init__()

        self.setWindowTitle("Thread Example")
        form_layout = QVBoxLayout()
        self.setLayout(form_layout)
        self.resize(400, 800)

        self.button_start_threads = QPushButton()
        self.button_start_threads.clicked.connect(self.start_threads)
        self.button_start_threads.setText("Start {} threads".format(self.NUM_THREADS))
        form_layout.addWidget(self.button_start_threads)

        self.button_stop_threads = QPushButton()
        self.button_stop_threads.clicked.connect(self.abort_workers)
        self.button_stop_threads.setText("Stop threads")
        self.button_stop_threads.setDisabled(True)
        form_layout.addWidget(self.button_stop_threads)

        self.log = QTextEdit()
        form_layout.addWidget(self.log)

        self.progress = QTextEdit()
        form_layout.addWidget(self.progress)

        QThread.currentThread().setObjectName('main')  # threads can be named, useful for log output
        self.__workers_done = None
        self.__threads = None

    def start_threads(self):
        self.log.append('starting {} threads'.format(self.NUM_THREADS))
        self.button_start_threads.setDisabled(True)
        self.button_stop_threads.setEnabled(True)

        self.__workers_done = 0
        self.__threads = []
        for idx in range(self.NUM_THREADS):
            worker = Worker(idx)
            thread = QThread()
            thread.setObjectName('thread_' + str(idx))
            self.__threads.append((thread, worker))  # need to store worker too otherwise will be gc'd
            worker.moveToThread(thread)

            # get progress messages from worker:
            worker.sig_step.connect(self.on_worker_step)
            worker.sig_done.connect(self.on_worker_done)
            worker.sig_msg.connect(self.log.append)

            # control worker:
            self.sig_abort_workers.connect(worker.abort)

            # get read to start worker:
            # self.sig_start.connect(worker.work)  # needed due to PyCharm debugger bug (!); comment out next line
            thread.started.connect(worker.work)
            thread.start()  # this will emit 'started' and start thread's event loop

        # self.sig_start.emit()  # needed due to PyCharm debugger bug (!)

    @pyqtSlot(int, str)
    def on_worker_step(self, worker_id: int, data: str):
        self.log.append('Worker #{}: {}'.format(worker_id, data))
        self.progress.append('{}: {}'.format(worker_id, data))

    @pyqtSlot(int)
    def on_worker_done(self, worker_id):
        self.log.append('worker #{} done'.format(worker_id))
        self.progress.append('-- Worker {} DONE'.format(worker_id))
        self.__workers_done += 1
        if self.__workers_done == self.NUM_THREADS:
            self.log.append('No more workers active')
            self.button_start_threads.setEnabled(True)
            self.button_stop_threads.setDisabled(True)
            # self.__threads = None

    @pyqtSlot()
    def abort_workers(self):
        self.sig_abort_workers.emit()
        self.log.append('Asking each worker to abort')
        for thread, worker in self.__threads:  # note nice unpacking by Python, avoids indexing
            thread.quit()  # this will quit **as soon as thread event loop unblocks**
            thread.wait()  # <- so you need to wait for it to *actually* quit

        # even though threads have exited, there may still be messages on the main thread's
        # queue (messages that threads emitted before the abort):
        self.log.append('All threads exited')


if __name__ == "__main__":
    app = QApplication([])

    form = MyWidget()
    form.show()

    sys.exit(app.exec_())
导入时间
导入系统
从PyQt5.QtCore导入QObject、QThread、pyqtSignal、pyqtlot
从PyQt5.QtWidgets导入QApplication、QPushButton、QTextEdit、QVBoxLayout、QWidget
def trap_exc_调试期间(*args):
#当应用程序引发未捕获异常时,打印信息
打印(args)
#安装异常挂钩:如果没有这个,未捕获的异常将导致应用程序退出
sys.excepthook=trap\u exc\u调试期间
班级工作人员(QObject):
"""
必须从QObject派生才能发出信号,将插槽连接到其他信号,并在QThread中操作。
"""
sig_step=pyqtSignal(int,str)#工作者id,步骤描述:通过work()循环每一步发出
sig_done=pyqtSignal(int)#工作人员id:在工作结束时发出()
sig_msg=pyqtSignal(str)#向用户显示的消息
定义初始化(self,id:int):
super()。\uuuu init\uuuuu()
self.\uu id=id
self.\uu abort=False
@pyqtSlot()
def工作(自我):
"""
假设此辅助方法的工作时间很长。在此期间,线程
事件循环被阻止,除非调用了应用程序的processEvents():这将提供
线程(包括main)有机会处理事件,在本示例中,这意味着处理信号
从GUI接收(例如中止)。
"""
thread_name=QThread.currentThread().objectName()
thread_id=int(QThread.currentThreadId())#强制转换为int()是必需的
self.sig_msg.emit('Running worker#{}from thread“{}”(#{})').format(self.sig_id,thread_name,thread_id))
对于步进范围(100):
睡眠时间(0.1)
self.sig_step.emit(self.sig_id,'step'+str(step))
#检查是否需要中止循环;需要处理事件以接收信号;
app.processEvents()#这可能会导致对self的更改。u中止
如果自动终止:
#请注意,对于每个线程,“步长”值不一定相同
self.sig_msg.emit('Worker{}正在步骤{}中止工作。格式(self.sig_id,步骤))
打破
self.sig\u done.emit(self.\u id)
def中止(自我):
self.sig_msg.emit('Worker{}通知中止'。格式(self.\uu id))
self.\uuu abort=True
类MyWidget(QWidget):
线程数=5
#sig_start=pyqtSignal()#仅由于PyCharm调试器错误(!)而需要
sig_abort_workers=pyqtSignal()
定义初始化(自):
super()。\uuuu init\uuuuu()
self.setWindowTitle(“线程示例”)
form_layout=QVBoxLayout()
self.setLayout(表单_布局)
自我调整大小(400800)
self.button\u start\u threads=QPushButton()
self.button\u start\u threads.clicked.connect(self.start\u threads)
self.button_start_threads.setText(“start{}threads.format(self.NUM_threads))
form\u layout.addWidget(self.button\u start\u线程)
self.button\u stop\u threads=QPushButton()
self.button\u stop\u threads.clicked.connect(self.abort\u workers)
self.button\u stop\u threads.setText(“停止线程”)
self.button\u stop\u threads.setDisabled(True)
form\u layout.addWidget(self.button\u stop\u线程)
self.log=QTextEdit()
form_layout.addWidget(self.log)
self.progress=QTextEdit()
form_layout.addWidget(self.progress)
QThread.currentThread().setObjectName('main')#可以命名线程,这对日志输出很有用
self.\u workers\u done=无
self.\uu线程=无
def start_线程(自):
self.log.append('starting{}threads'。格式(self.NUM_threads))
self.button\u start\u threads.setDisabled(True)
self.button\u stop\u threads.setEnabled(True)
self.\uu workers\u done=0
self._线程=[]
对于范围内的idx(self.NUM_线程):
工人=工人(idx)
thread=QThread()
setObjectName('thread_'+str(idx))
self.u threads.append((线程,工作线程))#也需要存储工作线程,否则将被gc
worker.moveToThread(线程)
#从worker获取进度消息:
worker.sig\u step.connect(self.on\u worker\u step)
worker.sig\u done.connect(self.on\u worker\u done)
worker.sig_msg.connect(self.log.append)
#控制工人:
self.sig\u abort\u workers.connect(worker.abort)
#获取读取以启动工作进程:
#self.sig_start.connect(worker.work)#由于PyCharm调试器错误(!)而需要;注释掉下一行
线程.started.connect(worker.work)
thread.start()#这将发出'started'和start线程的事件循环
#self.sig_start.emit()#由于PyCharm调试器错误(!)而需要
@pyqtSlot(int,str)
工作步骤上的定义(self,worker\u id:int,data:str):
self.log.append('Worker{}:{}'。格式(Worker\u id,数据))
self.progress.append(“{}:{}.”格式(工作者id,数据))
@pyqtSlot(int)
已完成工作人员的定义(自身、工作人员id):
self.log.append('worker#{}done'。格式(worker#u id))
self.progress.append('--Worker{}DONE'。格式(Worker_id))
自身工作人员工作完成+=1
如果self.\u workers\u done==self.NUM\u线程:
self.log.append('不再有工作进程处于活动状态')
self.button\u start\u threads.setEnabled(True)
self.button\u stop\u threads.setDisabled(True)
#self.\uu线程=无
@pyqtSlot()
def中止工作人员(自身):
self.sig\u abort\u workers.emit()
self.log.append('要求每个工作进程中止')
thread = QThread()
thread = QThread(parent=self)