暂停工作线程并等待主线程发送事件

5
我们有一个执行不同查询的应用程序。它启动了最多四个线程,并在其中运行提取操作。
代码如下:
```python # 开始执行查询 for i in range(4): t = Thread(target=execute_query) t.start() ```
以上是指定四个线程来执行查询的部分代码。
    if len(self.threads) == 4:
        self.__maxThreadsMsg(base)
        return False
    else:
        self.threads.append(Extractor(self.ui, base))
        self.threads[-1].start()
        self.__extractionMsg(base)
        return True

我们的Extractor类继承了QThread类:
class Extractor(QThread):
    def init(self, ui, base):
        QThread.__init__(self)
        self.ui = ui
        self.base = base

    def run(self):
        self.run_base(base)

并且self.ui被设置为Ui_MainWindow()

class Cont(QMainWindow):
    def __init__(self, parent=None):
        QWidget.__init__(self,parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

有一个特定的基地会在继续之前向用户发送数据(返回到主窗口),在这种情况下,是一个带有两个按钮的弹出窗口:

#This code is in the main file inside a method, not in the Extractor class
msg_box = QMessagebox()
msg_box.setText('Quantity in base: '.format(n))
msg_box.setInformativeText('Would you like to continue?')
msg_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
signal = msg_box.exec_()

如何在某个特定点暂停线程,显示窗口(我相信这将返回到主线程),然后返回到工作线程,传递单击的按钮事件?我读了一些关于信号的内容,但由于这是我第一次处理线程,所以似乎有点困惑。
编辑:阅读了这个问题:类似的问题之后,我将代码修改为以下内容:
Cont类中的一个方法上
thread = QThread(self)
worker = Worker()

worker.moveToThread(thread)
worker.bv.connect(self.bv_test)

thread.started.connect(worker.process()) # This, unlike in the linked question.. 
#doesn't work if I remove the parentheses of the process function. 
#If I remove it, nothing happens and I get QThread: "Destroyed while thread is still running"

thread.start()

@pyqtSlot(int)
def bv_test(self, n):
    k = QMessageBox()
    k.setText('Quantity: {}'.format(n))
    k.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
    ret = k.exec_()
    return ret

这是 Worker 类:

class Worker(QObject):

    #Signals
    bv = pyqtSignal(int)

    def process(self):
        self.bv.emit(99)

现在我只需要弄清楚如何将ret值发送回工作线程,以便启动第二个进程。我还一直遇到这个错误:

TypeError: connect()插槽参数应该是可调用的或信号,而不是'NoneType'


2个回答

6
以下是一个简单的演示,基于你提问中的代码实现了你所需的功能。实际上,关于它并没有太多可说的,除了你需要通过信号(双向)在工作线程和主线程之间进行通信。finished 信号用于退出线程,这将停止显示警告信息 QThread: "Destroyed while thread is still running"
你看到这个错误的原因是:
TypeError: connect() slot argument should be a callable or a signal, not `NoneType'

这是因为您尝试将一个信号与函数的返回值(即None)连接起来,而不是函数对象本身。您必须始终向connect方法传递Python可调用对象,否则会引发TypeError异常。

请运行下面的脚本,并确认它按预期工作。希望您能很容易地看到如何使其适应您的实际代码。

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class Cont(QWidget):
    confirmed = pyqtSignal()

    def __init__(self):
        super(Cont, self).__init__()
        self.thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.thread)
        self.worker.bv.connect(self.bv_test)
        self.worker.finished.connect(self.thread.quit)
        self.confirmed.connect(self.worker.process_two)
        self.thread.started.connect(self.worker.process_one)
        self.thread.start()

    def bv_test(self, n):
        k = QMessageBox(self)
        k.setAttribute(Qt.WA_DeleteOnClose)
        k.setText('Quantity: {}'.format(n))
        k.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        if k.exec_() == QMessageBox.Yes:
            self.confirmed.emit()
        else:
            self.thread.quit()

class Worker(QObject):
    bv = pyqtSignal(int)
    finished = pyqtSignal()

    def process_two(self):
        print('process: two: started')
        QThread.sleep(1)
        print('process: two: finished')
        self.finished.emit()

    def process_one(self):
        print('process: one: started')
        QThread.sleep(1)
        self.bv.emit(99)
        print('process: one: finished')

app = QApplication([''])
win = Cont()
win.setGeometry(100, 100, 100, 100)
win.show()
app.exec_()

2
如果您希望线程等待某个操作完成,请使用线程连接信号。
PyQt4.QtCore.Qt.BlockingQueuedConnection

作为标志。

现在我不明白为什么你需要线程,如果你让它们等待,这会带来很多复杂性。对我来说,更好的解决方案是将你想要在线程中执行的任务分成更小的部分。每次当一个部分准备好时,你可以询问用户是否也想要下一个部分。


只有一个特定的提取需要确认,当程序编写时,我们没有这个要求。 - Onilol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接