在PyQT中使用QThread进行串行通信(w. pyseries)

时间:2021-09-24 23:00:02

I am pretty much a beginner when it comes to GUI programming. I am using QT in combination with python bindings (PyQT4).

在GUI编程方面,我是一个新手。我使用QT与python绑定(PyQT4)结合使用。

What I am trying to do:

我想做的是:

  • Setting up a QThread to read from & write to a Serial Port with pyserial.
  • 设置一个可读的QThread,并将其写入一个带有pyseries的串口。
  • The main application should be able to emit new serial data via a signal to the running QThread. and receive serial data from the QThread with a signal.
  • 主应用程序应该能够通过一个信号发送新的串行数据到运行的QThread。并通过一个信号接收来自QThread的串行数据。

I started my own test implementation based on this code (Link). Prior to this I read the basics about QThreads and tried to understand how they are intended to be used. The following test code is what I have come up with. I m sorry, I tried to keep it minmal, but it is still 75 lines of code:

我基于这个代码开始了自己的测试实现(Link)。在此之前,我阅读了关于QThreads的基础知识,并试图了解它们是如何被使用的。下面的测试代码是我想出来的。对不起,我试着让它保持原样,但它仍然是75行代码:

from PyQt4 import QtCore, QtGui
import time
import sys

class SerialData(QtCore.QObject):
    def __init__(self, message):
        super(SerialData, self).__init__()
        self.__m = message
    def getMsg(self):
        return self.__m

class SerialCon(QtCore.QObject):

    finished = QtCore.pyqtSignal()
    received = QtCore.pyqtSignal(SerialData)

    def init(self):
       super(SerialCon, self).__init__()
       # TODO setup serial connection:
       # setting up a timer to check periodically for new received serial data
       self.timer = QtCore.QTimer()
       self.timer.setInterval(400)
       self.timer.timeout.connect(self.readData)
       self.timer.start(200)
       # self.finished.emit()

    def readData(self):
       self.received.emit(SerialData("New serial data!"))
       print "-> serial.readLine() ..."

    @QtCore.pyqtSlot(SerialData)
    def writeData(self, data):
       print "-> serial.write(), ", data.getMsg()


class MyGui(QtGui.QWidget):
    serialWrite = QtCore.pyqtSignal(SerialData)

    def __init__(self):
       super(MyGui, self).__init__()
       self.initUI()

    def initUI(self):
       bSend = QtGui.QPushButton("Send",self)
       bSend.clicked.connect(self.sendData)
       self.show()

    @QtCore.pyqtSlot(SerialData)
    def updateData(self, data):
        print "Gui:", data.getMsg()

    def sendData(self, pressed):
       data = SerialData("Send me!")
       self.serialWrite.emit(data)

def usingMoveToThread():
    app = QtGui.QApplication(sys.argv)
    guui = MyGui()

    thread = QtCore.QThread()
    serialc = SerialCon()
    serialc.moveToThread(thread)

    # connecting signals to slots
    serialc.finished.connect(thread.quit)
    guui.serialWrite.connect(serialc.writeData)
    serialc.received.connect(guui.updateData)
    thread.started.connect(serialc.init)
    thread.finished.connect(app.exit)
    thread.start()

    sys.exit(app.exec_())

if __name__ == "__main__":
    usingMoveToThread()

My Problems:

我的问题:

  • In test code the signal emitted from the SerialCon object (which has been moved to the QThread) seems to be not received by the corresponding slot (in MyGui, updateData)

    在测试代码中,从SerialCon对象(已移动到QThread)发出的信号似乎没有被相应的槽接收(在MyGui中,updateData)

  • Sooner or later the running test code always causes a Segmentation fault (core dumped). Which makes me believe that I missed some important bits.

    运行的测试代码迟早会导致分割错误(核心转储)。这让我相信我漏掉了一些重要的信息。

What could cause this?

这可能会导致什么?

Maybe I m taking a completely wrong approach? - so if you have a better idea how to achieve this, I d be very grateful to hear about it!

也许我采取了完全错误的方法?-所以如果你有更好的方法来实现这个目标,我将非常感激!

Thanks a lot!

谢谢!

1 个解决方案

#1


0  

At first I was only focussing on the new way, how QThreads should be used since QT4 (Link), by creating a QObject, and then invoking moveToThread(), pretty much like in my first code sample (at least thats how I understood it). However I just could not figure out, why I was not able to pass signals from the QThread to the main application.

起初,我只关注新方法,如何使用QT4 (Link),创建一个QObject,然后调用moveToThread(),这与我的第一个代码示例非常相似(至少我是这么理解的)。然而,我只是无法理解,为什么我不能将信号从QThread传递到主应用程序。

As I really needed a fast solution to my problem, I desperately tried varius things. Here is some second code, that does seem to work the way I wanted:

当我真的需要一个快速的解决方案来解决我的问题时,我绝望地尝试了各种各样的东西。下面是一些第二段代码,它们似乎按照我的想法运行:

from PyQt4 import QtCore, QtGui
import time
import sys
import math


