pyqt5 gui crashes at the end of the process

Viewed 39

What I intended :

  1. Push the "next" button on Main window -> open the Sub window
  2. Push the "Start" button on Sub window -> Long-running task starts
  3. After the task is over -> Open the Last window
  4. If error is occurred -> Open Error window
  5. Push (Last window or Error window)'s "Back to Main" button -> Back to Main window

Real Work :

  1. Push the "next" button on Main window -> open the Sub window
  2. Push the "Start" button on Sub window -> Long-running task starts
  3. After the task is over -> Open the Last window
  4. If error is occurred -> Open Error window but after 1~2 seconds it crashes
  5. Last window's "Back to Main" button works well

and below is my code ↓

  1. Main window

     from PyQt5 import QtCore, QtWidgets
     from Sub import SubWindow, Thread
     from Last import LastWindow
     from Error import ErrorWindow
     import sys
     class Main_Window(QtWidgets.QMainWindow) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
         def initUI(self):
             self.pb_next = QtWidgets.QPushButton(self)
             self.pb_next.setGeometry(QtCore.QRect(540, 480, 112, 34))
             self.pb_next.setText('Next')
             self.pb_next.clicked.connect(self.CallSub)
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
         def CallSub(self):
             self.hide()
             self.SW = SubWindow()
             self.SW.show()
             print('call sub')
             self.SW.PB.clicked.connect(self.RSC)
             print('rsc')
         def RSC(self):
             print('start rsc')
             self.text1 = 'hosts.txt'
             self.text2 = 'access.txt'
             self.thread = Thread(self.text1, self.text2)
             self.thread.error.connect(self.thread.quit)
             self.thread.error.connect(self.SW.deleteLater)
             self.thread.error.connect(self.thread.deleteLater)
             self.thread.error.connect(self.CallError)
             self.thread.finished.connect(self.thread.quit)
             self.thread.finished.connect(self.SW.deleteLater)
             self.thread.finished.connect(self.thread.deleteLater)
             self.thread.finished.connect(self.CallLast)
             self.thread.start()
         def CallLast(self):
             self.LW = LastWindow()
             self.SW.hide()
             self.LW.show()
             self.LW.pb_next.clicked.connect(self.CallMain)
         def CallError(self):
             self.EW = ErrorWindow()
             self.SW.hide()
             self.EW.show()
             self.EW.pb_next.clicked.connect(self.CallMain)
         def CallMain(self):
             self.LW.hide()
             self.show()
     def ExceptionHook(exctype, value, traceback):
         sys.__excepthook__(exctype, value, traceback)
     if __name__ == "__main__":
         sys.excepthook = ExceptionHook
         app = QtWidgets.QApplication(sys.argv)
         mw = Main_Window()
         mw.show()
         sys.exit(app.exec_())
    
  2. sub window

     from time import sleep
     from PyQt5 import QtCore, QtWidgets, QtGui
     class SubWindow(QtWidgets.QWidget) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
         def initUI(self):
             # Quit PushButton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
             self.PB = QtWidgets.QPushButton(self)
             self.PB.setGeometry(QtCore.QRect(280, 240, 100, 25))
             self.PB.setText('Start')
     class Thread(QtCore.QThread):
         finished = QtCore.pyqtSignal()
         error = QtCore.pyqtSignal()
         def __init__(self, t1, t2):
             super().__init__()
             self.t1 = t1
             self.t2 = t2
         def RunSecurityCheck(self, t1, t2):
             print('start sleep')
             if t1 != 'host.txt' :
                 print('error occured')
                 self.error.emit()
             sleep(10)
         def run(self):
             self.RunSecurityCheck(self.t1, self.t2)
             print('finished occured')
             self.finished.emit()
    
  3. Last window

     from PyQt5 import QtCore, QtWidgets
     class LastWindow(QtWidgets.QWidget) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
         def initUI(self):
             # Next PushButton
             self.pb_next = QtWidgets.QPushButton(self)
             self.pb_next.setGeometry(QtCore.QRect(540, 480, 112, 34))
             self.pb_next.setText('Back to Main')
             # Quit PushButton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
             # LB1 + LB2
             self.LB1 = QtWidgets.QLabel(self)
             self.LB1.setGeometry(QtCore.QRect(140, 220, 221, 18))
             self.LB1.setText('Complete.')
    
  4. Error window

     from PyQt5 import QtCore, QtWidgets
     class ErrorWindow(QtWidgets.QWidget) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
         def initUI(self):
             # Next PushButton
             self.pb_next = QtWidgets.QPushButton(self)
             self.pb_next.setGeometry(QtCore.QRect(540, 480, 112, 34))
             self.pb_next.setText('Back to Main')
             # Quit PushButton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
             # LB1 + LB2
             self.LB1 = QtWidgets.QLabel(self)
             self.LB1.setGeometry(QtCore.QRect(140, 220, 221, 18))
             self.LB1.setText('Error Occured.')
    

I think it's because of the error signal of the sub window... but not sure. I added the excepthook code, but there is no error messages but only get like below

-> Process finished with exit code -1073740791 (0xC0000409)

How could I fix the codes for keeping from gui crashing...?

1 Answers

0xC0000409 is STATUS_STACK_BUFFER_OVERRUN, which is some sort of segmentation fault that happens because you are trying to quit and delete a thread while it is running, and not terminate it properly. which is a buggy behavior for QThread.

to fix this issue, you should replace in your main file

self.thread.error.connect(self.thread.quit)

with

self.thread.error.connect(self.thread.terminate)

this will fix the segmentation fault, but it's never a good idea to forcefully terminate a running Thread in python, because this functionality isn't implemented in python, and things might not get properly cleaned and you might run into memory leaks or segmentation faults, a better option is to have your thread return (or raise an error) when the error happens so it will terminate gracefully, and if the termination command is coming from a different thread, a better approach is to have the thread check on a variable that controls its termination, so that it will terminate itself, instead of being forcefully terminated.

i am not saying you can't forcefully terminate QThreads, i am just saying from exprience it's better to have them terminate themselves.

Edit: as pointed out by musicamante, in a comment, you shouldn't be overriding the thread finished signal, or emit it within the thread, as it should be called by the thread automatically when it is over, and this will lead to the signal being called more than once ... which is bad and will lead to unexpected behvior, but likely won't cause your app to crash.

Related