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.
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_())