Here is a little code, where I have a thread connecting to a ftp server and showing message boxes.
I don't understant why, as soon as the msgBoxWait
dialog is closed, the app crashed (terminates before the FTP thread).
I guess this is because it is the last window displayed, however, adding a QEventLoop doesn't fix it. Can you help me ?
# coding: utf-8
import sys
import random
from PySide2 import QtCore, QtWidgets, QtGui
class Logic():
def __init__(self):
self.msgBoxWait = QtWidgets.QMessageBox()
self.ftpThread = FtpThread()
self.isConnected = False
self.loop = QtCore.QEventLoop()
def run(self):
self.ftpThread.sigIsConnected.connect(self.ftpConnected, QtCore.Qt.QueuedConnection)
self.ftpThread.finished.connect(self.ftpFinished, QtCore.Qt.QueuedConnection)
self.ftpThread.sigError.connect(self.ftpError, QtCore.Qt.QueuedConnection)
self.ftpThread.start()
QtCore.QTimer.singleShot(200, self.showWaitMsgBox)
self.loop.exec_()
def showWaitMsgBox(self):
self.msgBoxWait.setWindowTitle("Waiting")
self.msgBoxWait.setText("""Waiting for ftp connection""")
if not self.isConnected:
self.msgBoxWait.exec()
def ftpConnected(self):
print("connected")
self.isConnected = True
self.msgBoxWait.close() # <- crash here or when I click on the close button
def ftpFinished(self):
print("finished")
self.ftpThread = None
self.loop.quit()
def ftpError(self, title, message):
QtWidgets.QMessageBox.critical(None, title, message)
class FtpThread(QtCore.QThread):
sigIsConnected = QtCore.Signal()
sigError = QtCore.Signal(str, str)
def run(self):
QtCore.QThread.sleep(2)
self.sigIsConnected.emit()
QtCore.QThread.sleep(1)
self.sigError.emit("error", "An error appened")
QtCore.QThread.sleep(3)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
logic = Logic()
QtCore.QTimer.singleShot(0, logic.run)
sys.exit(app.exec_())
By default, Qt is configured so that if the last window is closed, the application will end because it is generally the expected behavior but that is not your case because there are times when you need to continue running even if there are no open windows. The solution is to set the quitOnLastWindowClosed
property to False:
# coding: utf-8
import sys
import random
from PySide2 import QtCore, QtWidgets, QtGui
class Logic:
def __init__(self):
self.msgBoxWait = QtWidgets.QMessageBox()
self.ftpThread = FtpThread()
self.isConnected = False
def run(self):
self.ftpThread.sigIsConnected.connect(
self.ftpConnected, QtCore.Qt.QueuedConnection
)
self.ftpThread.finished.connect(self.ftpFinished, QtCore.Qt.QueuedConnection)
self.ftpThread.sigError.connect(self.ftpError, QtCore.Qt.QueuedConnection)
self.ftpThread.start()
QtCore.QTimer.singleShot(200, self.showWaitMsgBox)
def showWaitMsgBox(self):
self.msgBoxWait.setWindowTitle("Waiting")
self.msgBoxWait.setText("""Waiting for ftp connection""")
if not self.isConnected:
self.msgBoxWait.exec()
def ftpConnected(self):
print("connected")
self.isConnected = True
self.msgBoxWait.close()
def ftpFinished(self):
print("finished")
self.ftpThread = None
# QtCore.QCoreApplication.quit() should be used
# to close the entire application if necessary
QtCore.QCoreApplication.quit()
def ftpError(self, title, message):
QtWidgets.QMessageBox.critical(None, title, message)
class FtpThread(QtCore.QThread):
sigIsConnected = QtCore.Signal()
sigError = QtCore.Signal(str, str)
def run(self):
QtCore.QThread.sleep(2)
self.sigIsConnected.emit()
QtCore.QThread.sleep(1)
self.sigError.emit("error", "An error appened")
QtCore.QThread.sleep(3)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
app.setQuitOnLastWindowClosed(False)
logic = Logic()
QtCore.QTimer.singleShot(0, logic.run)
sys.exit(app.exec_())