self.close() 将关闭程序,即使 closeEvent() 内部有 self.ignore()

Posted

技术标签:

【中文标题】self.close() 将关闭程序,即使 closeEvent() 内部有 self.ignore()【英文标题】:self.close() will close the program even though closeEvent() has self.ignore() inside 【发布时间】:2019-08-16 18:42:44 【问题描述】:

当我在我的应用程序上单击“X”,然后在 MessageBox 中按“否”时,程序不会关闭。但是当我隐藏程序并从系统托盘的菜单中单击“退出”并从消息框中单击“否”时,程序仍将关闭成功....

我的代码是这样的:

    exitAction = menu.addAction("Quit")
    exitAction.triggered.connect(self.close)

那么我的 closeEvent() 代码是:

    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'Quit', 'Are You Sure to Quit?', QMessageBox.No | QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

编辑: 我意识到每当菜单弹出一个QMessageBox,无论我选择哪个选项,整个程序仍然会关闭,我想可能是这个问题?:

os._exit(app2.exec_())

我在消息框之前添加了 self.show() 并且它可以工作,有没有办法让它在没有 self.show() 的情况下工作?因为我允许用户在程序隐藏时从系统托盘退出程序

   def closeEvent(self, event):
        self.show() << I added this and it works
        reply = QMessageBox.question(self, 'Quit', 'Are You Sure to Quit?', QMessageBox.No | QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

可重现的代码:

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUi()

    def initUi(self):
        self.resize(350, 150)
        self.setWindowTitle("Test")
        pb_min = QPushButton("Minimise the Program", self)
        pb_min.clicked.connect(self.pbMin)

        h_box = QHBoxLayout()
        h_box.addStretch()

        h_box.addWidget(pb_min)
        h_box.addStretch()

        self.setLayout(h_box)

    def pbMin(self):
        menu = QMenu()
        showAction = menu.addAction('Show')
        showAction.triggered.connect(self.showGUI)
        exitAction = menu.addAction("Quit")
        exitAction.triggered.connect(self.close)
        self.hide()
        self.mSysTrayIcon = QSystemTrayIcon(self)
        icon = QIcon("test.png")
        self.mSysTrayIcon.setIcon(icon)
        self.mSysTrayIcon.setContextMenu(menu)
        self.mSysTrayIcon.setToolTip("Show Test")
        self.mSysTrayIcon.activated.connect(self.onActivated)
        self.mSysTrayIcon.show()

    def showGUI(self):
        self.show()
        self.mSysTrayIcon.hide()

    def onActivated(self, reason):
        if reason == self.mSysTrayIcon.Trigger:
            self.show()
            self.mSysTrayIcon.hide()


    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'Quit', 'Are You Sure to Quit?', QMessageBox.Yes | QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    os._exit(app.exec_())

【问题讨论】:

您使用的是什么操作系统?有没有可能是 MacOS? @musicamante 我使用的是 Windows 10 好的,我看到了你的编辑,现在更清楚了。几个问题:“从系统托盘的菜单中退出”,您的意思是您正在使用 QSystemTrayIcon,或者您正在单击任务栏中的“关闭窗口”? @DanielSeow 提供minimal reproducible example @musicamante 从 QSystemTrayIcon 退出 【参考方案1】:

您提供的代码存在一些问题。

首先,你不应该在每次最小化窗口时都创建一个新的系统托盘图标;虽然它可能一些意义,但它可能是一个问题,因为在某些平台(特别是 Windows)中,图标可能并不总是被“删除”(如“隐藏” ) 当你创建一个新的时,除非你明确地删除它(通常使用deleteLater())。

那么,虽然您说“程序仍然会成功关闭”,但它可能不会。这通常取决于平台(不同的操作系统和操作系统版本),但这不是重点。

此外,由于您需要一种统一的方式来确保用户真的想要退出,因此您应该提供相应的方法来做到这一点,并因此对其返回值做出反应,因为closeEvent和触发的动作连接以不同的方式响应。

我已经修改了您的代码,统一了一个“closeRequest”方法,可以更好地响应用户交互。

一些笔记。

    当您使用“非标准”QWidgets(例如 QSystemTrayIcon)时,您需要更好地控制程序实际退出的方式/时间,并且在设置 QApplication.setQuitOnLastWindowClosed(bool) 时需要特别小心。

    使用os._exit sys.exit 相同:“os._exit() 通常只能在 fork() 之后的子进程中使用”,即 (很少)并发事件循环的情况,例如将 PyQt 事件循环与 PyGame 一起使用(参见official docs)。

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUi()

    def initUi(self):
        self.resize(350, 150)
        self.setWindowTitle("Test")
        pb_min = QPushButton("Minimise the Program", self)
        pb_min.clicked.connect(self.pbMin)

        h_box = QHBoxLayout()
        h_box.addStretch()

        h_box.addWidget(pb_min)
        h_box.addStretch()

        self.setLayout(h_box)

        menu = QMenu()
        showAction = menu.addAction('Show')
        showAction.triggered.connect(self.showGUI)
        exitAction = menu.addAction("Quit")
        exitAction.triggered.connect(self.quitRequest)
        self.mSysTrayIcon = QSystemTrayIcon(self)
        icon = QIcon("test.png")
        self.mSysTrayIcon.setIcon(icon)
        self.mSysTrayIcon.setContextMenu(menu)
        self.mSysTrayIcon.setToolTip("Show Test")
        self.mSysTrayIcon.activated.connect(self.onActivated)

    def pbMin(self):
        self.hide()
        self.mSysTrayIcon.show()

    def showGUI(self):
        self.show()
        self.mSysTrayIcon.hide()

    def onActivated(self, reason):
        if reason == self.mSysTrayIcon.Trigger:
            self.show()
            self.mSysTrayIcon.hide()

    def quitRequest(self):
        if self.closeRequest():
            QApplication.quit()

    def closeRequest(self):
        reply = QMessageBox.question(self, 'Quit', 'Are You Sure to Quit?', QMessageBox.Yes | QMessageBox.No)
        return reply == QMessageBox.Yes

    def closeEvent(self, event):
        if self.closeRequest():
            event.accept()
            QApplication.quit()
        else:
            event.ignore()

if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())

