Python Can';无法从线程显示窗口

Python Can';无法从线程显示窗口,python,multithreading,pyqt,pyqt4,Python,Multithreading,Pyqt,Pyqt4,我有几个线程需要使用Windows。以下是线程定义: class MyThread(QtCore.QThread): def __init__(self, id, window, mutex): super(MyThread, self).__init__() self.id = id self.window = window self.mutex = mutex self.connect(self, QtC

我有几个线程需要使用Windows。以下是线程定义:

class MyThread(QtCore.QThread):
    def __init__(self, id, window, mutex):
        super(MyThread, self).__init__()
        self.id = id
        self.window = window
        self.mutex = mutex
        self.connect(self, QtCore.SIGNAL("load_message_input()"), self.window, QtCore.SLOT("show_input()"))

    def run(self):
        self.mutex.lock()
        self.emit(QtCore.SIGNAL("load_message_input()"))
        self.connect(self.window, QtCore.SIGNAL("got_message(QString)"), self.print_message)
        self.window.input_finished.wait(self.mutex)
        self.mutex.unlock()

    def print_message(self, str):
        print "Thread %d: %s" % (self.id, str)
class MyDialog(QtGui.QDialog):
    def __init__(self, *args, **kwargs):
        super(MyDialog, self).__init__(*args, **kwargs)
        self.last_message = None

        self.setModal(True)
        self.message_label = QtGui.QLabel(u"Message")
        self.message_input = QtGui.QLineEdit()
        self.dialog_buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
        self.dialog_buttons.accepted.connect(self.accept)
        self.dialog_buttons.rejected.connect(self.reject)
        self.hbox = QtGui.QHBoxLayout()
        self.hbox.addWidget(self.message_label)
        self.hbox.addWidget(self.message_input)
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addLayout(self.hbox)
        self.vbox.addWidget(self.dialog_buttons)
        self.setLayout(self.vbox)

        self.input_finished = QtCore.QWaitCondition()

    @QtCore.pyqtSlot()
    def show_input(self):
        self.exec_()

    def on_accepted(self):
        self.emit(QtCore.SIGNAL("got_message(QString)"), self.message_input.text())
        self.input_finished.wakeOne()
下面是窗口定义:

class MyThread(QtCore.QThread):
    def __init__(self, id, window, mutex):
        super(MyThread, self).__init__()
        self.id = id
        self.window = window
        self.mutex = mutex
        self.connect(self, QtCore.SIGNAL("load_message_input()"), self.window, QtCore.SLOT("show_input()"))

    def run(self):
        self.mutex.lock()
        self.emit(QtCore.SIGNAL("load_message_input()"))
        self.connect(self.window, QtCore.SIGNAL("got_message(QString)"), self.print_message)
        self.window.input_finished.wait(self.mutex)
        self.mutex.unlock()

    def print_message(self, str):
        print "Thread %d: %s" % (self.id, str)
class MyDialog(QtGui.QDialog):
    def __init__(self, *args, **kwargs):
        super(MyDialog, self).__init__(*args, **kwargs)
        self.last_message = None

        self.setModal(True)
        self.message_label = QtGui.QLabel(u"Message")
        self.message_input = QtGui.QLineEdit()
        self.dialog_buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
        self.dialog_buttons.accepted.connect(self.accept)
        self.dialog_buttons.rejected.connect(self.reject)
        self.hbox = QtGui.QHBoxLayout()
        self.hbox.addWidget(self.message_label)
        self.hbox.addWidget(self.message_input)
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addLayout(self.hbox)
        self.vbox.addWidget(self.dialog_buttons)
        self.setLayout(self.vbox)

        self.input_finished = QtCore.QWaitCondition()

    @QtCore.pyqtSlot()
    def show_input(self):
        self.exec_()

    def on_accepted(self):
        self.emit(QtCore.SIGNAL("got_message(QString)"), self.message_input.text())
        self.input_finished.wakeOne()
以下是主要内容:

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)

    mutex = QtCore.QMutex()
    threads = []
    window = test_qdialog.MyDialog()

    for i in range(5):
        thread = MyThread(i, window, mutex)
        thread.start()
        threads.append(thread)

    for t in threads:
        t.wait()

    sys.exit(app.exec_())
我不明白为什么在执行脚本时不显示窗口

更新
由于某些原因,其他线程不会使用
self.mutex.lock()
联机停止。无法找出原因。