class SerialCon(QtCore.QThread):

    received = QtCore.pyqtSignal(object)

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self)
        # specify thread context for signals and slots:
        # test: comment following line, and run again
        self.moveToThread(self)
        # timer:
        self.timer = QtCore.QTimer()
        self.timer.moveToThread(self)
        self.timer.setInterval(800)
        self.timer.timeout.connect(self.readData)

    def run(self):
        self.timer.start()
        #start eventloop
        self.exec_()

    def readData(self):
        # keeping the thread busy
        # figure out if the GUI remains responsive (should be running on a different thread)
        result = []
        for i in range(1,1000000):
            result.append(math.pow(i,0.2)*math.pow(i,0.1)*math.pow(i,0.3))
        #
        self.received.emit("New serial data!")

    @QtCore.pyqtSlot(object)
    def writeData(self, data):
       #print(self.currentThreadId())
       print(data)

class MyGui(QtGui.QWidget):
    serialWrite = QtCore.pyqtSignal(object)

    def __init__(self, app, parent=None):
       self.app = app
       super(MyGui, self).__init__(parent)
       self.initUI()

    def initUI(self):
       self.bSend = QtGui.QPushButton("Send",self)
       self.bSend.clicked.connect(self.sendData)
       self.show()
    def closeEvent(self, event):
        print("Close.")
        self.serialc.quit();

    @QtCore.pyqtSlot(object)
    def updateData(self, data):
        print(data)

    def sendData(self, pressed):
       self.serialWrite.emit("Send Me! Please?")

    def usingMoveToThread(self):
        self.serialc = SerialCon()
        # binding signals:
        self.serialc.received.connect(self.updateData)
        self.serialWrite.connect(self.serialc.writeData)
        # start thread
        self.serialc.start()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    guui = MyGui(app)
    guui.usingMoveToThread()
    sys.exit(app.exec_())

I consider it a workaround for now, but it does not really answer the question for me. Plus, as mentioned in the previously linked QT blog entry, it is not really the intended way to use the QThread. So I am still wondering how to get the first code in my question to work as expected. If you have some ideas about what is wrong with my first code, please let my know!

我认为这是一种变通方法,但它并不能真正回答我的问题。另外,正如前面提到的QT博客条目中提到的,它并不是真正的使用QThread的方法。因此,我仍然在考虑如何在我的问题中获得第一个代码,以达到预期的效果。如果您对我的第一个代码有什么问题,请告诉我!

#1


0  

At first I was only focussing on the new way, how QThreads should be used since QT4 (Link), by creating a QObject, and then invoking moveToThread(), pretty much like in my first code sample (at least thats how I understood it). However I just could not figure out, why I was not able to pass signals from the QThread to the main application.

起初,我只关注新方法,如何使用QT4 (Link),创建一个QObject,然后调用moveToThread(),这与我的第一个代码示例非常相似(至少我是这么理解的)。然而,我只是无法理解,为什么我不能将信号从QThread传递到主应用程序。

As I really needed a fast solution to my problem, I desperately tried varius things. Here is some second code, that does seem to work the way I wanted:

当我真的需要一个快速的解决方案来解决我的问题时,我绝望地尝试了各种各样的东西。下面是一些第二段代码,它们似乎按照我的想法运行:

from PyQt4 import QtCore, QtGui
import time
import sys
import math


class SerialCon(QtCore.QThread):

    received = QtCore.pyqtSignal(object)

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self)
        # specify thread context for signals and slots:
        # test: comment following line, and run again
        self.moveToThread(self)
        # timer:
        self.timer = QtCore.QTimer()
        self.timer.moveToThread(self)
        self.timer.setInterval(800)
        self.timer.timeout.connect(self.readData)

    def run(self):
        self.timer.start()
        #start eventloop
        self.exec_()

    def readData(self):
        # keeping the thread busy
        # figure out if the GUI remains responsive (should be running on a different thread)
        result = []
        for i in range(1,1000000):
            result.append(math.pow(i,0.2)*math.pow(i,0.1)*math.pow(i,0.3))
        #
        self.received.emit("New serial data!")

    @QtCore.pyqtSlot(object)
    def writeData(self, data):
       #print(self.currentThreadId())
       print(data)

class MyGui(QtGui.QWidget):
    serialWrite = QtCore.pyqtSignal(object)

    def __init__(self, app, parent=None):
       self.app = app
       super(MyGui, self).__init__(parent)
       self.initUI()

    def initUI(self):
       self.bSend = QtGui.QPushButton("Send",self)
       self.bSend.clicked.connect(self.sendData)
       self.show()
    def closeEvent(self, event):
        print("Close.")
        self.serialc.quit();

    @QtCore.pyqtSlot(object)
    def updateData(self, data):
        print(data)

    def sendData(self, pressed):
       self.serialWrite.emit("Send Me! Please?")

    def usingMoveToThread(self):
        self.serialc = SerialCon()
        # binding signals:
        self.serialc.received.connect(self.updateData)
        self.serialWrite.connect(self.serialc.writeData)
        # start thread
        self.serialc.start()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    guui = MyGui(app)
    guui.usingMoveToThread()
    sys.exit(app.exec_())

I consider it a workaround for now, but it does not really answer the question for me. Plus, as mentioned in the previously linked QT blog entry, it is not really the intended way to use the QThread. So I am still wondering how to get the first code in my question to work as expected. If you have some ideas about what is wrong with my first code, please let my know!

我认为这是一种变通方法,但它并不能真正回答我的问题。另外,正如前面提到的QT博客条目中提到的,它并不是真正的使用QThread的方法。因此,我仍然在考虑如何在我的问题中获得第一个代码,以达到预期的效果。如果您对我的第一个代码有什么问题,请告诉我!