无法显示线程中的窗口



我有几个线程需要与窗口一起使用。这是线程定义:

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使用插槽,则需要 create 一个事件循环(这很容易,只需致电QThread.exec_),但是带有事件循环的QThread S需要进行不同的编码(接下来我会向您发布一个例子)
  • 如果要发出消息,则需要将on_accepted连接到accepted,除非您使用QT的自动连接功能。
  • 如果您要使用QThread首先使用QApplication,因此无法在致电QApplication.exec_之前执行for t in threads: t.wait()(在我的示例中刚刚将其删除)。
  • 最后一个但同样重要的问题:如果您希望您的线程仅消费资源,则应该考虑一种消费者生产者的方法(问题是,当您发出信号时,每个插槽都会获得数据的副本,以及如果您尝试用事件循环阻止线程,应用程序刚刚冻结,为了解决消费者生产商的问题,我将额外的互音传递给消息的信号,然后 try 锁定它[永远不会阻止它!]知道线程是否消耗事件)

正如承诺的,有一个示例,说明如何在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_()之前正在等待线程退出。您可能应该在GUI空闲循环中监视螺纹,或连接到螺纹的完成()信号。

最新更新