Передача текстовых сообщений из класса в поток в QTextEdit

Снова прошу помощи с потоками. Есть несколько проблем.

При передаче сообщений из scanclass в функцию обработки message_handler, должны эти самые сообщения печататься .append("text") в QTextEdit каждого из потоков (для 1 потока это textEdit_plate_1, для второго это textEdit_plate_2 соответственно)
и когда цикл while scan.send_comm(opn_srl_prt, i) == "STOP" завершается,
(после полученная команды СТОП), то в message_handler тоже должно отправиться сообщение "Сканирование завершено".

Фактически функция "message_handler" не работает, как правильно задать всё это, я не понимаю.

main.py

import sys
import serial
import serial.tools.list_ports
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.Qt import *

from reo_main_window import Ui_MainWindow
from dialog_adress import Ui_Form_dialog_adress

from scanclass import ScanningReo


class Thread(QThread):
    updateSignal = QtCore.pyqtSignal(object, str, str)

    def __init__(self, plate, name_thread):
        super().__init__()
        self.number_port = plate
        self.name_thread = name_thread
        print(f'number_port = {self.number_port}; {self.name_thread}')

    def run(self):
        scan = ScanningReo(
            "serial_port", "date_from_cmd",
            "date_command", "name_freq", "name_dev")
        opn_srl_prt = scan.open_serial_port(self.number_port)
        opn_srl_prt.close()
        opn_srl_prt = scan.open_serial_port(self.number_port)
        id_dev = scan.device_identification(opn_srl_prt)

        print(id_dev)
        if "120" in id_dev:
            self.updateSignal.emit(id_dev, '120', self.name_thread)
            date_command = [f'"AT^SYSCFGEX="03",1\r"',
                            f'"AT^SYSCFGEX="01",2\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="01",3\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="02",4\r"', "AT^NETSCAN=1\r",
                            f'"AT^SYSCFGEX="03",5\r"', "AT^NETSCAN=3\r",
                            "STOP"
                            ]
        elif "821" in id_dev:
            self.updateSignal.emit(id_dev, '821', self.name_thread)
            date_command = [f'"AT^SYSCFGEX="03",1\r"',
                            f'"AT^SYSCFGEX="01",2\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="01",3\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="02",4\r"', "AT^NETSCAN=1\r",
                            f'"AT^SYSCFGEX="03",5\r"', "AT^NETSCAN=3\r",
                            "STOP"
                            ]

        for i in date_command:
            if "SYSCFGEX" in i:
                i = i.strip('"')
                self.msleep(1000 * 2)  # 2 сек.
            while scan.send_comm(opn_srl_prt, i) == "STOP":
                Thread.message_handler(self, id_dev, msg=date_command)
                self.msleep(1000 * 2)  # 2 сек.
            if scan.send_comm(opn_srl_prt, i) == "STOP":
                Thread.message_handler(self, id_dev, msg="Сканирование завершено")

    def message_handler(self, id_dev, msg):
        """ Функция "message_handler" обрабатывает поступающие сообщения
            и выводит их в ui.textEdit
        """

        if "120" in id_dev:
            if "AT^NETSCAN=20,-110,0" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 2!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 2")    #ДОБАВЛЕНО!!!!
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 3")    #ДОБАВЛЕНО!!!!
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 4")    #ДОБАВЛЕНО!!!!
            if "Начат" or "Закончен" in msg:
                self.updateSignal.emit(
                    id_dev, f"{msg}", self.name_thread)
                main_window.textEdit_plate_2.append(f"{msg}")    #ДОБАВЛЕНО!!!!

        if "821" in id_dev:
            if "AT^NETSCAN=20,-110,0" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 2!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 2")    #ДОБАВЛЕНО!!!!
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 3")    #ДОБАВЛЕНО!!!!
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4!", self.name_thread)
                main_window.textEdit_plate_2.append("Начинаю сканирование 4")    #ДОБАВЛЕНО!!!!
            if "Начат" or "Закончен" in msg:
                self.updateSignal.emit(
                    id_dev, f"{msg}", self.name_thread)
                main_window.textEdit_plate_2.append(f"{msg}")    #ДОБАВЛЕНО!!!!


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.thread_1 = None
        self.thread_2 = None

        self.pushButton_start.clicked.connect(self.run_th)

    def run_th(self):
        """ Функция "run_th" запускает потоки с экземплярами класса """

        device_port = self.scan_ttyusb()
        plate1 = device_port[0]
        plate2 = device_port[1]
        icon = QtGui.QIcon()

        if self.thread_1 is None and self.thread_2 is None:
            self.thread_1 = Thread(plate1, 'Plate 1')
            self.thread_1.updateSignal.connect(self.update_thread)
            self.thread_1.start()

            self.thread_2 = Thread(plate2, 'Plate 2')
            self.thread_2.updateSignal.connect(self.update_thread)
            self.thread_2.start()

            self.pushButton_start.setText("Stop")
            icon.addPixmap(QtGui.QPixmap(":/icons_main/cancel-64.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
            self.pushButton_start.setIcon(icon)

        else:
            reply = QMessageBox.question(
                self,
                'ВНИМАНИЕ !',
                "Вы уверены, что хотите прервать выполнение программы?",
                QMessageBox.Yes, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.thread_1.terminate()
                self.thread_1 = None
                self.thread_2.terminate()
                self.thread_2 = None

                self.pushButton_start.setText("Start")
                icon.addPixmap(QtGui.QPixmap(":/icons_main/go-64.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
                self.pushButton_start.setIcon(icon)

    def update_thread(self, id_dev, plate, name_thread):
        if name_thread == 'Plate 1':
            if '821' in plate:
                self.label_plate_1.setText(f"{id_dev}")
            else:
                self.label_plate_1.setText(f"{id_dev}")
        if name_thread == 'Plate 2':
            if '120' in plate:
                self.label_plate_2.setText(f"{id_dev}")
            else:
                self.label_plate_2.setText(f"{id_dev}")

    def scan_ttyusb(self):
        """ Функция "scan_ttyusb" сканирует порты и возвращает номера портов
            к которым подключены нужные устройства
        """

        ports = list(serial.tools.list_ports.comports())
        result = ""
        for text in ports:
            if 'Pcui' in text[1]:
                txt = text[0]
                result = result + txt + ','
                device_port = result

        return device_port.split(",")

    def closeEvent(self, event):
        reply = QMessageBox.question(
            self,
            'Информация',
            "Вы уверены, что хотите закрыть приложение?",
            QMessageBox.Yes, QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            if self.thread_1:
                self.thread_1.quit()
            del self.thread_1
            if self.thread_2:
                self.thread_2.quit()
            del self.thread_2

            super(MainWindow, self).closeEvent(event)
        else:
            event.ignore()


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.setFixedSize(680, 572)
    main_window.show()
    sys.exit(app.exec_())

Вот часть кода scanclass.py:

class ScanningReo:
    def __init__(self, serial_port, date_from_cmd, date_command, name_freq, name_dev):
        self.serial_port = serial_port
        self.date_from_cmd = date_from_cmd
        self.date_command = date_command
        self.name_freq = name_freq
        self.name_dev = name_dev
    def send_comm(self, serial_port, date_command):
        print(date_command)
        if "NETSCAN" in str(date_command):
            time.sleep(2)
            result = ""
            for i in range(3):  # количество проходов от 0 до 2
                serial_port.write(date_command.encode())
                state_cycle = True
                print(f"Начат круг № '{i+1}'")    #Эти сообщения дожны сыпаться в QtextEdit каждого из потоков.
                prev_comm = ''
                start_cycle = time.time()
                while state_cycle:
               ....
               ....
               ....
                    state_cycle = False
                print(f"Закончен проход № '{i+1}'")    #Эти сообщения дожны сыпаться в QtextEdit

Добавлено:

reo_main_window.py

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(680, 572)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap(":/icons_main/electrical_sensor_main-100.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        MainWindow.setStyleSheet("QMainWindow{\n"
"    background-color: white;\n"
"}")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton_start = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_start.setGeometry(QtCore.QRect(550, 10, 120, 41))
        self.pushButton_start.setStyleSheet("QPushButton {\n"
"    color: rgb(255, 255, 255);\n"
"    background-color: rgb(17, 154, 40);\n"
"}")
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap(":/icons_main/go-64.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.pushButton_start.setIcon(icon1)
        self.pushButton_start.setIconSize(QtCore.QSize(40, 40))
        self.pushButton_start.setAutoDefault(True)
        self.pushButton_start.setObjectName("pushButton_start")
        self.textEdit_plate_1 = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit_plate_1.setGeometry(QtCore.QRect(10, 70, 251, 421))
        self.textEdit_plate_1.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.textEdit_plate_1.setObjectName("textEdit_plate_1")
        self.textEdit_plate_2 = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit_plate_2.setGeometry(QtCore.QRect(280, 70, 251, 421))
        self.textEdit_plate_2.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.textEdit_plate_2.setObjectName("textEdit_plate_2")
        self.label_log_work_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_log_work_1.setGeometry(QtCore.QRect(10, 0, 251, 31))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.label_log_work_1.setFont(font)
        self.label_log_work_1.setObjectName("label_log_work_1")
        self.label_plate_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_plate_1.setGeometry(QtCore.QRect(10, 30, 251, 31))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.label_plate_1.setFont(font)
        self.label_plate_1.setText("")
        self.label_plate_1.setObjectName("label_plate_1")
        self.label_plate_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_plate_2.setGeometry(QtCore.QRect(280, 30, 251, 31))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.label_plate_2.setFont(font)
        self.label_plate_2.setText("")
        self.label_plate_2.setObjectName("label_plate_2")
        self.label_log_work_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_log_work_2.setGeometry(QtCore.QRect(280, 0, 251, 31))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.label_log_work_2.setFont(font)
        self.label_log_work_2.setObjectName("label_log_work_2")
        self.progressBar_plate_2 = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar_plate_2.setGeometry(QtCore.QRect(280, 500, 251, 23))
        self.progressBar_plate_2.setStyleSheet("QProgressBar {\n"
"    border-radius: 5px;\n"
"    text-align: center;\n"
"    border: 1px solid rgb(192, 191, 188);\n"
"}\n"
"\n"
"QProgressBar::chunk{\n"
"    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(201, 87, 149, 255), stop:1 rgba(179, 65, 244, 255));\n"
"    border-radius: 5px;\n"
"}")
        self.progressBar_plate_2.setProperty("value", 0)
        self.progressBar_plate_2.setObjectName("progressBar_plate_2")
        self.progressBar_plate_1 = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar_plate_1.setGeometry(QtCore.QRect(10, 500, 251, 23))
        self.progressBar_plate_1.setStyleSheet("QProgressBar {\n"
"    border-radius: 5px;\n"
"    text-align: center;\n"
"    border: 1px solid rgb(192, 191, 188);\n"
"}\n"
"\n"
"QProgressBar::chunk{\n"
"    background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1, stop:0 rgba(201, 87, 149, 255), stop:1 rgba(179, 65, 244, 255));\n"
"    border-radius: 5px;\n"
"}")
        self.progressBar_plate_1.setProperty("value", 0)
        self.progressBar_plate_1.setObjectName("progressBar_plate_1")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 680, 22))
        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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Radio-electronic environment scanning"))
        self.pushButton_start.setText(_translate("MainWindow", "Start"))
        self.textEdit_plate_1.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'Ubuntu\'; font-size:11pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
        self.textEdit_plate_1.setPlaceholderText(_translate("MainWindow", "В этом окне можно наблюдать лог работы устройства сканирования №1"))
        self.textEdit_plate_2.setPlaceholderText(_translate("MainWindow", "В этом окне можно наблюдать лог работы устройства сканирования №2"))
        self.label_log_work_1.setText(_translate("MainWindow", "Лог работы устройства:"))
        self.label_log_work_2.setText(_translate("MainWindow", "Лог работы устройства:"))

print(id_dev)

ME909s-120    #plate1 = device_port[1]
ME909s-821    #plate1 = device_port[0]

textEdit_plate_1.append("text") я пытаюсь выполнить в каждом if функции "message_handler"

print(f'{id_dev}; \n{msg};') в "message_handler" не принтует, т.е. в "message_handler" нет обращения.


Добавлено №2:

в с комментариями пользователя @S. Nick:

и так пойдем по порядку кода, с объяснениями

ddate_command - это опечатка моя, которую не заметил, должно быть date_command

if "SYSCFGEX" in i: - да, я понимаю для чего делаю, убираю ковычки лишние, чтобы команду нормально отправить в порт, так как тут написана сокращенная версия команды, и у питона возникает диссонанс при отправке.

пауза self.msleep(1000 * 2) - по большей части являлась отладочной, чтобы всё сыпалось постепенно в cmd.

print(f'что тут --> {scan.send_comm(opn_srl_prt, i)} <-- проверьте') - что тут -->None

print(f'что тут --> {scan.send_comm(opn_srl_prt, i)} <-- проверьте') # !!!   
            # я так понимаю что вы никогда не попадаете в цикл ниже
            # потому что видимо вы хотите что-то
            # делать пока ---------------------> != <------- не равно "STOP"
            while scan.send_comm(opn_srl_prt, i) == "STOP":
#                Thread.message_handler(self, id_dev, msg=date_command)
                print(f'а что тут --> {scan.send_comm(opn_srl_prt, i)} <-- проверьте')

Да, вы похоже правы на счёт того, что я не попадаю в цикл while. пока != - не равно, я хотел бы выводить сообщения из модуля scanclass о проходе кругов в QTextEdit'ы по потокам соответственно, а при получении стоп - в тот же Эдит, печать "скан завершен", а также заканчивать выполенения потоков thread_1 и thread_2 - чтобы в дальнейшем вывести еще одно диалоговое окно, для дальнейшей работы приложения.

print("а что тут -->" + f'{scan.send_comm(opn_srl_prt, i)}')
  • не принтует но хочу сразу сказать, что opn_srl_prt - это функция открытия СОМ-порта, он выполняется правильно, а i - это команда из массива date_command
print(f'что тут --> {msg)} <-- проверьте') # +++ 
  • не принтует

Ответы (1 шт):

Автор решения: S. Nick

textEdit_plate_1.append("text")
я пытаюсь выполнить в каждом if функции "message_handler"

Не надо пытаться в каждом if функции "message_handler" выполнять:

???.textEdit_plate_1.append("text")

Вы должны делать:

self.textEdit_plate_1.append(plate)

в методе def update_thread(self, id_dev, plate, name_thread)



print(f'{id_dev}; \n{msg};')
в "message_handler" не принтует, т.е. в "message_handler" нет обращения.

Не обращается, потому что условие:

while scan.send_comm(opn_srl_prt, i) == "STOP":

ЛОЖНОЕ и метод message_handler(self, id_dev, msg) не вызывается.


и другие не понятные вещи я прокомментировал в тексте вашего кода.
Очень внимательно читаем все строки, которые я написал для вас.


...

class Thread(QThread):
    updateSignal = QtCore.pyqtSignal(object, str, str)

    def __init__(self, plate, name_thread):
        super().__init__()
        self.number_port = plate
        self.name_thread = name_thread
        print(f'number_port = {self.number_port}; {self.name_thread}')

    def run(self):
        scan = ScanningReo(                                 
            "serial_port", "date_from_cmd",
            "date_command", "name_freq", "name_dev")
        opn_srl_prt = scan.open_serial_port(self.number_port)
        opn_srl_prt.close()
        opn_srl_prt = scan.open_serial_port(self.number_port)
        id_dev = scan.device_identification(opn_srl_prt)

        print(id_dev)
#ME909s-120    #plate1 = device_port[1]
#ME909s-821    #plate1 = device_port[0]        
        if "120" in id_dev:
            self.updateSignal.emit(id_dev, '120', self.name_thread)
            date_command = [f'"AT^SYSCFGEX="03",1\r"',
                            f'"AT^SYSCFGEX="01",2\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="01",3\r"', "AT^NETSCAN=0\r",
                            f'"AT^SYSCFGEX="02",4\r"', "AT^NETSCAN=1\r",
                            f'"AT^SYSCFGEX="03",5\r"', "AT^NETSCAN=3\r",
                            "STOP"
                            ]
        elif "821" in id_dev:
            self.updateSignal.emit(id_dev, '821', self.name_thread)
# --------> v <---- почему ddate_command, а не date_command                 ???
# и чем данные ddate_command, отличаются от данных date_command             ???
# Я не вижу чтобы ddate_command где-то дальще использовалось.
# --------> v <---- почему ddate_command, а не date_command                 ???
            ddate_command = [f'"AT^SYSCFGEX="03",1\r"',
                             f'"AT^SYSCFGEX="01",2\r"', "AT^NETSCAN=0\r",
                             f'"AT^SYSCFGEX="01",3\r"', "AT^NETSCAN=0\r",
                             f'"AT^SYSCFGEX="02",4\r"', "AT^NETSCAN=1\r",
                             f'"AT^SYSCFGEX="03",5\r"', "AT^NETSCAN=3\r",
                             "STOP"
                            ]


        for i in date_command:
# для чего этот if ???  
# --------> vv <^^ <---------------------------------------      ???   
            if "SYSCFGEX" in i:
                i = i.strip('"')
                '''                
вы понимаете что вы делаете и для чего вы это делаете ??? 
>>> i = f'"AT^SYSCFGEX="03",1\r"'
>>> i
'"AT^SYSCFGEX="03",1\r"'
>>> i = i.strip('"')
>>> i
'AT^SYSCFGEX="03",1\r'
>>>                 
                '''
# зачем тут пауза ???
                self.msleep(1000 * 2)  # 2 сек.
# !!!
            print(f'что тут --> {scan.send_comm(opn_srl_prt, i)} <-- проверьте') # !!!   
            # я так понимаю что вы никогда не попадаете в цикл ниже
            # потому что видимо вы хотите что-то
            # делать пока ---------------------> != <------- не равно "STOP"
            while scan.send_comm(opn_srl_prt, i) == "STOP":
#                Thread.message_handler(self, id_dev, msg=date_command)
                print(f'а что тут --> {scan.send_comm(opn_srl_prt, i)} <-- проверьте') 
                self.message_handler(self, id_dev, msg=date_command)
                self.msleep(1000 * 2)  # 2 сек.
                
            if scan.send_comm(opn_srl_prt, i) == "STOP":
#                Thread.message_handler(self, id_dev, msg="Сканирование завершено")
                self.message_handler(self, id_dev, msg="Сканирование завершено")


# message_handler
    def message_handler(self, id_dev, msg):
        """ Функция "message_handler" обрабатывает поступающие сообщения
            и выводит их в ui.textEdit
            
> textEdit_plate_1.append("text") 
> я пытаюсь выполнить в каждом if функции "message_handler"

Не надо пытаться в каждом if функции "message_handler" выполнять:

    ???.textEdit_plate_1.append("text")
    
Вы должны дулать:
    self.textEdit_plate_1.append(plate)
в методе def update_thread(self, id_dev, plate, name_thread)

---    

> print(f'{id_dev}; \n{msg};') 
> в "message_handler" не принтует, т.е. в "message_handler" нет обращения.

Не обращается, потому что условие:

    while scan.send_comm(opn_srl_prt, i) == "STOP":
    
ЛОЖНОЕ и метод message_handler(self, id_dev, msg) не вызывается.
        """


        if "120" in id_dev:
# !!!
            print(f'что тут --> {msg)} <-- проверьте') # +++
            if "AT^NETSCAN=20,-110,0" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 2G", self.name_thread)
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3G", self.name_thread)
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4G", self.name_thread)
            if "Начат" or "Закончен" in msg:
                self.updateSignal.emit(
                    id_dev, f"{msg}", self.name_thread)

        if "821" in id_dev:
            if "AT^NETSCAN=20,-110,0" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 2G", self.name_thread)
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3G", self.name_thread)
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4G", self.name_thread)
            if "Начат" or "Закончен" in msg:
                self.updateSignal.emit(
                    id_dev, f"{msg}", self.name_thread)


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.thread_1 = None                 
        self.thread_2 = None                 
        self.pushButton_start.clicked.connect(self.run_th)

    def run_th(self):
        """ Функция "run_th" запускает потоки с экземплярами класса """

        device_port = self.scan_ttyusb()
        plate1 = device_port[0]  
        plate2 = device_port[1]  

        if self.thread_1 is None and self.thread_2 is None:
            self.thread_1 = Thread(plate1, 'Plate 1')
            self.thread_1.updateSignal.connect(self.update_thread)
            self.thread_1.start()
            
            self.thread_2 = Thread(plate2, 'Plate 2')
            self.thread_2.updateSignal.connect(self.update_thread)
            self.thread_2.start()
        
            self.pushButton_start.setText("Stop Thread`s")
        else:
            reply = QMessageBox.question(
                self, 
                'ВНИМАНИЕ !',
                "Вы уверены, что хотите прервать выполнение программы?",
                 QMessageBox.Yes, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.thread_1.terminate() 
                self.thread_1 = None
                self.thread_2.terminate() 
                self.thread_2 = None                
                
                self.pushButton_start.setText("Start Thread`s")    

    def update_thread(self, id_dev, plate, name_thread):
        if name_thread == 'Plate 1':
            if '821' in plate:
                self.label_plate_1.setText(f"{id_dev}")
            else:
                self.label_plate_1.setText(f"{id_dev}")
        if name_thread == 'Plate 2':
            if '120' in plate:
                self.label_plate_2.setText(f"{id_dev}")
            else:
                self.label_plate_2.setText(f"{id_dev}")

    def scan_ttyusb(self):
        """ Функция "scan_ttyusb" сканирует порты и возвращает номера портов
            к которым подключены нужные устройства
        """

        ports = list(serial.tools.list_ports.comports())
        result = ""
        for text in ports:
            if 'Pcui' in text[1]:
                txt = text[0]
                result = result + txt + ','
                device_port = result

        return device_port.split(",")

    def closeEvent(self, event):
        reply = QMessageBox.question(
            self, 
            'Информация',
            "Вы уверены, что хотите закрыть приложение?",
             QMessageBox.Yes, QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            if self.thread_1:
                self.thread_1.quit()
            del self.thread_1
            if self.thread_2:
                self.thread_2.quit()
            del self.thread_2
            
            super(MainWindow, self).closeEvent(event)
        else:
            event.ignore()
            

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main_window = MainWindow()
    main_window.setFixedSize(680, 572)
    main_window.show()
    sys.exit(app.exec_())
→ Ссылка