1

I have a problem with the gui data, my gui doesn't update a real-time value when I click the button. the first time i click my connect button it shows the correct value, but when i change the sensor position, it doesn't update the value. Where i miss the code, i try to solve the problem from the other similar question to this question, but still does not solve my problem

here is my code

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()])

and here is my gui

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Dona F
  • 51
  • 7
  • Why do you use threads if QSerialPort does not need them? It seems to me that the use of threads is complicating the logic – eyllanesc Jul 02 '19 at 07:24
  • But how i could emit the data when i did not use the thread, i did fully understand how to use QSerialPort – Dona F Jul 02 '19 at 07:40
  • I am implementing your same logic without using threads. – eyllanesc Jul 02 '19 at 07:41
  • So where this code specificly place buf = self.serial.read(14) if buf: self.received_data.emit(buf) when i didnot use thread – Dona F Jul 02 '19 at 07:57

1 Answers1

2

It is not necessary to use a thread for this case besides that your implementation has at least one problem: QSerialPort is a QObject that should only be used in a single thread since it is not thread-safe, this belongs to the GUI thread since there It was created but you use it in another thread.

In this case you must use the readyRead signal from QSerialPort:

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_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • thank's its works fine, but where i could give delay to read the data, if i place the code time.sleep(1) to onReadyRead function, its give me slug response – Dona F Jul 02 '19 at 09:20
  • @MKDFF Why do you want to give a sleep? with the current code is not necessary – eyllanesc Jul 02 '19 at 09:22
  • it just for the view only, in further development, i want to use the value of the serial data to make a calculation, i want to make sure that, the calculation is calculating correctly based on that view – Dona F Jul 02 '19 at 09:28
  • 1
    @MKDFF If your calculations are correct then the view will show them correctly. Separate the logic of sight. The logic of my code is to show the data as soon as it comes. If you give it a sleep it could fill the buffer of QSerialPort and in the long term it is unmanageable. Have your code of logic, implement the necessary tests to verify that it works correctly, to check your calculation it is not necessary to use the data of the serial, you could create your data with errors to see the robustness of your algorithm. – eyllanesc Jul 02 '19 at 09:33