Pyqt5 qthread +不工作信号+ gui冻结

时间:2021-08-29 20:40:08

I am trying to make a mailbox checker with imap lib, it work pretty fine with python, queue and multithread without gui.

我正在尝试使用imap lib创建一个邮箱检查器,它可以在没有gui的python、队列和多线程中正常工作。

But when I try to put a gui, every fonction i made, make the gui freeze until finish .

但是当我尝试放置一个gui时,我做的每一个函数都会使gui冻结直到完成。

I tried many thing from various doc(add qthread, signal, cursorr etcc) and tutorials none worked for me .

我尝试了各种文档(添加了qthread、signal、cursorr etcc)和教程,但没有一个适合我。

Can someone help me to understand how to set or append a text to a QtextEdit while running a function coz it work only after finish .

是否有人能帮助我理解如何在运行函数时设置或附加文本到QtextEdit,因为它只在完成之后才会工作。

Here is my code :

这是我的代码:

class Checker(QtCore.QThread):
    signal = QtCore.pyqtSignal(object)

    def __init__(self, lignesmailtocheck):
        QtCore.QThread.__init__(self)
        self.lignesmailtocheck = lignesmailtocheck

    def run(self):
            lignemailtocheck = self.lignesmailtocheck.strip()                        
            maillo, passo = lignemailtocheck.split(":",1)
            debmail, finmail = maillo.split("@",1)
            setimap =["oultook.com:imap-mail.outlook.com", "gmail.com:imap.gmail.com"]
            for lignesimaptocheck in sorted(setimap):
                    ligneimaptocheck = lignesimaptocheck.strip()
                    fai, imap = ligneimaptocheck.split(":",1)                                
                    if finmail == fai:
                            passo0 = passo.rstrip()
                            try :
                                    mail = imaplib.IMAP4_SSL(imap)
                                    mail.login(maillo, passo)
                                    mailboxok = open("MailBoxOk.txt", "a+", encoding='utf-8', errors='ignore')
                                    mailboxok.write(maillo+":"+passo+"\n")
                                    mailboxok.close()
                                    totaly = maillo+":"+passo0+":"+imap                                
                                    print(maillo+":"+passo+"\n")

                                    self.send_text.emit(totaly)
                                    time.sleep(1)
                            except imaplib.IMAP4.error:                          
                                           print ("LOGIN FAILED!!! ")
class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 300)

        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setGeometry(QtCore.QRect(150, 210, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.pushButton.clicked.connect(self.gogogo)

        self.openliste = QtWidgets.QToolButton(Form)
        self.openliste.setGeometry(QtCore.QRect(40, 110, 71, 21))
        self.openliste.setObjectName("openliste")

        self.textEdit = QtWidgets.QTextEdit(Form)
        self.textEdit.setGeometry(QtCore.QRect(170, 50, 201, 121))
        self.textEdit.setObjectName("textEdit")

        self.progressBar = QtWidgets.QProgressBar(Form)
        self.progressBar.setGeometry(QtCore.QRect(10, 260, 381, 23))
        self.progressBar.setValue(0)
        self.progressBar.setObjectName("progressBar")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "PushButton"))
        self.openliste.setText(_translate("Form", "..."))

    def gogogo(self):

        mailtocheck = open('File/toCheck.txt', 'r', encoding='utf-8', errors='ignore').readlines()        
        setmailtocheck = set(mailtocheck)
        for lignesmailtocheck in sorted(setmailtocheck):
            checker = Checker(lignesmailtocheck)

            thread = QThread()
            checker.moveToThread(thread)
            # connections after move so cross-thread:
            thread.started.connect(checker.run)
            checker.signal.connect(self.checkedok)
            thread.start()

    def checkedok(self, data):
        print(data)
        self.textEdit.append(data)
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())

3 个解决方案

#1


26  

Since there are often questions about using QThread in PyQt, similar to yours, here is an example that shows how to correctly use threads in PyQt. I'm hoping it can be useful as a goto-answer for similar questions so I spent a bit more time than usual preparing this.

由于在PyQt中使用QThread经常会遇到类似于您的问题,这里有一个示例展示了如何在PyQt中正确使用线程。我希望它能成为类似问题的有效答案,所以我花了比平时多一点的时间准备这个问题。

