Остановка потоков QThread, передача текста в QTextEdit и работа QProgressBar

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

  1. Нужно инициализировать кнопку "СТОП", при клике на которую потоки должны фактически останавливаться (т.е. thread_1 и thread_2), предварительно выдавать диалоговое окно (Вы уверены, что хотите прервать выполнение программы? ОК - прерываем, Отмена - продолжаем), без блокирования основного окна и всех работающих потоков, т.е. сканирование должно продолжаться вплоть до нажатия кнопки ОК)
  2. При передаче сообщений из scanclass в функцию обработки message_handler, должны эти самые сообщения печататься .append("text") в QTextEdit каждого из потоков (для 1 потока это textEdit_plate_1, для второго это textEdit_plate_2 соответственно). Фактически функция "message_handler" не работает.
  3. При передаче сообщения, пользователь должен видеть как заполняет QStatusBar каждого из потоков (примерно 33 сообщения должно высыпаться т.е. для каждого сообщения шаг СтатусБара должен быть равен ~3,03)
  4. После того, как оба потока получили в переменной date_command - STOP, т.е. фактически отправка команд для работы с портом закончена, должно высыпаться окно dialog_adress, в котором пользователь вводит своё местоположение и местоположение должно передаваться в другой py файл, после нажатия кнопки ОК, должен запускаться py файл, в который ушла переменная со значением местоположения.

main.py

import sys

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QMainWindow

from reo_main_window import Ui_MainWindow
from dialog_adress import Ui_Form_dialog_adress

import scanclass

import serial
import serial.tools.list_ports


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 = scanclass.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)
            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 "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)
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3!", self.name_thread)
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4!", 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, "Начинаю сканирование 2!", self.name_thread)
            if "AT^NETSCAN=20,-110,1" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 3!", self.name_thread)
            if "AT^NETSCAN=20,-110,3" in msg:
                self.updateSignal.emit(
                    id_dev, "Начинаю сканирование 4!", 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.pushButton_start.clicked.connect(self.run_th)
        self.pushButton_stop.clicked.connect(self.stop_th)

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

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

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

    def stop_th(self):
        self.thread_1.finished
        self.thread_2.finished


    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(",")


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    main_window = MainWindow()
    main_window.setFixedSize(680, 572)
    # main_window.resize(680, 572)
    main_window.show()



    #     # Создание формы диалога внесения адреса и инициализация uid dialog_adress
    #     dialog = QtWidgets.QWidget()
    #     uid = Ui_Form_dialog_adress()
    #     uid.setupUi(dialog)
    #     dialog.show()
    #     dialog.setFixedSize(440, 161)


    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:
                # main3.Thread.message_handler(id_dev=self.name_dev, msg=f"Начат круг № '{i + 1}'")
               ....
               ....
               ....
                print(f"Закончен проход № '{i+1}'")
                # main2.message_handler(msg=f"Закончен круг № '{i + 1}'", id_dev=self.name_dev)

С предыдущими проблемами, мне помог пользовать @S. Nick, за что ему выражаю отдельную огромную благодарность.


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

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

Ваш вопрос должен касаться ОДНОЙ проблемы.

Я попробую предложить вам один из возможных вариантов, который должен вам помочь в решении ПЕРВОЙ вашей проблемы. Помните, что я не могу проверить работу вашего приложения.

main.py

import sys
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                                         # !

import serial
import serial.tools.list_ports


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)
            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 "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, "Начинаю сканирование 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)
#-        self.pushButton_stop.clicked.connect(self.stop_th)

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

        device_port = self.scan_ttyusb()
        plate1 = device_port[0]  
        plate2 = device_port[1]  
        
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
        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 stop_th(self):
#        self.thread_1.finished
#        self.thread_2.finished

# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    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(",")
        
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    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_())

Если у вас есть другие проблемы - задайте НОВЫЙ вопрос.

→ Ссылка