PySide2:不能在不退出 APP 的情况下关闭 QMessageBox

Posted

技术标签:

【中文标题】PySide2:不能在不退出 APP 的情况下关闭 QMessageBox【英文标题】:PySide2 : cannot close a QMessageBox without quitting the APP 【发布时间】:2020-01-14 10:08:18 【问题描述】:

这是一个小代码,我有一个线程连接到 ftp 服务器并显示消息框。 我不明白为什么,一旦关闭msgBoxWait 对话框,应用程序就会崩溃(在 FTP 线程之前终止)。

我猜这是因为它是显示的最后一个窗口,但是,添加 QEventLoop 并不能解决它。 你能帮帮我吗?

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

【问题讨论】:

您是否尝试将app 的属性quitOnLastWindowClosed 设置为False 【参考方案1】:

默认情况下,Qt 配置为如果关闭最后一个窗口,应用程序将结束,因为这通常是预期的行为,但您的情况并非如此,因为有时您需要继续运行,即使没有打开窗户。解决方法是将quitOnLastWindowClosed属性设置为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_())

【讨论】:

你说得对,谢谢!但是,我不明白为什么手动添加的事件循环并不能阻止程序结束。 @Kiruahxh QEventLoops 依赖于 QApplication,所以如果 QApplication 没有运行,那么其他事件循环将无法工作。

以上是关于PySide2:不能在不退出 APP 的情况下关闭 QMessageBox的主要内容,如果未能解决你的问题,请参考以下文章

有啥方法可以在不关闭其 BaseStream 的情况下关闭 StreamWriter?

如何在不离开当前视图的情况下关闭 modalViewController?

如何在不关闭模式的情况下关闭从 modalViewController 推送的 viewController?

iOS:在不访问其父 ViewController 的情况下关闭和呈现 ModalViewController

如何在不关闭 TCP 连接的情况下关闭处理 TCP 请求的线程?

在不添加完成键的情况下关闭数字键盘式键盘