I’ve got some issues with threading in my program. I have a main program and also a GUI-Class, created with PyQt. I’ve created a small example of my problems. When I start the python script and press start, the progress bar is working fine, but I get the following warnings:
QBackingStore::endPaint() called with active painter on backingstore paint device
QObject::setParent: Cannot set parent, new parent is in a different thread
These alarms occur due to different threads. But to be honest, I have no idea how to handle the objects, like this progress bar of the gui-class. I’ve tried many ways but haven’t found a good solution for this. By pressing start, it starts, by pressing stop the progress bar stops. But here is the next issue, mostly my kernel dies when I press stop. I guess it’s connected to the same problem with the threading?!
The reason for the threading is so that the script can still be interacted with while it is running.
Attached are my two python files: my program and also the gui.
MAINPROGRAM:
#MAINPROGRAM
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import threading
import time
class exampleprogram(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(exampleprogram, self).__init__(parent)
self.setupUi(self)
self.pushButton_Start.clicked.connect(self.start)
self.pushButton_Stop.clicked.connect(self.stop)
self.running = False
def start(self):
if(self.running == False):
print("Start")
self.thread = threading.Thread(target=self.run, args=())
self.thread.start()
def stop(self):
print("Stop")
self.running = False
def run(self):
self.running = True
x = 0
thread = threading.currentThread()
while getattr(thread, "do_run", True):
self.thread.do_run = self.running
if(x == 100):
thread.do_run = False
self.progressBar.setValue(x)
time.sleep(0.1)
x = x 1
self.stop()
def main():
app = QApplication(sys.argv)
form = exampleprogram()
form.show()
app.exec_()
if __name__ == '__main__':
main()
GUI:
#GUI
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(573, 92)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.layoutWidget = QtWidgets.QWidget(self.centralwidget)
self.layoutWidget.setGeometry(QtCore.QRect(20, 20, 195, 30))
self.layoutWidget.setObjectName("layoutWidget")
self.formLayout_3 = QtWidgets.QFormLayout(self.layoutWidget)
self.formLayout_3.setContentsMargins(0, 0, 0, 0)
self.formLayout_3.setObjectName("formLayout_3")
self.pushButton_Start = QtWidgets.QPushButton(self.layoutWidget)
self.pushButton_Start.setObjectName("pushButton_Start")
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.pushButton_Start)
self.pushButton_Stop = QtWidgets.QPushButton(self.layoutWidget)
self.pushButton_Stop.setObjectName("pushButton_Stop")
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pushButton_Stop)
self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
self.progressBar.setGeometry(QtCore.QRect(230, 20, 311, 23))
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Test Gui"))
self.pushButton_Start.setText(_translate("MainWindow", "Start"))
self.pushButton_Stop.setText(_translate("MainWindow", "Stop"))
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_())
Does anyone have an idea, how to fix it?
EDIT: After stopping the run-function it should be possible to run it again by pressing the start button. Therefore it doesn't matter if it was stopped by the counter x or by pressing the stop button. It's just important not too start multiple times the function.
Many thanks in advance!
Andrew
CodePudding user response:
Whilst it's possible to use Python threads with PyQt, it's generally better to use QThread with a separate worker object, since it more easily allows thread-safe communication between the worker thread and main thread via signals and slots. One of the main problems with your example is precisely that you are attempting to perform GUI-related operations outside of the main thread, which is not supported by Qt (hence the warning messages).
If you want to start, stop and pause the worker from the GUI, it will require some careful handling to ensure the worker and thread shut down cleanly if the user tries to close the window while they are still running. In the example below, I have used a simple abort mechanism in the closeEvent; if you want more sophisticated handling, you could, for example, ignore() the close-event while the worker is still running. Pressing Start will start/contine the worker, and pressing Stop will pause it. Once the worker has finished, pressing Start will completely re-start it (i.e. it will go back to zero):
class Worker(QtCore.QObject):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
self._paused = True
self._count = -1
def start(self):
self._paused = False
if self._count < 0:
print('Start')
self._run()
else:
print('Continue')
def stop(self, *, abort=False):
self._paused = True
if abort:
print('Abort')
self._count = -1
else:
print('Pause')
def _run(self):
self._count = 0
self._paused = False
while 0 <= self._count <= 100:
if not self._paused:
QtCore.QThread.msleep(100)
self._count = 1
self.progress.emit(self._count)
self.stop(abort=True)
self.finished.emit()
print('Finished')
class exampleprogram(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(exampleprogram, self).__init__(parent)
self.setupUi(self)
self.thread = QtCore.QThread(self)
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.start)
self.worker.finished.connect(self.thread.quit)
self.worker.progress.connect(self.progressBar.setValue)
self.pushButton_Start.clicked.connect(self.start)
self.pushButton_Stop.clicked.connect(self.stop)
def start(self):
if self.thread.isRunning():
self.worker.start()
else:
self.thread.start()
def stop(self):
if self.thread.isRunning():
self.worker.stop()
def closeEvent(self, event):
self.worker.stop(abort=True)
self.thread.quit()
self.thread.wait()