Обновления виджетов основного потока данными из дополнительного потока

Написал функцию для обновления виджетов, но когда дело дошло до обновления виджета textEdit, в который нужно поместить разные данные из БД, перестало все получаться.

В классе написал sql запрос, чтобы он при сканировании инвентарника искал и сравнивал в БД, и после всю строку выводил в отдельные виджеты.

Подскажите, что можно сделать чтобы данные выводил и не выдавал ошибок.

Сама функция:

class ProgressBarThread(QThread):
    def __init__(self, mainwindow, parant=None):
        super().__init__()
        self.mainwindow = mainwindow
        
    def AddTextEdit(self):
        conn = sqlite3.connect(DeviceBase)
        cursor = conn.cursor()
        Inv = """SELECT Inv FROM DeviceList WHERE Inv = res1[0][0]"""
        Model = """SELECT Model FROM DeviceList WHERE Inv = ?"""
        Serial = """SELECT Serial FROM DeviceList WHERE Inv = ?"""
        Owner = """SELECT Owner FROM DeviceList WHERE Inv = ?"""
        Location = """SELECT Location FROM DeviceList WHERE Inv = ?"""
        cursor = conn.cursor()
        cursor.execute(Inv, (res1[0][0],))
        inv = cursor.fetchone()
        cursor.execute(Model, (res1[0][0],))
        model = cursor.fetchone()
        cursor.execute(Serial, (res1[0][0],))
        serial = cursor.fetchone()
        cursor.execute(Owner, (res1[0][0],))
        owner = cursor.fetchone()
        cursor.execute(Location, (res1[0][0],))
        location = cursor.fetchone()
        conn.commit()
        return inv, model, serial, owner, location
    
    def run(self):
        global res1
        global test_rfid
        current_rfid = ''
        while True:
            self.mainwindow.pushButton_2.setEnabled(True)
            with open(textbase) as file:
                RFID_original = file.readlines()[-1]
                RFID = RFID_original.partition(';')[0]
            if current_rfid != RFID:
                font = QFont("Arial", 8)
                self.mainwindow.RFID_Label.setFont(font)
                self.mainwindow.RFID_Label.setText(RFID_original)
                if RFID in str(RFID_list):
                    *res1, = list(filter(lambda x: x[1] == RFID, RFID_list))
                    res1 = np.array(res1)
                    self.mainwindow.Device_Label.setText(f"<html><head/><body><p align=\"center\"><span style=\" font-size:20pt; font-weight:600;\">{str(res1[0][0])}</span></p></body></html>")
                    current_rfid = RFID
                    for item in DeviceList:
                        if str(res1[0][0]) == str(item[0]):
                            inv, model, serial, owner, location = self.AddTextEdit()
                            self.mainwindow.textEdit.setText(" ".join(str(cell) for cell in inv))
                            self.mainwindow.textEdit_2.setText(" ".join(str(cell) for cell in model))
                            self.mainwindow.textEdit_3.setText(" ".join(str(cell) for cell in serial))
                            self.mainwindow.textEdit_4.setText(" ".join(str(cell) for cell in owner))
                            self.mainwindow.textEdit_5.setText(" ".join(str(cell) for cell in location))
                    else:
                        test_rfid = RFID
                        self.mainwindow.pushButton_2.setEnabled(True)
                        
            time.sleep(1)

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

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

Вы не предоставляете минимально-воспроизводимый пример, который демонстрирует проблему и в этом ВАША ОСНОВНАЯ ПРОБЛЕМА.

Но я вижу, что вы уже давно не можете решить проблему и попробую вам помочь.

Запомните, что нельзя взаимодействовать с виджетами в дополнительном потоке.
Т.е. элементы графического интерфейса (все, что унаследовано или связано с подклассом QWidget) должны быть созданы и доступны только из основного потока Qt.

Еще раз, графический интерфейс не должен обновляться непосредственно в другом потоке, который не является основным потоком, и это также означает, что вы не должны создавать виджеты в другом потоке читайте Основы работы с потоками

Сигналы и слоты используются для связи между объектами.
Механизм сигналов и слотов является центральной особенностью Qt и, вероятно, той частью, которая больше всего отличается от функций, предоставляемых другими платформами.

Также внимательно читаем Support for Signals and Slots
и то что я написал в комментариях кода.

Очень приблизительно ваш код может выглядеть так:

# Какие-то импорты

RFID_list = ???
DeviceList = ???
textbase = ???


class ProgressBarThread(QThread):
    values_changed = pyqtSignal(object)                # 1  создаем сигналы
    label_changed = pyqtSignal(str, str)               # 2 
  
    def __init__(self):
        super().__init__()
#        self.mainwindow = mainwindow
        self.res1 = None                               # ?
        self.test_rfid = None                          # ?

    def run(self):
# забудьте про глобальные переменные
#        global res1
#        global test_rfid

        current_rfid = ''
        
        while True:
#            self.mainwindow.pushButton_2.setEnabled(True)
            with open(textbase) as file:
                RFID_original = file.readlines()[-1]
                RFID = RFID_original.partition(';')[0]
            if current_rfid != RFID:
