【问题标题】:Can't show window from thread无法从线程显示窗口
【发布时间】:2014-02-26 20:11:33
【问题描述】:

我有几个线程需要使用window。这是线程定义:

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() 在线停止。不知道为什么。

【问题讨论】:

    标签: python multithreading pyqt pyqt4


    【解决方案1】:

    您的代码中有几个问题:

    • 如果你想让QThread 使用槽,你需要为它创建一个事件循环(这很简单,只需调用QThread.exec_),但QThreads 需要有事件循环以不同的方式编码(接下来我会给你一个例子)
    • 如果你想发送消息,你需要将on_accepted连接到accepted,除非你使用Qt的自动连接功能。
    • 如果你想首先使用QThread,你需要启动一个QApplication,所以for t in threads: t.wait()不能在调用QApplication.exec_之前被执行(在我的例子中只是删除它)。
    • 最后一个但同样重要的问题:如果您希望您的线程专门消耗资源,您应该考虑一种消费者-生产者方法(问题是当您发出信号时,每个插槽都会获得数据的副本,如果您尝试使用应用程序冻结的事件循环来阻塞线程,为了解决消费者-生产者的问题,我将一个额外的互斥锁传递给消息的信号并 try 将其锁定 [从不阻塞! ] 以了解线程是否消耗事件)

    正如所承诺的,有一个如何在QThreads 上使用事件循环的示例:

    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 的线程。

    我的建议是,不要在线程中使用槽(这样可以安全地使用互斥锁和等待条件),并为消息实现消费者-生产者方法。

    【讨论】:

    • 有几件事我还是不明白。首先,为什么要从print_message 再次调用do_stuff。其次,为什么要从 QDialog 传输 QMutex?我认为每次 QDialog 发出这样的信号时,QMutex 对象都会有所不同。
    • 我再次调用do_stuff 来提供一个处理循环,如果您希望线程只运行一次,您可以安全地删除它。互斥对象用于在处理消息时排除:只有第一个调用QMutex.tryLock 的线程获得True 的值,所以只有一个线程使用消息(我认为这是你想要的)。
    • 这里的问题是请求窗口显示的同一个线程必须得到消息。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-02
      • 2021-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      • 2013-09-15
      相关资源
      最近更新 更多