PyQt:如何从GUI向线程发送信息

3

我的HMI由两个按钮组成:启动和停止,还有一个QStatusBar字段,用于将线程中的信息打印到GUI上。当我点击“开始”时,我会启动一个线程,可以使用停止按钮来中断该线程。 我想做的是在线程的某一点处打开一个弹出窗口,在线程暂停时给用户选择。

from PyQt4 import QtCore, QtGui
import sys
import os
import time
from PyQt4.QtGui import QApplication, QDialog, QListWidgetItem, QListWidget, QIcon
from test_bench_tool2 import Ui_MainWindow
from popup import Ui_popup

"""Class poppup window(continue/quit)"""
class MyPopup(QtGui.QDialog):
    def __init__(self,type):
        super(MyPopup, self).__init__()
        self.res = 0
        self.type=type
        self.ui = Ui_popup()
        self.ui.setupUi(self)
        QtCore.QObject.connect(self.ui.Quit,QtCore.SIGNAL("clicked()"),self.showDialogstop)
       QtCore.QObject.connect(self.ui.Continue,QtCore.SIGNAL("clicked()"),self.showDialogcontinue)

    def showDialogcontinue(self):
        self.res=1
        self.close()
    def showDialogstop(self):
        self.res=0
        self.close()


class MyThread(QtCore.QThread):
    Statuschanged = QtCore.pyqtSignal(str)
    popupmodechanged = QtCore.pyqtSignal(str)
    def __init__(self, parent=None):
        super(MyThread, self).__init__(parent=parent)
        self.Status_auto = ''
        self.dialog = MyPopup('toto')
        self.Status_auto +=''
        self.popup=''
        self.answer=''

    def run(self):
        result = self.get_result()

    def get_result(self, ):
        empty_result = []
        self.popup='Vn'
        self.popupmodechanged.emit((self.popup))
        self.Status_auto +='\n\nMeasures finished!!'
        self.Statuschanged.emit((self.Status_auto))
        results=[]
        #do things to calculate results
        return(results)


class Main(QtGui.QDialog):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        # Connect the Buttons
        QtCore.QObject.connect(self.ui.Start,QtCore.SIGNAL("clicked()"),self.Start)
        QtCore.QObject.connect(self.ui.Stop,QtCore.SIGNAL("clicked()"),self.Stop)
        self.__thread = MyThread(self)
        self.__thread.Statuschanged.connect(self.ui.report_auto.setText)
        self.__thread.popupmodechanged.connect(self.open_popup)

    def open_popup(self):
        self.__thread.dialog.__init__(self.__thread.dialog.type)
        self.__thread.dialog.exec_()

    def Start(self):
        global tableRx, tableTx
        self.ui.report_auto.setText('test in process...')
        self.__thread.start()

    def  Stop(self):
        self.ui.report_auto.setText('test canceled')
        if self.__thread.isRunning():
            self.__thread.terminate()

def main():

    app = QtGui.QApplication(sys.argv)
    window = Main()

    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

我该如何获取弹出窗口中的选择结果以继续线程?我尝试在get_result()函数中添加如下测试:
if self.dialog.res == 1:
            print "continue"
            self.dialog.close()
            #do things
        else:
            self.dialog.close()

但是我无法从GUI中获取结果到线程中。我该如何将信息从GUI传递给我的线程??我需要使用队列吗?

1
不应该在单独的QThread中创建GUI(这样做时应该会看到警告)。相反,应该在QThread中进行计算,等待线程完成,然后从主GUI线程打开弹出窗口。 - sebastian
抱歉,但这不是我想要做的,弹出窗口必须出现在线程的中间,但还是谢谢你的建议。 - paul marguerite
1个回答

7

在我的经验中,队列是最好的解决方案。它是一种线程安全的方式,在线程之间共享信息。您应该在主类中实例化队列,并将其作为初始化参数传递给线程:

#MyThread init
def __init__(self, queue, parent=None):
    #the input queue
    self.in_queue = queue 
    #flag indicating if the thread's run function should actually run
    self.running = True 
    super(MyThread, self).__init__(parent=parent)
    self.Status_auto = ''
    self.dialog = MyPopup('toto')
    self.Status_auto +=''
    self.popup=''
    self.answer=''

使用队列,您的线程工作函数应该如下所示:
def run(self)
    while self.running: #to keep the thread running
        if not self.in_queue.empty():
            msg = self.in_queue.get()
            #do something according to the message
        else:
            #do regular operation

这也允许您使用self.queue.put(value)在消息队列中放置'STOP'消息或其他内容。 请注意,在这种情况下,队列的get方法将阻塞线程,直到它能够从队列中实际获取某些内容(因此需要if语句)。您可以更改此行为:

msg = self.in_queue.get(block = False)

如果队列为空,则会引发“Empty”异常。

访问队列类的方法:

from queue import Queue
queue = Queue()

除了上述解决方案之外,您还可以将对话框中的信号连接到线程中的事件处理程序,但我没有尝试过这种方式,也不确定它是否正常工作。

希望这能解决您的问题。

祝好, Mátyás


非常感谢你,Màtyàs,你可能救了我的命 :)。我需要尽快学习这个解决方案,并立即给你反馈。 - paul marguerite
抱歉,我已经尝试了多次,但仍然存在问题。你能继续帮助我吗?在我的线程中,我已经像你说的那样声明了self.in_queue = queue,但是当我在get_result()中使用它时,我遇到了这个疯狂的错误:Traceback (most recent call last): File "F:\auto_bench\materiel\try4.py", line 46, in run self.get_result() File "F:\auto_bench\materiel\try4.py", line 68, in get_result self.in_queue.put('True') AttributeError: 'Main' object has no attribute 'put' 而我确实在线程对象中?出了什么问题? - paul marguerite
确保 in_queue 实际上是 Queue 对象(只需打印它,你应该会看到类似 <queue.Queue object at _memoryaddress_> 的内容)。可能是参数名称造成了问题(因为 queue 也是导入的模块名称),尝试使用其他名称作为参数名称而不是 "queue"。还要检查在实例化线程时是否传递了正确的参数。似乎您颠倒了队列和父级参数的顺序:错误消息表明您正在尝试将某些内容放入类型为 Main 的对象中。 - Mátyás Kuti

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