您的代码中有几个问题:

  • 如果希望
    QThread
    使用插槽,则需要为其创建一个事件循环(这很简单,只需调用
    QThread.exec
    ),但是带有事件循环的
    QThread
    需要进行不同的编码(接下来我将为您提供一个示例)
  • 如果要发出消息,需要将_accepted上的
    连接到
    accepted
    ,除非使用Qt的自动连接功能
  • 如果您想首先使用
    QThread
    ,您需要启动
    QApplication
    ,因此
    对于线程中的t:t.wait()
    不能在调用
    QApplication.exec
    之前执行(在我的示例中,刚刚删除了它)
  • 最后一个但同样重要的问题是:如果您希望您的线程以独占方式使用资源,那么您应该考虑消费者-生产者方法(问题是,当您发出信号时,每个插槽将获得数据的一个副本,如果您尝试使用事件循环阻止线程,应用程序将冻结,为了解决消费者生产者问题,我向消息信号传递一个额外的互斥锁,并尝试锁定它[从不阻塞!],以了解线程是否消费该事件)
如前所述,这里有一个如何在
QThread
s上使用事件循环的示例:

from PyQt4 import QtCore, QtGui

class MyThread(QtCore.QThread):

    load_message_input = QtCore.pyqtSignal()

    def __init__(self, id, window):
        super(MyThread, self).__init__()
        self.id = id
        self.window = window
        self.load_message_input.connect(self.window.show_input)
        self.window.got_message.connect(self.print_message)
        self.started.connect(self.do_stuff)

    def run(self):
        print "Thread %d: %s" % (self.id,"running")
        self.exec_()

    @QtCore.pyqtSlot() 
    def do_stuff(self):
        print "Thread %d: %s" % (self.id,"emit load_message_input")
        self.load_message_input.emit()

    @QtCore.pyqtSlot("QString","QMutex")
    def print_message(self, msg, mutex):
        if mutex.tryLock():
            print "Thread %d: %s" % (self.id, msg)
        self.do_stuff()


class MyDialog(QtGui.QDialog):

    got_message = QtCore.pyqtSignal("QString","QMutex")    

    def __init__(self, *args, **kwargs):
        super(MyDialog, self).__init__(*args, **kwargs)
        self.last_message = None

        self.setModal(True)
        self.message_label = QtGui.QLabel(u"Message")
        self.message_input = QtGui.QLineEdit()
        self.dialog_buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
        self.dialog_buttons.accepted.connect(self.accept)
        self.dialog_buttons.accepted.connect(self.on_accepted)
        self.dialog_buttons.rejected.connect(self.reject)
        self.hbox = QtGui.QHBoxLayout()
        self.hbox.addWidget(self.message_label)
        self.hbox.addWidget(self.message_input)
        self.vbox = QtGui.QVBoxLayout()
        self.vbox.addLayout(self.hbox)
        self.vbox.addWidget(self.dialog_buttons)
        self.setLayout(self.vbox)

        self.input_finished = QtCore.QWaitCondition()   


    @QtCore.pyqtSlot()
    def show_input(self):
        print "showing input"
        window.show()
        window.setModal(True)    


    @QtCore.pyqtSlot()
    def on_accepted(self):
        print "emit: ", self.message_input.text()
        self.got_message.emit(self.message_input.text(), QtCore.QMutex())

if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)

    mutex = QtCore.QMutex()
    threads = []
    window = MyDialog()

    for i in range(5):
        thread = MyThread(i, window)
        thread.start()
        threads.append(thread)

    print "start app"
    sys.exit(app.exec_())
注意:首先接收信号的线程几乎总是id为1的线程


我的建议是,不要在线程中使用插槽(这将确保互斥和等待条件的安全使用),并对消息实施消费者-生产者方法。

在调用app.exec_389;()之前,您正在等待线程退出。您可能应该监视GUI空闲循环中的线程,或者连接到线程的finished()信号。

有几件事我仍然不明白。首先,为什么你要从
print\u message
再次调用
do\u stuff
。其次,为什么你要从QDialog传输QMutex?我想每次QDialog发出这样的信号QMutex对象都会不同。我再次调用
do\u stuff
来提供一个处理空间p、 如果希望线程只运行一次,您可以安全地删除它。互斥对象用于在处理消息时排除:只有调用
QMutex的第一个线程。tryLock
获得
True
的值,因此只有一个线程使用消息(我认为这是您想要的).这里的问题是,请求窗口显示的同一线程必须获取消息。