PyQT与线程

1 投票
3 回答
7956 浏览
提问于 2025-04-16 04:52

我正在开发一个应用程序,这个程序会用多个线程从各种网络设备收集数据。我使用PyQT来在图形界面上显示收集到的数据。在我的应用中,我使用的是普通的Python线程(来自thread和threading),而不是QThread。为了在不同的线程中更新图形界面,我使用了一个锁(thread.allocate_lock())。所以,每当需要更新图形界面时,我就会先获取这个锁,然后再更新界面。这样做有什么需要注意的吗?

3 个回答

-1

我在使用pyqtSignal和Python的线程功能。你可以创建线程,当线程完成时,它会发送一个信号来更新你的图形界面(GUI)。

2

这是个晚回复,但我想分享我找到的东西。这段代码来自WickedDevice博客,我觉得它对理解线程和PyQt很有帮助:

#authors: Dirk Swart, Doudewijn Rempt, Jacob Hallen

import sys, time, threading, random, Queue
from PyQt4 import QtGui, QtCore as qt
import serial

SERIALPORT = 'COM6'

class GuiPart(QtGui.QMainWindow):

    def __init__(self, queue, endcommand, *args):
        QtGui.QMainWindow.__init__(self, *args)
        self.setWindowTitle('Arduino Serial Demo')
        self.queue = queue
        # We show the result of the thread in the gui, instead of the console
        self.editor = QtGui.QTextEdit(self)
        self.setCentralWidget(self.editor)
        self.endcommand = endcommand    

    def closeEvent(self, ev):
        self.endcommand()

    def processIncoming(self):
        """
        Handle all the messages currently in the queue (if any).
        """
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                # Check contents of message and do what it says
                # As a test, we simply print it
                self.editor.insertPlainText(str(msg))
            except Queue.Empty:
                pass

class ThreadedClient:
    """
    Launch the main part of the GUI and the worker thread. periodicCall and
    endApplication could reside in the GUI part, but putting them here
    means that you have all the thread controls in a single place.
    """
    def __init__(self):
        # Create the queue
        self.queue = Queue.Queue()

        # Set up the GUI part
        self.gui=GuiPart(self.queue, self.endApplication)
        self.gui.show()

        # A timer to periodically call periodicCall :-)
        self.timer = qt.QTimer()
        qt.QObject.connect(self.timer,
                           qt.SIGNAL("timeout()"),
                           self.periodicCall)
        # Start the timer -- this replaces the initial call to periodicCall
        self.timer.start(100)

        # Set up the thread to do asynchronous I/O
        # More can be made if necessary
        self.running = 1
        self.thread1 = threading.Thread(target=self.workerThread1)
        self.thread1.start()

    def periodicCall(self):
        """
        Check every 100 ms if there is something new in the queue.
        """
        self.gui.processIncoming()
        if not self.running:
            root.quit()

    def endApplication(self):
        self.running = 0

    def workerThread1(self):
        """
        This is where we handle the asynchronous I/O. 
        Put your stuff here.
        """
        while self.running:
            #This is where we poll the Serial port. 
            #time.sleep(rand.random() * 0.3)
            #msg = rand.random()
            #self.queue.put(msg)
            ser = serial.Serial(SERIALPORT, 115200)
            msg = ser.readline();
            if (msg):
                self.queue.put(msg)
            else: pass  
            ser.close()



if __name__ == "__main__":
    #rand = random.Random()
    root = QtGui.QApplication(sys.argv)
    client = ThreadedClient()
    sys.exit(app.exec_())
3

我很确定在Qt中,从不同的线程更新图形界面是很危险的,即使你在自己的代码中尝试加锁。因为Qt可能会在主线程中处理自己的事件,而它不会去获取你的锁来保护它可能会修改的对象。在Qt文档的这一页中,明确提到QWidget不是可重入的,也不是线程安全的。

我建议你把收集到的数据,或者处理过的数据,发送回主线程。可以使用排队的信号/槽连接,或者自定义QEventQApplication::postEvent来实现。在jkerian提到的前一个问题中,提到如果你想让事件发送正常工作,就必须使用QThread,而不是Python的线程。

撰写回答