0

I am building a simple QT UI that communicates with an Arduino. I implemented a simple mechanism for automatic device detection. The pc is looking for all the connected serial devices and tries to send a Hi! message to the available ports. If the Arduino is connected to the pc, it replies this message. I want to show the status of device search procedure on the status bar of the UI. However, the status bar is not updated. It only shows the latest message. The original idea is not even having a push button for device search. I was simply calling findDevice() in setupUi() but in that case the UI does not even start till the end of findDevice() execution. If I directly call the method during UI setup I see a white window and everything appears when findDevice() method finishes execution. So why the status bar message does not update till the end of findDevice() execution and how can I eliminate pushButton_Connect? The code does not complete. It is a bit long but most of it is the generated code. I added only a few lines.

EDIT: The answer of this question is here: https://stackoverflow.com/a/57500544/10370627. Additionaly my closeEvent() method is not working in below code. For that problem the solution is here: https://stackoverflow.com/a/22460392/10370627

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file C:\ard_serial\python_code\calibrator_ui.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
from pyqtgraph import PlotWidget
import numpy as np
import sys, time, serial, glob

# Global constants
ENCODING = "UTF-8"
GREETING = "Hi!"

DATA_READ = '0'
LIGHT_OFF = '1'
LED_ON = '2'
LASER_ON = '3'
EX_LED_ON = '4'
EX_LASER_ON = '5'

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # STARTS: User added class members
        self.ser_port = None
        # ENDS: User added class members
        
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1070, 595)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
        MainWindow.setSizePolicy(sizePolicy)
        MainWindow.setMinimumSize(QtCore.QSize(1070, 595))
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.lineEdit_Input = QtWidgets.QLineEdit(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.lineEdit_Input.sizePolicy().hasHeightForWidth())
        self.lineEdit_Input.setSizePolicy(sizePolicy)
        self.lineEdit_Input.setObjectName("lineEdit_Input")
        self.gridLayout.addWidget(self.lineEdit_Input, 0, 1, 1, 1)
        self.graphicsView_PlotWidget = PlotWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.graphicsView_PlotWidget.sizePolicy().hasHeightForWidth())
        self.graphicsView_PlotWidget.setSizePolicy(sizePolicy)
        self.graphicsView_PlotWidget.setMinimumSize(QtCore.QSize(1050, 490))
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.NoBrush)
        self.graphicsView_PlotWidget.setBackgroundBrush(QtGui.QColor('black'))
        self.graphicsView_PlotWidget.setObjectName("graphicsView_PlotWidget")
        self.gridLayout.addWidget(self.graphicsView_PlotWidget, 1, 0, 1, 5)
        self.label_Only_integers = QtWidgets.QLabel(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_Only_integers.sizePolicy().hasHeightForWidth())
        self.label_Only_integers.setSizePolicy(sizePolicy)
        self.label_Only_integers.setObjectName("label_Only_integers")
        self.gridLayout.addWidget(self.label_Only_integers, 0, 0, 1, 1)
        self.pushButton_Generate = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton_Generate.sizePolicy().hasHeightForWidth())
        self.pushButton_Generate.setSizePolicy(sizePolicy)
        self.pushButton_Generate.setObjectName("pushButton_Generate")
        self.gridLayout.addWidget(self.pushButton_Generate, 0, 2, 1, 1)
        self.pushButton_Connect = QtWidgets.QPushButton(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.pushButton_Connect.sizePolicy().hasHeightForWidth())
        self.pushButton_Connect.setSizePolicy(sizePolicy)
        self.pushButton_Connect.setObjectName("pushButton_Connect")
        self.gridLayout.addWidget(self.pushButton_Connect, 0, 3, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1070, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        
        # STARTS: User added setup code
        self.pushButton_Generate.clicked.connect(lambda:self.draw())
        '''
        I even do not want to use a push button for device search. But when I
        call findDevice() method the UI appears after the execution of
        findDevice() method.
        '''
        self.pushButton_Connect.clicked.connect(lambda:self.findDevice())
        
        x = np.random.normal(size = 500)
        y = np.random.normal(size = (3, 500))
        for i in range(3):
            self.graphicsView_PlotWidget.plot(x, y[i], pen = (i, 3)) 
            
        # ENDS: User added setup code
        
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Calibrator"))
        self.label_Only_integers.setText(_translate("MainWindow", "Only integers"))
        self.pushButton_Generate.setText(_translate("MainWindow", "Generate"))
        self.pushButton_Connect.setText(_translate("MainWindow", "Connect"))
        
    # STARTS: User added methods
    def findDevice(self):
        '''
        Here I expect to see all the status bar messages but I see only the last
        one. For instance if there is no serial device is connected, I only see
        <No serial device is connected> message on the status bar.
        '''
        self.statusbar.showMessage("Looking for device...")
        print("Looking for device...")
        
        if sys.platform.startswith('win'):
            ports = ['COM%s' % (i + 1) for i in range(256)]
        elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
            import subprocess
            # ports = glob.glob('/dev/tty[A-Za-z]*')
            ports = glob.glob('/dev/ttyUSB[0-9]*')
            for port in ports:
                process = subprocess.Popen(["sudo", "chmod", "777", port], stdout = subprocess.PIPE)
                output, error = process.communicate()
        else:
            raise EnvironmentError('Unsupported platform')
        
        for port in ports:
            try:
                self.ser_port = serial.Serial(port, 115200)
                time.sleep(1.8)
                self.statusbar.showMessage("Trying: " + str(port))
                print("Trying:", port)
                self.ser_port.write(GREETING.encode(ENCODING))
                time.sleep(0.1)
                self.ser_port.reset_input_buffer()
                time.sleep(1)
                read_data = self.ser_port.read_all().decode(ENCODING)
                if(GREETING == read_data):
                    self.statusbar.showMessage("Device detected on: " + str(port))
                    print("\nDevice detected on:", port)
                else:
                    self.statusbar.showMessage("No device detected")
                    print("No device detected")
            except (OSError, serial.SerialException):
                pass
            
        if(self.ser_port is None):
            print("No serial device is connected")
            self.statusbar.showMessage("No serial device is connected")
        
        
    def draw(self):
        self.graphicsView_PlotWidget.clear()
        
    def closeEvent(self, event):
        self.ledit.setText(LIGHT_OFF)
        if(self.ser_port is not None):
            ret_val = self.ser_port.close()
            print("Program terminated with return code: ", ret_val)
    # ENDS: User added methods

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
  • 1
    Create a separate worker thread for finding the dervices. Use signals to communicate with the main thread and update the staus-bar. DO NOT attempt to *directly* update the status-bar in the worker thread. Also avoid using `time.sleep` in the main thread as it will block ui updates. – ekhumoro Oct 19 '21 at 16:18

0 Answers0