#                font = QFont("Arial", 8)
#                self.mainwindow.RFID_Label.setFont(font)
#                self.mainwindow.RFID_Label.setText(RFID_original)

                self.label_changed.emit('RFID_Label', str(RFID_original))    # !!! 5
                self.msleep(10)
                
                if RFID in str(RFID_list):
                    *res1, = list(filter(lambda x: x[1] == RFID, RFID_list))
                    res1 = np.array(res1)
#                    self.mainwindow.Device_Label.setText(f"<html><head/><body><p align=\"center\"><span style=\" font-size:20pt; font-weight:600;\">{str(res1[0][0])}</span></p></body></html>")

                    text = f"<html><head/><body><p align=\"center\"><span style=\" font-size:20pt; font-weight:600;\">{str(res1[0][0])}</span></p></body></html>"
                    self.label_changed.emit('Device_Label', text)                # !!! 5
                    self.msleep(10)

                    current_rfid = RFID
                    for item in DeviceList:
                        if str(res1[0][0]) == str(item[0]):
                        
                            inv, model, serial, owner, location = self.addTextEdit()
# Излючаем сигнал и передаем аргументы подключенному слоту
# !!! 5
                            self.values_changed.emit((inv, model, serial, owner, location)) 
            
#                            self.mainwindow.textEdit.setText(" ".join(str(cell) for cell in inv))
#                            self.mainwindow.textEdit_2.setText(" ".join(str(cell) for cell in model))
#                            self.mainwindow.textEdit_3.setText(" ".join(str(cell) for cell in serial))
#                            self.mainwindow.textEdit_4.setText(" ".join(str(cell) for cell in owner))
#                            self.mainwindow.textEdit_5.setText(" ".join(str(cell) for cell in location))

                    else:
                        test_rfid = RFID

#                        self.mainwindow.pushButton_2.setEnabled(True)
                        self.label_changed.emit('pushButton_2', 'True')       # !!! 5

            self.msleep(1000)  # Заставляет текущий поток спать (1000) миллисекунд msecs.
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

# я надеюсь что из метода addTextEdit() вы возвращаете то что вам надо
    def addTextEdit(self):
        conn = sqlite3.connect(DeviceBase)
        cursor = conn.cursor()
        Inv = """SELECT Inv FROM DeviceList WHERE Inv = res1[0][0]"""
        Model = """SELECT Model FROM DeviceList WHERE Inv = ?"""
        Serial = """SELECT Serial FROM DeviceList WHERE Inv = ?"""
        Owner = """SELECT Owner FROM DeviceList WHERE Inv = ?"""
        Location = """SELECT Location FROM DeviceList WHERE Inv = ?"""
        cursor = conn.cursor()
        cursor.execute(Inv, (res1[0][0],))
        inv = cursor.fetchone()
        cursor.execute(Model, (res1[0][0],))
        model = cursor.fetchone()
        cursor.execute(Serial, (res1[0][0],))
        serial = cursor.fetchone()
        cursor.execute(Owner, (res1[0][0],))
        owner = cursor.fetchone()
        cursor.execute(Location, (res1[0][0],))
        location = cursor.fetchone()
        conn.commit()
        return inv, model, serial, owner, location


# очень приблизительный вариан того что у вас написано, я так думаю 
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self) 

        ...

        self.pushButton_2 = QPushButton()
        
        self.RFID_Label = QLabel()
        font = QFont("Arial", 8)
        self.RFID_Label.setFont(font)
        
        self.Device_Label = QLabel()
        
        self.textEdit = QTextEdit()
        self.textEdit_2 = QTextEdit()
        self.textEdit_3 = QTextEdit()
        self.textEdit_4 = QTextEdit()
        self.textEdit_5 = QTextEdit()
        
        ...
     

# я незнаю каким событием вы запускаете дополнительный поток 
# пость это будет вызов метода startThread(), сделайте это как там у вас
    def startThread(self):
        self.thread = ProgressBarThread()                                   # !!!
        self.thread.values_changed.connect(self.slot_values_changed)        # !!! 3
        self.thread.label_changed.connect(self.slot_value_label)            # !!! 4
        self.thread.start()                                                 # !!!
        self.pushButton_2.setEnabled(True)

    def slot_value_label(self, name, value_changed):                        # !!! 6
        #print(f'{name}: {value_changed}') 
        if name = 'RFID_Label':
            self.RFID_Label.setText(value_changed)
        elif name = 'Device_Label':
            self.Device_Label.setText(value_changed)
        elif name = 'pushButton_2':
            self.pushButton_2.setEnabled(True)
       
    def slot_values_changed(self, values_changed):                           # !!! 6
        #print(f'{values_changed}') 
        
        inv, model, serial, owner, location = values_changed

        self.textEdit.setText(" ".join(str(cell) for cell in inv))
        self.textEdit_2.setText(" ".join(str(cell) for cell in model))
        self.textEdit_3.setText(" ".join(str(cell) for cell in serial))
        self.textEdit_4.setText(" ".join(str(cell) for cell in owner))
        self.textEdit_5.setText(" ".join(str(cell) for cell in location))

    ...        
        
        
→ Ссылка