【讨论】:

使用您的代码,我尝试仅在程序开始运行时创建系统托盘。但是当我运行程序时会显示系统托盘 嗯,它不应该,但是 QSystemTrayIcon 被称为跨系统和 Qt 版本的行为不稳定。您是否尝试在创建后隐藏它? @DanielSeow 您使用的是您的代码还是我的代码?因为,正如我在回答中所解释的那样,在大多数情况下,不断创建 QSystemTrayIcon 的新实例不会导致隐藏以前的实例。如果不是这样,请尝试更好地解释您的努力,可能通过改进您的问题或在 cmets 中描述它们;我知道这很难做到,但这就是这里的做法,而且有很好的理由。 是的,我正在使用您的代码,当我运行它时,QSystemTrayIcon 会显示在系统托盘的开头。所以我在 self.mSysTrayIcon.activated.connect(self.onActivated) 之后添加了 self.mSysTrayIcon.hide() 但系统托盘图标仍然显示 您确定您没有多次创建 QSystemTrayIcon 吗?你在哪里/如何隐藏它?你确定你没有在其他任何地方展示它吗?【参考方案2】:

找到了自己的解决方案,当主程序被隐藏时,系统托盘显示的消息框一旦关闭,整个程序也会关闭, 所以添加app.setQuitOnLastWindowClosed(False)以避免关闭MessageBox导致整个程序关闭。 最后在closeEvent()中添加quit()

【讨论】:

以上是关于self.close() 将关闭程序,即使 closeEvent() 内部有 self.ignore()的主要内容,如果未能解决你的问题,请参考以下文章

Python Tornado Websocket连接在关闭后仍然打开

javascript的window.close()在谷歌和火狐下失效

关闭标签页Chrome / Safari浏览器-Javascript

js关闭当前页面

如何在网页中加入关闭窗口的按钮

如何使用线程自动关闭 PyQt/PySide 窗口?