0

I have a job to create a stm mcu downloader using uart communication.

I used to python and pyqt5.

Results were bad because of the program GUI freezing when starting the download. (download progress is well)

So I changed the code to the way I use Qthread.

But nothing change.

I have 2 Questions.

  1. Am I use Qthread correctly?

  2. How can I fix GUI freezing when start download?

Attach the code below

from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog, QTextBrowser
from PyQt5 import uic
from PyQt5.QtCore import *
import sys, serial, time, struct, os

form_class = uic.loadUiType('USART_Downloader.ui')[0]

class Worker(QObject):
    ports=['COM%s'%(i+1)for i in range(256)]
    check_connect = 0
    serial = serial.Serial()
    time_instance = 0.00008

    finished = pyqtSignal(list)
    finished2 = pyqtSignal(str)
    finished3 = pyqtSignal(str)
    finished4 = pyqtSignal(int)

    @pyqtSlot()
    def Find_Com(self):
        result = []
        for i in self.ports:
            try:
                temp = serial.Serial(i)
                temp.close()
                result.append(i)
            except serial.SerialException:
                pass
        self.finished.emit(result)

    @pyqtSlot(str)
    def Apply_Com(self, COM_Port):
        self.check_connect=0
        self.serial.close()

        try :
            self.serial = serial.Serial(COM_Port, 115200, timeout = 1)
            self.check_connect = 1
            self.finished2.emit('Successfully Port Connection')
        except serial.SerialException:
            self.finished2.emit('Failure Port Connection')

        if self.check_connect :
            self.serial.write(b'\x7F')
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                self.finished2.emit('Successfully Enter Boot Mode')
            else:
                self.finished2.emit('Failure Enter Boot Mode')

    @pyqtSlot(str)
    def Download_Flash(self, fileName):
        if self.check_connect:
            self.serial.write(b'\x44')
            time.sleep(self.time_instance)
            self.serial.write(b'\xBB')
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                self.finished3.emit('Erasing')
            else:
                self.finished3.emit('Failure Enter Erase Flash')
                return

            self.serial.write(b'\xFF')
            time.sleep(self.time_instance)
            self.serial.write(b'\xFF')
            time.sleep(self.time_instance)
            self.serial.write(b'\x00')
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                self.finished3.emit('Successfully Erase Flash')
            else:
                self.finished3.emit('Failure Enter Erase Flash')
                return
        else :
            self.finished3.emit('Need USART Connection')

        Bit_Num = 256
        f = open(fileName, 'rb')
        download_binary = f.read()
        download_binary_len = len(download_binary)
        f.close()

        start_address = b'\x00\x00\x00\x08'
        total_data = []

        while(True):
            if(download_binary_len > Bit_Num):
                total_data.append(Bit_Num)
                download_binary_len -= Bit_Num
            elif(download_binary_len > 0):
                total_data.append(download_binary_len)
                download_binary_len = 0
            else:
                break

        self.finished3.emit('Downloading')

        for i, k in enumerate(total_data):
            self.serial.write(b'\x31')
            time.sleep(self.time_instance)
            self.serial.write(b'\xCE')
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                pass
            else:
                self.finished3.emit('Failure Download1')
                return

            self.serial.write(start_address[3:4])
            time.sleep(self.time_instance)
            self.serial.write(start_address[2:3])
            time.sleep(self.time_instance)
            self.serial.write(start_address[1:2])
            time.sleep(self.time_instance)
            self.serial.write(start_address[0:1])
            time.sleep(self.time_instance)

            temp2 = struct.unpack('4B', start_address)
            check_sum = temp2[0] ^ temp2[1] ^ temp2[2] ^ temp2[3]
            check_sum = struct.pack('B', check_sum)

            self.serial.write(check_sum)
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                pass
            else:
                self.finished3.emit('Failure Download2')
                return

            check_sum = (k-1)
            self.serial.write(struct.pack('B', check_sum))
            time.sleep(self.time_instance)

            for j in range(k):
                self.serial.write(download_binary[(i*Bit_Num)+j:(i*Bit_Num)+j+1])
                time.sleep(self.time_instance)

            for j in download_binary[(i*Bit_Num):(i*Bit_Num)+j+1] :
                check_sum = check_sum ^ j
            check_sum = struct.pack('B', check_sum)

            self.serial.write(check_sum)
            time.sleep(self.time_instance)

            res = self.serial.read()
            if res == b'\x79' :
                pass
            else:
                self.finished3.emit('Failure Download3')
                return

            temp3 = struct.unpack('i', start_address)[0]
            temp3 = temp3 + Bit_Num
            start_address = struct.pack('i', temp3)

            self.finished4.emit(i/(len(total_data)-1)*100)

        self.finished3.emit('Success Download')

class MyWindowClass(QMainWindow, form_class):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)

        self.fileName = ''

        self.worker_thread = QThread()
        self.worker = Worker()
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.start()
        self.worker.finished.connect(self.Refresh_Com)
        self.worker.finished2.connect(self.Print_textBrowser)
        self.worker.finished3.connect(self.Print_textBrowser_3)
        self.worker.finished4.connect(self.Set_ProgressBar)

        self.pushButton.clicked.connect(self.Find_Binary)
        self.pushButton_2.clicked.connect(lambda:self.worker.Download_Flash(self.fileName))
        self.pushButton_3.clicked.connect(lambda:self.worker.Apply_Com(self.comboBox.currentText()))
        self.pushButton_4.clicked.connect(self.worker.Find_Com)

        self.textBrowser.setAcceptRichText(True)
        self.textBrowser.setOpenExternalLinks(True)
        self.textBrowser_3.setAcceptRichText(True)
        self.textBrowser_3.setOpenExternalLinks(True)

        self.progressBar.reset()
        self.worker.Find_Com()

    @pyqtSlot(list)
    def Refresh_Com(self, result):
        self.comboBox.clear()
        for i in result:
            self.comboBox.addItem(i)
        self.textBrowser.append('COM Port Refresh Done')

    @pyqtSlot(str)
    def Print_textBrowser(self, text):
        self.textBrowser.append(text)

    @pyqtSlot(str)
    def Print_textBrowser_3(self, text):
        self.textBrowser_3.append(text)

    @pyqtSlot(int)
    def Set_ProgressBar(self, percent):
        self.progressBar.setValue(percent)

    def Find_Binary(self):
        options = QFileDialog.Options()
        self.fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "", "Binary Files (*.bin)", options=options)
        self.lineEdit.setText(self.fileName)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindowClass()
    myWindow.show()
    app.exec_()
greendino
  • 416
  • 3
  • 17
sphinx
  • 3
  • 5
  • you're using time.sleep. I think that's the problem. same problem as [here](https://stackoverflow.com/questions/41545300/equivalent-to-time-sleep). pyqt has its own sleep method – greendino Oct 20 '20 at 19:02

1 Answers1

1

You're using time.sleep() in your code which blocks your GUI. I would recommend you to use QTimer.singleShot() instead. according from this source, you can use time.sleep equivalent like this QtTest.QTest.qWait(msecs)

greendino
  • 416
  • 3
  • 17
  • I changed the code according to your recommendations. The GUI works smoothly. Thanks for your help. – sphinx Oct 21 '20 at 01:06