The example creates a number of worker objects that execute in non-main threads and communicate with the main (ie GUI) thread via Qt's asynchronous signals.

这个示例创建了许多工作对象,它们在非主线程中执行,并通过Qt的异步信号与主(ie GUI)线程通信。

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_())

The main concepts necessary to understand multi-thread programming in PyQt are the following:

理解PyQt中多线程编程所需的主要概念如下:

  • Qt threads have their own event loop (specific to each thread). The main thread, aka the GUI thread, is also a QThread, and its event loop is managed by that thread.
  • Qt线程有自己的事件循环(特定于每个线程)。主线程,即GUI线程,也是一个QThread,它的事件循环由该线程管理。
  • Signals between threads are transmitted (asynchronously) via the receiving thread's event loop. Hence responsiveness of GUI or any thread = ability to process events. E.g., if a thread is busy in a function loop, it can't process events, so it won't respond to signals from the GUI until the function returns.
  • 线程之间的信号通过接收线程的事件循环(异步)传输。因此,GUI或任何线程都有响应能力来处理事件。例:如果一个线程在函数循环中很忙,那么它就不能处理事件,所以在函数返回之前,它不会对来自GUI的信号做出响应。
  • If a worker object (method) in a thread may have to change its course of action based on signals from the GUI (say, to interrupt a loop or a wait), it must call processEvents() on the QApplication instance. This will allow the QThread to process events, and hence to call slots in response to async signals from the GUI. Note that QApplication.instance().processEvents() seems to call processEvents() on every thread, if this is not desired then QThread.currentThread().processEvents() is a valid alternative.
  • 如果线程中的worker对象(方法)可能必须基于来自GUI的信号(例如,中断循环或等待)更改其操作过程,那么它必须在QApplication实例上调用processEvents()。这将允许QThread处理事件,并因此调用slot以响应GUI的异步信号。注意,QApplication.instance().processEvents()似乎在每个线程上都调用processEvents(),如果不需要这样做,那么QThread.currentThread().processEvents()是一个有效的替代方法。
  • A call to QThread.quit() does not immediately quit its event loop: it must wait for currently executing slot (if any) to return. Hence once a thread is told to quit, you must wait() on it. So aborting a worker thread usually involves signaling it (via a custom signal) to stop whatever it is doing: this requires a custom signal on a GUI object, a connection of that signal to a worker slot, and worker work method must call thread's processEvents() to allow the emitted signal to reach the slot while doing work.
  • 对QThread.quit()的调用不会立即退出它的事件循环:它必须等待当前执行的slot(如果有的话)返回。因此,一旦线程被告知要退出,您必须等待()。所以堕胎工作线程通常涉及到信号(通过自定义信号)停止正在做的一切:这需要一个自定义的信号一个GUI对象,连接的信号到一个工人槽,和工人工作方法必须调用线程的processEvents()允许发射信号到达槽而工作。

#2


1  

I can't test because setimap is not available on my system. I renamed CheckerThread to Checker since it is no longer a thread (it just "lives" in a thread):

我不能测试,因为我的系统上没有setimap。我将CheckerThread重命名为Checker,因为它不再是一个线程(它只是在一个线程中“生存”):

class Checker(QtCore.QObject):

Then just replace the contents of the loop in gogogo(self) with this:

然后将gogogogo (self)中的循环内容替换为:

for lignesmailtocheck in sorted(setmailtocheck):
    checker = Checker(lignesmailtocheck)

    thread = QThread()
    checker.moveToThread(thread)
    # connections after move so cross-thread:
    thread.started.connect(checker.run)
    checker.signal.connect(self.checkedok)
    thread.start()

    self.threads.append(thread)

It is almost always a good idea to decorate slots with pyqtSlot so both run and checkedok should be thus decorated.

用pyqtSlot来装饰插槽几乎总是一个好主意,因此运行和checkedok都应该这样装饰。

The SO answer about Qt threads is quite handy to remind yourself of details (note however that it uses old-style connections -- you have to translate C++ connect( sender, SIGNAL(sig), receiver, SLOT(slot)); to PyQt5 sender.sig.connect(receiver.slot)).

