1

This is PyQt5 code. I want to count down 5 seconds and update every 1 second. Then application will pop up QMessageBox.
But it will closed after click button on QMessageBox due to QObject::setParent: Cannot set parent, new parent is in a different thread,
detail code below:

from PyQt5 import QtCore, QtGui, QtWidgets
import sys, time
from _thread import *

class ThreadClass(QtCore.QThread):
    # Create the signal
    sig = QtCore.pyqtSignal(int)

    def __init__(self, mw, parent=None):
        self.mw = mw
        self.mbox = QtWidgets.QMessageBox()
        super().__init__(parent)
        self.sig.connect(self.showtime)

    def showtime(self, t):
        self.mw.label.setText(str(t))

    def run(self):
        for t in range(5):
            self.sig.emit(t)
            time.sleep(1)
        self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(253, 181)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(90, 100, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 20, 211, 16))
        self.label.setObjectName("label")
        self.tc = ThreadClass(self)
        self.pushButton.clicked.connect(lambda: self.tc.start())
        #self.pushButton.clicked.connect(lambda: start_new_thread(showtime, (self.label, )))
        MainWindow.setCentralWidget(self.centralwidget)


        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.pushButton.setText("Show")
        self.label.setText("Time")         

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    ex = Ui_MainWindow()
    w = QtWidgets.QMainWindow()
    ex.setupUi(w)
    w.show()
    sys.exit(app.exec_())

App will close after clicked button on QMessageBox and error message below.

QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
QObject::setParent: Cannot set parent, new parent is in a different thread
Raoslaw Szamszur
  • 1,723
  • 12
  • 21
movep
  • 21
  • 4

1 Answers1

3

Try it:

import sys   #, time
from PyQt5 import QtCore, QtGui, QtWidgets
from _thread import *

class ThreadClass(QtCore.QThread):
    # Create the signal
    sig = QtCore.pyqtSignal(int)
    finish = QtCore.pyqtSignal()                # +++

    def __init__(self, mw, parent=None):
#        self.mw = mw
#        self.mbox = QtWidgets.QMessageBox()
        super().__init__(parent)
#        self.sig.connect(self.showtime)

#    def showtime(self, t):
#        self.mw.label.setText(str(t))

    def run(self):
        for t in range(5):
            self.sig.emit(t)
            #time.sleep(1)
            QtCore.QThread.msleep(1000)

        self.finish.emit()                                             # +++
#        self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(253, 181)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(90, 100, 75, 23))
        self.pushButton.setObjectName("pushButton")

        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 20, 211, 16))
        self.label.setObjectName("label")

        self.tc = ThreadClass(self)
        self.pushButton.clicked.connect(lambda: self.tc.start())
        #self.pushButton.clicked.connect(lambda: start_new_thread(showtime, (self.label, )))

        self.tc.sig.connect(self.showtime)            # +++
        self.tc.finish.connect(self.finishTime)       # +++


        MainWindow.setCentralWidget(self.centralwidget)


        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.pushButton.setText("Show")
        self.label.setText("Time")         

# +++
    def showtime(self, t):
        self.label.setText(str(t)) 

# +++
    def finishTime(self):
        self.mbox = QtWidgets.QMessageBox()
        self.mbox.about(QtWidgets.QMainWindow(), "Title", "Finished")


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w   = QtWidgets.QMainWindow()
    ex  = Ui_MainWindow()
    ex.setupUi(w)
    w.show()
    sys.exit(app.exec_())

enter image description here

S. Nick
  • 12,879
  • 8
  • 25
  • 33
  • 3
    Adding some explanation would make this answer more useful. – mx0 Nov 10 '18 at 15:24
  • Agreed! The gist is: Use signals/slots to communicate between GUI/main thread and worker thread and spawn the dialog from the GUI/main thread. – bugmenot123 Sep 20 '22 at 12:51