不使用按钮单击更新串行数据

Posted

技术标签:

【中文标题】不使用按钮单击更新串行数据【英文标题】:doesn't update serial data using button click 【发布时间】:2019-07-02 07:19:09 【问题描述】:

我的 gui 数据有问题,当我单击按钮时,我的 gui 没有更新实时值。第一次单击连接按钮时,它会显示正确的值,但是当我更改传感器位置时,它不会更新值。在我错过代码的地方,我尝试解决与此问题类似的问题,但仍然没有解决我的问题

这是我的代码

class SerialReadThread(QThread):
received_data = pyqtSignal(QByteArray, name="receivedData")

def __init__(self, serial):
    QThread.__init__(self)
    self.cond = QWaitCondition()
    self._status = False
    self.mutex = QMutex()
    self.serial = serial

def __del__(self):
    self.wait()

def run(self):
    while True:
        self.mutex.lock()
        if not self._status:
            self.cond.wait(self.mutex)

        buf = self.serial.read(14)
        if buf:
            self.received_data.emit(buf)
        self.sleep(1)
        self.mutex.unlock()

def toggle_status(self):
    self._status = not self._status
    if self._status:
        self.cond.wakeAll()

@pyqtSlot(bool, name='setStatus')
def set_status(self, status):
    self._status = status
    if self._status:
        self.cond.wakeAll()

class Form(QDialog):
   received_data = pyqtSignal(QByteArray, name="receivedData")

def __init__(self):
    super().__init__()
    self.ui = Ui_Dialog()
    self.ui.setupUi(self)
    self.show()

    self.serial = QSerialPort()
    self.serial_info = QSerialPortInfo()
    self._fill_serial_info()
    self.ui.btnConnect.clicked.connect(self.slot_clicked_connect_button)
    self.serial_read_thread = SerialReadThread(self.serial)
    self.serial_read_thread.received_data.connect(lambda v: self.received_data.emit(v))

@staticmethod
def get_port_path():
    return "linux": '/dev/ttyS', "win32": 'COM'[__platform__]

def _get_available_port(self):
    available_port = list()
    port_path = self.get_port_path()

    for number in range(255):
        port_name = port_path + str(number)
        if not self._open(port_name):
            continue
        available_port.append(port_name)
        self.serial.close()
    return available_port

def _fill_serial_info(self):
    self.ui.cmbPort.insertItems(0, self._get_available_port())

def _open(self, port_name, baudrate=QSerialPort.Baud9600):
    info = QSerialPortInfo(port_name)
    self.serial.setPort(info)
    self.serial.setBaudRate(baudrate)
    return self.serial.open(QIODevice.ReadWrite)

def connect_serial(self):
    serial_info = "port_name": self.ui.cmbPort.currentText()
    status = self._open(**serial_info)
    self.received_data.connect(self.read_data)
    self.serial_read_thread.start()
    self.serial_read_thread.setStatus(status)
    return status

def disconnect_serial(self):
    return self.serial.close()

@pyqtSlot(QByteArray, name="readData")
def read_data(self, rd):
    rd = str(binascii.hexlify(rd), 'ascii', 'replace')
    if rd.startswith("68"):
        val = rd[10:14]
        self.ui.txtRaw.insertPlainText(val)
        self.ui.txtRaw.insertPlainText("\n")

@pyqtSlot(name="clickedConnectButton")
def slot_clicked_connect_button(self):
    if self.serial.isOpen():
        self.disconnect_serial()
    else:
        self.connect_serial()
    self.ui.btnConnect.setText(False: 'Connect', True: 'Stop'  [self.serial.isOpen()])

这是我的 gui

【问题讨论】:

如果 QSerialPort 不需要线程,为什么还要使用线程?在我看来,线程的使用使逻辑复杂化 但是当我不使用线程时如何发出数据,我确实完全了解如何使用 QSerialPort 我在不使用线程的情况下实现与您相同的逻辑。 所以当我没有使用线程时,这段代码具体放在哪里 buf = self.serial.read(14) if buf: self.received_data.emit(buf) 【参考方案1】:

除了你的实现至少有一个问题之外,没有必要在这种情况下使用线程: QSerialPort 是一个 QObject ,因为它不是线程安全的,所以只能在单个线程中使用,这属于 GUI线程,因为它是创建的,但是你在另一个线程中使用它。

在这种情况下,您必须使用来自 QSerialPort 的 readyRead 信号:

import binascii
from PyQt5 import QtCore, QtGui, QtWidgets, QtSerialPort

from dialog_ui import Ui_Dialog


class Dialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(Dialog, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.btnConnect.clicked.connect(self.slot_clicked_connect_button)
        self.serial = QtSerialPort.QSerialPort(self)
        self.serial.readyRead.connect(self.onReadyRead)
        self._fill_serial_info()

    def onReadyRead(self):
        while self.serial.bytesAvailable() >= 14:
            buff = self.serial.read(14)
            rd = str(binascii.hexlify(buff), "ascii", "replace")
            if rd.startswith("68"):
                val = rd[10:14]
                self.ui.txtRaw.insertPlainText(val)
                self.ui.txtRaw.insertPlainText("\n")

    def _fill_serial_info(self):
        self.ui.cmbPort.clear()
        for info in QtSerialPort.QSerialPortInfo.availablePorts():
            self.ui.cmbPort.addItem(info.portName())

    def _open(self, port_name, baudrate=QtSerialPort.QSerialPort.Baud9600):
        info = QtSerialPort.QSerialPortInfo(port_name)
        self.serial.setPort(info)
        self.serial.setBaudRate(baudrate)
        return self.serial.open(QtCore.QIODevice.ReadWrite)

    def connect_serial(self):
        serial_info = "port_name": self.ui.cmbPort.currentText()
        status = self._open(**serial_info)
        return status

    def disconnect_serial(self):
        return self.serial.close()

    @QtCore.pyqtSlot(name="clickedConnectButton")
    def slot_clicked_connect_button(self):
        if self.serial.isOpen():
            self.disconnect_serial()
        else:
            self.connect_serial()
        self.ui.btnConnect.setText(
            "Stop" if self.serial.isOpen() else "Connect"
        )


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Dialog()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

【讨论】:

感谢它的工作正常,但是如果我将代码 time.sleep(1) 放在 onReadyRead 函数中,我可以延迟读取数据,它会给我 slug 响应 @MKDFF 你为什么要睡觉?没有必要使用当前代码 仅供查看,在进一步开发中,我想使用串行数据的值进行计算,我想确保计算是基于该视图正确计算的 @MKDFF 如果您的计算正确,那么视图将正确显示它们。分离视觉的逻辑。我的代码的逻辑是数据一来就显示出来。如果你让它休眠,它可能会填满 QSerialPort 的缓冲区,从长远来看,它是无法管理的。拥有您的逻辑代码,实施必要的测试以验证它是否正常工作,检查您的计算,不必使用串行数据,您可以创建有错误的数据以查看算法的稳健性。跨度>

以上是关于不使用按钮单击更新串行数据的主要内容,如果未能解决你的问题,请参考以下文章

无效更新:iOS 中第 0 部分的行数无效

使用按钮单击更新 SQlite 数据库中的一行 [重复]

D3 choropleth 状态图数据更新按钮单击

如何计算 Visual Basic 中的最大行数

Rails - 当用户单击提交按钮时如何更新表中的数据

运行按钮单击事件时表单不更新