关于Qt线程的SO答案可以很方便地提醒您详细信息(但是要注意它使用老式的连接——您必须转换c++连接(发送方、信号(sig)、接收方、插槽(槽)));对PyQt5 sender.sig.connect(receiver.slot))。

#3


0  

Sorry for late answer but it is a technique that can solve similar problems.

很抱歉,这是一个可以解决类似问题的技术。

The problem is clear. The GUI freezes because its thread has to do another job. An abstracted(from the PyQt point) solution is given below:

这个问题是显而易见的。GUI冻结是因为它的线程必须做另一项工作。一个抽象的(从PyQt点)解决方案给出如下:

  1. Create a class inheriting from threading.Thread which will be the worker.
  2. 创建从线程继承的类。线程将是工作人员。
  3. Pass to the constructor a queue(queue.Queue) as a means of communication.
  4. 将队列(queue. queue)作为通信手段传递给构造函数。
  5. You can start the worker thread from the GUI thread and pass messages by using the queue.
  6. 您可以从GUI线程启动工作线程,并通过使用队列传递消息。
  7. To make the GUI thread read the messages, create a QTimer with interval of your choice and register a callback function. In the callback function read the queue.
  8. 要使GUI线程读取消息,可以使用您选择的间隔创建一个QTimer,并注册一个回调函数。在回调函数中读取队列。

Example Code:

示例代码:

class Worker(threading.Thread):

    def __init__(self, queue):
        super().init()
        self.queue = queue

    def run(self):
         # Your code that uses self.queue.put(object)

class Gui:

    def __init__(self):
        self.timer = Qtimer()
        self.timer.setInterval(milliseconds)
        self.timer.timeout.connect(self.read_data)


    def start_worker(self):
        self.queue = queue.Queue()

        thr = Worker(self.queue)

        thr.start()


    def read_data(self):
        data = self.queue.get()

self.timer.timeout.connect registers the callback function.

self.timer.timeout。connect注册回调函数。

#1


26  

Since there are often questions about using QThread in PyQt, similar to yours, here is an example that shows how to correctly use threads in PyQt. I'm hoping it can be useful as a goto-answer for similar questions so I spent a bit more time than usual preparing this.

由于在PyQt中使用QThread经常会遇到类似于您的问题,这里有一个示例展示了如何在PyQt中正确使用线程。我希望它能成为类似问题的有效答案,所以我花了比平时多一点的时间准备这个问题。

The example creates a number of worker objects that execute in non-main threads and communicate with the main (ie GUI) thread via Qt's asynchronous signals.

这个示例创建了许多工作对象,它们在非主线程中执行,并通过Qt的异步信号与主(ie GUI)线程通信。

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_())

The main concepts necessary to understand multi-thread programming in PyQt are the following:

理解PyQt中多线程编程所需的主要概念如下:

  • Qt threads have their own event loop (specific to each thread). The main thread, aka the GUI thread, is also a QThread, and its event loop is managed by that thread.
  • Qt线程有自己的事件循环(特定于每个线程)。主线程,即GUI线程,也是一个QThread,它的事件循环由该线程管理。
  • Signals between threads are transmitted (asynchronously) via the receiving thread's event loop. Hence responsiveness of GUI or any thread = ability to process events. E.g., if a thread is busy in a function loop, it can't process events, so it won't respond to signals from the GUI until the function returns.
  • 线程之间的信号通过接收线程的事件循环(异步)传输。因此,GUI或任何线程都有响应能力来处理事件。例:如果一个线程在函数循环中很忙,那么它就不能处理事件,所以在函数返回之前,它不会对来自GUI的信号做出响应。
  • If a worker object (method) in a thread may have to change its course of action based on signals from the GUI (say, to interrupt a loop or a wait), it must call processEvents() on the QApplication instance. This will allow the QThread to process events, and hence to call slots in response to async signals from the GUI. Note that QApplication.instance().processEvents() seems to call processEvents() on every thread, if this is not desired then QThread.currentThread().processEvents() is a valid alternative.
  • 如果线程中的worker对象(方法)可能必须基于来自GUI的信号(例如,中断循环或等待)更改其操作过程,那么它必须在QApplication实例上调用processEvents()。这将允许QThread处理事件,并因此调用slot以响应GUI的异步信号。注意,QApplication.instance().processEvents()似乎在每个线程上都调用processEvents(),如果不需要这样做,那么QThread.currentThread().processEvents()是一个有效的替代方法。
  • A call to QThread.quit() does not immediately quit its event loop: it must wait for currently executing slot (if any) to return. Hence once a thread is told to quit, you must wait() on it. So aborting a worker thread usually involves signaling it (via a custom signal) to stop whatever it is doing: this requires a custom signal on a GUI object, a connection of that signal to a worker slot, and worker work method must call thread's processEvents() to allow the emitted signal to reach the slot while doing work.
  • 对QThread.quit()的调用不会立即退出它的事件循环:它必须等待当前执行的slot(如果有的话)返回。因此,一旦线程被告知要退出,您必须等待()。所以堕胎工作线程通常涉及到信号(通过自定义信号)停止正在做的一切:这需要一个自定义的信号一个GUI对象,连接的信号到一个工人槽,和工人工作方法必须调用线程的processEvents()允许发射信号到达槽而工作。

