Search code examples
pythonpython-3.xmultithreadingpyqtpyqt5

QObject::setParent: Cannot set parent, new parent is in a different thread in Python


I am facing a problem with the GUI I developed using PyQt5.

The app tries to update the value of a progress bar of the window from a different thread (QThreadPool()). When it tries to update the progress bar, the python gives a warning as below:

QObject::setParent: Cannot set parent, new parent is in a different thread

This doesn't affect the app to certain extent but after a while the app crashes with the following error message.

QBackingStore::endPaint() called with active painter on backingstore paint device

Could anyone help me to get rid of the first warning? I guess that will solve the problem.

I will post a minimal example of the code.

main.py

import sys
from PyQt5.QtCore import *
from PyQt5 import QtCore, QtGui, QtWidgets
import thread_for_audio_record
import time


class FW(object):
    def __init__(self):
        self.threadpool = QThreadPool()

    def f_w(self, window):
        self.window = window
        self.window.setObjectName("Test")
        self.window.resize(1054, 700)
        self.window.setStyleSheet("background-color: rgb(180, 180, 180);")
        self.central_widget = QtWidgets.QWidget(self.window)
        self.central_widget.setObjectName("centralwidget")
        self.horizontal_layout = QtWidgets.QHBoxLayout(self.central_widget)
        self.horizontal_layout.setObjectName("horizontalLayout")
        spacer_item = QtWidgets.QSpacerItem(129, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontal_layout.addItem(spacer_item)
        self.vertical_layout = QtWidgets.QVBoxLayout()
        self.vertical_layout.setObjectName("verticalLayout")
        spacer_item_1 = QtWidgets.QSpacerItem(128, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.vertical_layout.addItem(spacer_item_1)
        self.label_utterance = QtWidgets.QLabel(self.central_widget)
        font = QtGui.QFont()
        font.setPointSize(15)
        font.setBold(True)
        font.setBold(True)
        font.setWeight(75)
        self.label_utterance.setFont(font)
        self.label_utterance.setAlignment(QtCore.Qt.AlignCenter)
        self.label_utterance.setObjectName("label_utterance")
        self.vertical_layout.addWidget(self.label_utterance)
        self.text_edit_utterance = QtWidgets.QTextEdit(self.central_widget)
        self.text_edit_utterance.setStyleSheet("background-color: rgb(255, 255, 255);")
        self.text_edit_utterance.setObjectName("text_edit_utterance")
        self.text_edit_utterance.setReadOnly(True)
        self.text_edit_utterance.setFont(font)
        self.vertical_layout.addWidget(self.text_edit_utterance)
        self.line_edit_message_for_user = QtWidgets.QLineEdit(self.central_widget)
        self.line_edit_message_for_user.setStyleSheet("background-color: rgb(255, 255, 255);")
        self.line_edit_message_for_user.setObjectName("lineEdit")
        self.progressBar = QtWidgets.QProgressBar(self.central_widget)
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")
        self.vertical_layout.addWidget(self.progressBar)
        self.vertical_layout.addWidget(self.line_edit_message_for_user)
        self.push_button_start_recording = QtWidgets.QPushButton(self.central_widget)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.push_button_start_recording.setFont(font)
        self.push_button_start_recording.setStyleSheet("background-color: rgb(85, 170, 0);")
        self.push_button_start_recording.setObjectName("pushButton_start_
                                                           recording")

        self.push_button_start_recording.clicked.connect(self.start_button_
                                                               func)
        self.vertical_layout.addWidget(self.push_button_start_recording)
        self.push_button_end_recording = QtWidgets.QPushButton(self.central_widget)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.push_button_end_recording.setFont(font)
        self.push_button_end_recording.setStyleSheet("background-color: rgb(182, 0, 0);")
        self.push_button_end_recording.setObjectName("pushButton_end_
                                                        recording")
        self.vertical_layout.addWidget(self.push_button_end_recording)
        self.label_status = QtWidgets.QLabel(self.central_widget)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.label_status.setFont(font)
        self.label_status.setObjectName("label_status")
        self.vertical_layout.addWidget(self.label_status)
        self.text_edit_status = QtWidgets.QTextEdit(self.central_widget)
        self.text_edit_status.setStyleSheet("background-color: rgb(255, 255, 255);")
        self.text_edit_status.setObjectName("textEdit_status")
        self.text_edit_status.setReadOnly(True)
        self.vertical_layout.addWidget(self.text_edit_status)
        self.push_button_record_again = QtWidgets.QPushButton(self.central_widget)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.push_button_record_again.setFont(font)
        self.push_button_record_again.setStyleSheet("background-color: rgb(255, 255, 127);")
        self.push_button_record_again.setObjectName("pushButton_record_
                                                               again")
        self.vertical_layout.addWidget(self.push_button_record_again)
        self.push_button_next_utterance = QtWidgets.QPushButton(self.central_widget)
        font = QtGui.QFont()
        font.setBold(True)
        font.setWeight(75)
        self.push_button_next_utterance.setFont(font)
        self.push_button_next_utterance.setStyleSheet("background-color: rgb(85, 170, 0);")
        self.push_button_next_utterance.setObjectName("pushButton_next_
                                                         utterance")
        self.vertical_layout.addWidget(self.push_button_next_utterance)
        spacer_item_2 = QtWidgets.QSpacerItem(128, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.vertical_layout.addItem(spacer_item_2)
        self.horizontal_layout.addLayout(self.vertical_layout)
        spacer_item_3 = QtWidgets.QSpacerItem(128, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontal_layout.addItem(spacer_item_3)
        self.window.setCentralWidget(self.central_widget)
        self.menubar = QtWidgets.QMenuBar(self.window)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1054, 21))
        self.menubar.setObjectName("menubar")
        self.window.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(self.window)
        self.statusbar.setObjectName("statusbar")
        self.window.setStatusBar(self.statusbar)
        self.retranslate_ui(self.window)
        QtCore.QMetaObject.connectSlotsByName(self.window)

    def retranslate_ui(self, window):

        _translate = QtCore.QCoreApplication.translate
        window.setWindowTitle(_translate("Utterance Recording", "Test"))
        self.label_utterance.setText(_translate("Utterance Recording", "Test"))
        self.line_edit_message_for_user.setText(_translate("Utterance Recording", "Test."))
        self.text_edit_utterance.setText(_translate("utterance_parsing", ('Test')))
        self.push_button_start_recording.setText(_translate("Utterance Recording", "Start Recording"))
        self.push_button_end_recording.setText(_translate("Utterance Recording", "End Recording"))
        self.label_status.setText(_translate("Utterance Recording", "Test"))
        self.push_button_record_again.setText(_translate("Utterance Recording", "Test"))
        self.push_button_next_utterance.setText(_translate("Utterance Recording", "Test"))

    def inside_thread(self):
        percentage = 0
        while percentage < 120:
            percentage = percentage + 20
            time.sleep(0.2)
            self.progressBar.setValue(percentage)

    def start_button_func(self):
        self.worker = thread_for_audio_record.Worker(self.inside_thread)
        self.threadpool.start(self.worker)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    utterance_parsing_window = QtWidgets.QMainWindow()
    utterance_parsing = FW()
    utterance_parsing.f_w(utterance_parsing_window)
    utterance_parsing_window.show()
    sys.exit(app.exec_())

thread_for_audio_record.py

import os
from PyQt5.QtCore import *


class Worker(QRunnable):

    def __init__(self, fn):
        super(Worker, self).__init__()
        self.fn = fn
        self.originating_PID = os.getpid()
        self._running = True

    @pyqtSlot()
    def run(self):
        self.fn()

Click the Start Recording button and you will see the progress bar getting updated. But the warning message is given cause the update is from a different thread.


Solution

  • First, do not modify the code generated by Qt Designer as PyQt recommends, instead create another class that inherits from the appropriate widget and use the initial class as an interface.

    Going to the point, Qt does not allow the GUI to be updated from another thread directly, in your case the setValue of the QProgressBar is called in the secondary thread causing the painting to be made in the secondary thread so Qt complains that it is not guaranteed operation (for example in your case it does not generate problems apparently but Qt does not guarantee that this always happens). The update of the GUI from other threads must be indirectly, which can be through signals, QEvent, QMetaObject::invokeMethod, etc. In this case I will use the signals:

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    import thread_for_audio_record
    import time
    
    
    class FW(object):
        def f_w(self, window):
            self.window = window
            self.window.setObjectName("Test")
            self.window.resize(1054, 700)
            self.window.setStyleSheet("background-color: rgb(180, 180, 180);")
            self.central_widget = QtWidgets.QWidget(self.window)
            self.central_widget.setObjectName("centralwidget")
            self.horizontal_layout = QtWidgets.QHBoxLayout(self.central_widget)
            self.horizontal_layout.setObjectName("horizontalLayout")
            spacer_item = QtWidgets.QSpacerItem(129, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.horizontal_layout.addItem(spacer_item)
            self.vertical_layout = QtWidgets.QVBoxLayout()
            self.vertical_layout.setObjectName("verticalLayout")
            spacer_item_1 = QtWidgets.QSpacerItem(128, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
            self.vertical_layout.addItem(spacer_item_1)
            self.label_utterance = QtWidgets.QLabel(self.central_widget)
            font = QtGui.QFont()
            font.setPointSize(15)
            font.setBold(True)
            font.setBold(True)
            font.setWeight(75)
            self.label_utterance.setFont(font)
            self.label_utterance.setAlignment(QtCore.Qt.AlignCenter)
            self.label_utterance.setObjectName("label_utterance")
            self.vertical_layout.addWidget(self.label_utterance)
            self.text_edit_utterance = QtWidgets.QTextEdit(self.central_widget)
            self.text_edit_utterance.setStyleSheet("background-color: rgb(255, 255, 255);")
            self.text_edit_utterance.setObjectName("text_edit_utterance")
            self.text_edit_utterance.setReadOnly(True)
            self.text_edit_utterance.setFont(font)
            self.vertical_layout.addWidget(self.text_edit_utterance)
            self.line_edit_message_for_user = QtWidgets.QLineEdit(self.central_widget)
            self.line_edit_message_for_user.setStyleSheet("background-color: rgb(255, 255, 255);")
            self.line_edit_message_for_user.setObjectName("lineEdit")
            self.progressBar = QtWidgets.QProgressBar(self.central_widget)
            self.progressBar.setProperty("value", 0)
            self.progressBar.setObjectName("progressBar")
            self.vertical_layout.addWidget(self.progressBar)
            self.vertical_layout.addWidget(self.line_edit_message_for_user)
            self.push_button_start_recording = QtWidgets.QPushButton(self.central_widget)
            font = QtGui.QFont()
            font.setBold(True)
            font.setWeight(75)
            self.push_button_start_recording.setFont(font)
            self.push_button_start_recording.setStyleSheet("background-color: rgb(85, 170, 0);")
            self.push_button_start_recording.setObjectName("pushButton_start_ recording")
            self.vertical_layout.addWidget(self.push_button_start_recording)
            self.push_button_end_recording = QtWidgets.QPushButton(self.central_widget)
            font = QtGui.QFont()
            font.setBold(True)
            font.setWeight(75)
            self.push_button_end_recording.setFont(font)
            self.push_button_end_recording.setStyleSheet("background-color: rgb(182, 0, 0);")
            self.push_button_end_recording.setObjectName("pushButton_end_recording")
            self.vertical_layout.addWidget(self.push_button_end_recording)
            self.label_status = QtWidgets.QLabel(self.central_widget)
            font = QtGui.QFont()
            font.setBold(True)
            font.setWeight(75)
            self.label_status.setFont(font)
            self.label_status.setObjectName("label_status")
            self.vertical_layout.addWidget(self.label_status)
            self.text_edit_status = QtWidgets.QTextEdit(self.central_widget)
            self.text_edit_status.setStyleSheet("background-color: rgb(255, 255, 255);")
            self.text_edit_status.setObjectName("textEdit_status")
            self.text_edit_status.setReadOnly(True)
            self.vertical_layout.addWidget(self.text_edit_status)
            self.push_button_record_again = QtWidgets.QPushButton(self.central_widget)
            font = QtGui.QFont()
            font.setBold(True)
            font.setWeight(75)
            self.push_button_record_again.setFont(font)
            self.push_button_record_again.setStyleSheet("background-color: rgb(255, 255, 127);")
            self.push_button_record_again.setObjectName("pushButton_record_again")
            self.vertical_layout.addWidget(self.push_button_record_again)
            self.push_button_next_utterance = QtWidgets.QPushButton(self.central_widget)
            font = QtGui.QFont()
            font.setBold(True)
            font.setWeight(75)
            self.push_button_next_utterance.setFont(font)
            self.push_button_next_utterance.setStyleSheet("background-color: rgb(85, 170, 0);")
            self.push_button_next_utterance.setObjectName("pushButton_next_utterance")
            self.vertical_layout.addWidget(self.push_button_next_utterance)
            spacer_item_2 = QtWidgets.QSpacerItem(128, 13, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
            self.vertical_layout.addItem(spacer_item_2)
            self.horizontal_layout.addLayout(self.vertical_layout)
            spacer_item_3 = QtWidgets.QSpacerItem(128, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
            self.horizontal_layout.addItem(spacer_item_3)
            self.window.setCentralWidget(self.central_widget)
            self.menubar = QtWidgets.QMenuBar(self.window)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 1054, 21))
            self.menubar.setObjectName("menubar")
            self.window.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(self.window)
            self.statusbar.setObjectName("statusbar")
            self.window.setStatusBar(self.statusbar)
            self.retranslate_ui(self.window)
            QtCore.QMetaObject.connectSlotsByName(self.window)
    
        def retranslate_ui(self, window):
    
            _translate = QtCore.QCoreApplication.translate
            window.setWindowTitle(_translate("Utterance Recording", "Altran Audio Manager"))
            self.label_utterance.setText(_translate("Utterance Recording", "Test"))
            self.line_edit_message_for_user.setText(_translate("Utterance Recording", "Test."))
            self.text_edit_utterance.setText(_translate("utterance_parsing", ('Test')))
            self.push_button_start_recording.setText(_translate("Utterance Recording", "Start Recording"))
            self.push_button_end_recording.setText(_translate("Utterance Recording", "End Recording"))
            self.label_status.setText(_translate("Utterance Recording", "Test"))
            self.push_button_record_again.setText(_translate("Utterance Recording", "Test"))
            self.push_button_next_utterance.setText(_translate("Utterance Recording", "Test"))
    
    
    class MainWindow(QtWidgets.QMainWindow, FW):
        progressChanged = QtCore.pyqtSignal(int)
    
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self.threadpool = QtCore.QThreadPool()
            self.f_w(self)
            self.push_button_start_recording.clicked.connect(self.start_button_func)
            self.progressChanged.connect(self.progressBar.setValue)
    
        def inside_thread(self):
            percentage = 0
            while percentage < 120:
                percentage = percentage + 20
                time.sleep(0.2)
                self.progressChanged.emit(percentage)
    
        def start_button_func(self):
            self.worker = thread_for_audio_record.Worker(self.inside_thread)
            self.threadpool.start(self.worker)
    
    
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        w = MainWindow()
        w.show()
        sys.exit(app.exec_())