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.