Home > database >  Python threading - Cannot set parent, new parent is in a different thread
Python threading - Cannot set parent, new parent is in a different thread

Time:07-02

I'm trying to make a GUI that, once you press a button a scrollArea would dynamically get updated. I tried using QThread to not lock the GUI and to show the user the scrollArea as it gets populated with elements. I made a thread worker class where inside a loop I'm creating the elements for the scrollArea.The elements are put on a QVBoxLayout and I'm trying to pass that layout to my GUI scrollArea using a signal of type QObject. I'm receiving this error: QObject::setParent: Cannot set parent, new parent is in a different thread I don't know what to do to solve this

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class WorkerThread(QThread):
    
    my_signal = pyqtSignal(QObject)

    def run(self):
        top_layout = QtWidgets.QVBoxLayout()
        for i in range(2500):
            group_box = QtWidgets.QGroupBox()
            group_box.setTitle('box {0}'.format(i))
            label = QtWidgets.QLabel()
            label.setText('label {0}'.format(i))
            layout = QtWidgets.QHBoxLayout(group_box)
            layout.addWidget(label)
            top_layout.addWidget(group_box)
            self.my_signal.emit(top_layout)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(640, 480)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(520, 30, 71, 51))
        self.pushButton.setObjectName("pushButton")
        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        self.scrollArea.setGeometry(QtCore.QRect(0, 90, 511, 461))
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 505, 455))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        MainWindow.setCentralWidget(self.centralwidget)  
        self.pushButton.clicked.connect(self.click)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "add"))




    def click(self):
        self.worker = WorkerThread()
        self.worker.start()
        self.worker.my_signal.connect(self.update_scrollArea)


    def update_scrollArea(self, top_layout):
        top_widget = QtWidgets.QWidget()
        top_widget.setLayout(top_layout)
        self.scrollArea.setWidget(top_widget)




if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

CodePudding user response:

I've had problems with creating PyQt5 objects in threads in the past, and one option for you would be to use a timer instead.

class Ui_MainWindow(object):
    def __init__(self):
        self.timer = QtCore.QBasicTimer()
        self.timer.start(250, self)
        self.i = 0
        
    def timerEvent(self, event):
        if self.i < 2500:
            self.i =1
            top_layout = QtWidgets.QVBoxLayout()
            group_box = QtWidgets.QGroupBox()
            group_box.setTitle('box {0}'.format(i))
            label = QtWidgets.QLabel()
            label.setText('label {0}'.format(i))
            layout = QtWidgets.QHBoxLayout(group_box)
            layout.addWidget(label)
            top_layout.addWidget(group_box)
            self.my_signal.emit(top_layout)
    
    def setupUi(self, MainWindow):
        ...

This way you will interrupt the main loop once every 250msec to update one loop iteration at a time and you will not freeze up the GUI.

  • Related