Python PyQt4在线程中等待来自GUI的用户输入

Python PyQt4在线程中等待来自GUI的用户输入,python,multithreading,python-3.x,pyqt,pyqt4,Python,Multithreading,Python 3.x,Pyqt,Pyqt4,我有一个线程类“MyThread”和我的主应用程序,它被简单地称为“Gui”。我想从thread类创建几个对象,但在本例中,我只创建了一个对象。thread类完成一些工作,然后向Gui类发出一个信号,指示需要用户输入(目前的指示只是更改按钮的文本)。然后线程应该等待用户输入(在本例中是单击按钮),然后继续执行它正在执行的操作 从PyQt4导入QtGui,QtCore 类MyTrhead(QtCore.QThread): 触发器=QtCore.pyqtSignal(str) def运行(自): 打

我有一个线程类“MyThread”和我的主应用程序,它被简单地称为“Gui”。我想从thread类创建几个对象,但在本例中,我只创建了一个对象。thread类完成一些工作,然后向Gui类发出一个信号,指示需要用户输入(目前的指示只是更改按钮的文本)。然后线程应该等待用户输入(在本例中是单击按钮),然后继续执行它正在执行的操作

从PyQt4导入QtGui,QtCore
类MyTrhead(QtCore.QThread):
触发器=QtCore.pyqtSignal(str)
def运行(自):
打印(self.currentThreadId())
对于范围(0,10)内的i:
打印(“工作”)
self.trigger.emit(“3+{}=?”。格式(i))
####等待结果
时间。睡眠(1)
类Gui(QMainWindow、Ui\u MainWindow):
def uuu init uuu(self,parent=None):
超级(图形用户界面,自我)。\uuuuu初始化\uuuuuuu(父级)
self.setupUi(self)
self.button.clicked.connect(self.btn)
self.t1=MyTrhead()
self.t1.trigger.connect(self.dispaly_消息)
self.t1.start()
打印(“线程:{}”。格式(self.t1.isRunning())
@QtCore.pyqtSlot(str)
def dispaly_消息(self,mystr):
self.butdown.setText(mystr)
def btn(自身):
打印(“将结果返回到相应的线程”)
如果“\uuuu main\uuuuuu”==\uuuuuuuu name\uuuuuuu:
导入系统
app=QtGui.QApplication(sys.argv)
m=Gui()
m、 show()
sys.exit(app.exec_())

如何在(多个)线程中等待用户输入?

默认情况下,
QThread
有一个可以处理信号和插槽的事件循环。在当前的实现中,您很不幸地通过重写
QThread.run
删除了此行为。如果你恢复它,你可以得到你想要的行为

所以,如果您不能覆盖
QThread.run()
,那么如何在Qt中执行线程?线程的另一种方法是将代码放入
QObject
的子类中,并将该对象移动到标准
QThread
实例中。然后,您可以将主线程和
QThread
之间的信号和插槽连接在一起,以便在两个方向上进行通信。这将允许您实现所需的行为

在下面的示例中,我启动了一个工作线程,它打印到终端,等待2秒钟,再次打印,然后等待用户输入。单击按钮时,辅助线程中的第二个单独函数将运行,并以与第一次相同的模式打印到终端。请注意我使用
moveToThread()
并连接信号的顺序(根据)

代码:

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

class MyWorker(QObject):

    wait_for_input = pyqtSignal()
    done = pyqtSignal()


    @pyqtSlot()
    def firstWork(self):
        print 'doing first work'
        time.sleep(2)
        print 'first work done'
        self.wait_for_input.emit()

    @pyqtSlot()
    def secondWork(self):
        print 'doing second work'
        time.sleep(2)
        print 'second work done'
        self.done.emit()


class Window(QWidget):
    def __init__(self, parent = None):
        super(Window, self).__init__()

        self.initUi()
        self.setupThread()

    def initUi(self):
        layout = QVBoxLayout()
        self.button = QPushButton('User input')
        self.button.setEnabled(False)
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.show()

    @pyqtSlot()
    def enableButton(self):
        self.button.setEnabled(True)

    @pyqtSlot()    
    def done(self):
        self.button.setEnabled(False)

    def setupThread(self):
        self.thread = QThread()
        self.worker = MyWorker()

        self.worker.moveToThread(self.thread)

        self.thread.started.connect(self.worker.firstWork)
        self.button.clicked.connect(self.worker.secondWork)
        self.worker.wait_for_input.connect(self.enableButton)
        self.worker.done.connect(self.done)

        # Start thread
        self.thread.start()    

if __name__ == "__main__":
    app = QApplication([])
    w = Window()
    app.exec_()

感谢您的帮助,还有一个问题:如果我有多个工作线程,如何将参数从函数firstWork传递到GUI并将结果传递回secondWork?想想看,函数firstWork会问一个问题,将其传递给用户,然后第二个工作会得到答案?将插槽装饰符更改为
@pyqtSlot()
,后跟表示插槽的函数的相同签名
def firstWork(self,)
。例如
@pyqtSlot(int,str)
意味着插槽需要两个参数(一个是
int
,另一个是
str
类型)。插槽装饰器所属的函数应类似于
def firstWork(self,x,y)
,其中
x
int
,而
y
为信号传输的
str
数据段。您还必须更改该插槽接收到的信号:
mySignal=pyqtSignal(int,str)
@rbaleksandar进行连接没有多大意义,除非您的线程正在实际完成(在本例中,这需要显式调用
QThread.quit()
,以结束事件循环)@Maecky您完全可以拥有多个工作线程。只需按照我演示的方法创建它们,并根据需要连接信号/插槽。您可以任意组合拥有任意多个
QThread
实例和
QObject
子类,并在它们之间建立连接。只需小心,如果3个事件循环一致,互操作可能是有挑战性的,如果你不计划好。@ TueSuxPiple不知道它是如何在PyQt,如果内部它得到照顾,但在Qt C++中,当你不清理你的对象,也叫退出线程,你得到警告,一旦你停止你的应用程序,A线程过早地被破坏。我也不确定是否将动态分配的
QObject
(即使它是PyQt,我们有C++)移动到
QThread
也会使
QThread
父对象移动到该对象。如果不是这样,我们会得到内存泄漏(再次——下面的C++)。