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()在谷歌和火狐下失效