我正在尝试制作一个能够拥有多个窗口的小型python程序。问题是当我试图实现一个菜单项来退出程序,同时关闭所有窗口时。我曾尝试使用qApp.close()
和qApp.exit()
,但如果它们允许有效地退出程序,则不会为仍打开的窗口生成关闭事件,这会阻止我保存修改后的数据或阻止我离开应用程序。最好的做法是什么?我可以理解不能取消退出过程,但能够提议保存修改后的数据是我真正想要的。
import sys
from PyQt5.QtWidgets import *
opened_windows = set()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.create_actions()
opened_windows.add(self)
def closeEvent(self, ev):
if QMessageBox.question(self, 'Closing', 'Really close?') == QMessageBox.Yes:
ev.accept()
opened_windows.remove(self)
else:
ev.ignore()
def create_action(self, action_callback, menu, action_name):
action = QAction(action_name, self)
action.triggered.connect(action_callback)
menu.addAction(action)
def create_actions(self):
_file_menu = self.menuBar().addMenu('&File')
self.create_action(self.on_new, _file_menu, '&New')
_file_menu.addSeparator()
self.create_action(self.on_close, _file_menu, '&Close')
self.create_action(self.on_quit, _file_menu, '&Quit')
self.create_action(self.on_exit, _file_menu, '&Exit')
def on_new(self):
win = MainWindow()
win.show()
def on_close(self):
self.close()
def on_quit(self):
qApp.quit()
def on_exit(self):
qApp.exit(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
status = app.exec()
print(len(opened_windows), ' window(s) opened')
print('status = ', status)
sys.exit(status)
目前我正在修改on_close
和on_exit
,如下所示:
def on_exit(self):
for w in opened_windows.copy():
w.on_close()
if len(opened_windows) == 0:
qApp.exit(1)
但我想知道我是否错过了一个更好的方法,它不会强迫我维护一组打开的窗户。
原因
重要的是要理解,应用程序和主窗口是相关的,但不是同一回事。所以,当你想关闭程序时,不要麻烦关闭应用程序。请关闭主窗口。来自QCloseEvent的文档:
关闭事件发送到用户想要关闭的窗口小部件,通常通过从窗口菜单中选择"关闭"或单击X标题栏按钮。当您调用QWidget::close((以编程方式关闭小部件时,也会发送它们。
解决方案
-
将退出操作的
triggered
信号连接到MainWindow
的close
插槽。在您的情况下,而不是:self.create_action(self.on_exit, _file_menu, '&Exit')
写入:
self.create_action(self.close, _file_menu, '&Exit').
-
在
MainWindow
中定义信号closed
,并从closedEvent
的实现中发射,例如在opened_windows.remove(self)
的位置 -
在
on_new
中,将win.closed
连接到self.close
示例
以下是我建议您如何更改代码以实现所提出的解决方案:
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
closed = pyqtSignal()
def __init__(self):
super().__init__()
self.create_actions()
def closeEvent(self, ev):
if QMessageBox.question(self, 'Closing', 'Really close?') == QMessageBox.Yes:
ev.accept()
self.closed.emit()
else:
ev.ignore()
def create_action(self, action_callback, menu, action_name):
action = QAction(action_name, self)
action.triggered.connect(action_callback)
menu.addAction(action)
def create_actions(self):
_file_menu = self.menuBar().addMenu('&File')
self.create_action(self.on_new, _file_menu, '&New')
_file_menu.addSeparator()
self.create_action(self.close, _file_menu, '&Exit')
def on_new(self):
win = MainWindow()
win.show()
win.closed.connect(self.close)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
status = app.exec()
print('status = ', status)
sys.exit(status)
编辑:我想知道我以前是怎么错过的。有一个QApplication::closeAllWindows插槽,它正是我想要的,其示例是绑定退出。
有一种方法可以建议保存quit
和exit
上的修改数据,即信号QCoreApplication::aboutToQuit
。
注意,尽管Qt文档说用户交互是不可能的,但至少在PyQt5中,我可以在没有明显问题的情况下使用QMessageBox。