#2


1  

I can't test because setimap is not available on my system. I renamed CheckerThread to Checker since it is no longer a thread (it just "lives" in a thread):

我不能测试,因为我的系统上没有setimap。我将CheckerThread重命名为Checker,因为它不再是一个线程(它只是在一个线程中“生存”):

class Checker(QtCore.QObject):

Then just replace the contents of the loop in gogogo(self) with this:

然后将gogogogo (self)中的循环内容替换为:

for lignesmailtocheck in sorted(setmailtocheck):
    checker = Checker(lignesmailtocheck)

    thread = QThread()
    checker.moveToThread(thread)
    # connections after move so cross-thread:
    thread.started.connect(checker.run)
    checker.signal.connect(self.checkedok)
    thread.start()

    self.threads.append(thread)

It is almost always a good idea to decorate slots with pyqtSlot so both run and checkedok should be thus decorated.

用pyqtSlot来装饰插槽几乎总是一个好主意,因此运行和checkedok都应该这样装饰。

The SO answer about Qt threads is quite handy to remind yourself of details (note however that it uses old-style connections -- you have to translate C++ connect( sender, SIGNAL(sig), receiver, SLOT(slot)); to PyQt5 sender.sig.connect(receiver.slot)).

关于Qt线程的SO答案可以很方便地提醒您详细信息(但是要注意它使用老式的连接——您必须转换c++连接(发送方、信号(sig)、接收方、插槽(槽)));对PyQt5 sender.sig.connect(receiver.slot))。

#3


0  

Sorry for late answer but it is a technique that can solve similar problems.

很抱歉,这是一个可以解决类似问题的技术。

The problem is clear. The GUI freezes because its thread has to do another job. An abstracted(from the PyQt point) solution is given below:

这个问题是显而易见的。GUI冻结是因为它的线程必须做另一项工作。一个抽象的(从PyQt点)解决方案给出如下:

  1. Create a class inheriting from threading.Thread which will be the worker.
  2. 创建从线程继承的类。线程将是工作人员。
  3. Pass to the constructor a queue(queue.Queue) as a means of communication.
  4. 将队列(queue. queue)作为通信手段传递给构造函数。
  5. You can start the worker thread from the GUI thread and pass messages by using the queue.
  6. 您可以从GUI线程启动工作线程,并通过使用队列传递消息。
  7. To make the GUI thread read the messages, create a QTimer with interval of your choice and register a callback function. In the callback function read the queue.
  8. 要使GUI线程读取消息,可以使用您选择的间隔创建一个QTimer,并注册一个回调函数。在回调函数中读取队列。

Example Code:

示例代码:

class Worker(threading.Thread):

    def __init__(self, queue):
        super().init()
        self.queue = queue

    def run(self):
         # Your code that uses self.queue.put(object)

class Gui:

    def __init__(self):
        self.timer = Qtimer()
        self.timer.setInterval(milliseconds)
        self.timer.timeout.connect(self.read_data)


    def start_worker(self):
        self.queue = queue.Queue()

        thr = Worker(self.queue)

        thr.start()


    def read_data(self):
        data = self.queue.get()

self.timer.timeout.connect registers the callback function.

self.timer.timeout。connect注册回调函数。