pyqt5 - не работают методы addWidget и addLayout в отдельных потоках

Такое происходит, например, при попытке добавить QLabel в QVBoxLayout в отдельном потоке. Вот демонстрация:

import sys
from threading import Thread

from PyQt5.QtWidgets import QVBoxLayout, QLabel, QWidget, QApplication


class Ui(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.resize(300, 200)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.layout.addWidget(QLabel(text="hello"))

        # Тут попытка добавить QLabel в отдельном потоке
        Thread(target=self.add_label).start()

    def add_label(self):
        self.layout.addWidget(QLabel(text="new label"))


app = QApplication(sys.argv)
ui = Ui()
ui.show()
sys.exit(app.exec_())

В результате в QVBoxLayout будет присутствовать исключительно один QLabel, который был добавлен в конструкторе:

Лишь один QLabel

Какие существуют методы борьбы с этим? Это какая-то ошибка?


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

Автор решения: Zemf4you

Благодаря комментарию Alexander Chernin смог решить проблему с помощью PyqtSignal.

Пример:

import sys
from threading import Thread

from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QWidget, QApplication

class Communicate(QObject):
    customEvent = pyqtSignal()

class Ui(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.resize(300, 200)
        self.layout = QVBoxLayout()
        self.setLayout(self.layout)
        self.com = Communicate()
        self.com.customEvent.connect(self.add_label)
        self.layout.addWidget(QLabel(text="hello"))
        Thread(target=self.other_thread).start()

    def other_thread(self):
        self.com.customEvent.emit()

    def add_label(self):
        self.layout.addWidget(QLabel(text="new label"))


app = QApplication(sys.argv)
ui = Ui()
ui.show()
sys.exit(app.exec_())
→ Ссылка
Автор решения: S. Nick

Обычно, это может выглядеть так:

import sys
#from threading import Thread
from PyQt5 import QtCore, QtWidgets, QtGui  
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QWidget, QApplication


class MyThread(QtCore.QThread):
    mysignal = QtCore.pyqtSignal(str) 

    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        for i in range(2):
            self.msleep(1000)
            text = "Hello World"
            #Передача данных из потока через сигнал
            self.mysignal.emit(f"{text} {i}")


class Ui(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(300, 200)

        self.label = QLabel(text="hello")

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.label)

        # Тут попытка добавить QLabel в отдельном потоке
#        Thread(target=self.add_label).start()

        self.mythread = MyThread()
        self.mythread.mysignal.connect(self.add_label)
        self.mythread.start()

    def add_label(self, text):
        self.layout.addWidget(QLabel(text))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = Ui()
    ui.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка