在QT中关闭窗口前获取应用程序退出信号

3
我有一个具有多个主窗口的Qt应用程序,希望在所有窗口仍然打开时获取一个应用程序即将退出的信号。因为QApplication.aboutToQuit信号只会在所有窗口关闭后才会被触发,所以该信号对我来说不可用。我看到的所有其他答案都建议实现主窗口的closeEvent()函数,但是我有多个主窗口,并且我找不到任何区分正常单个窗口关闭的CloseEvent和整个应用程序关闭的CloseEvent的方式,例如使用QMD+Q或通过任务栏单击“退出”按钮等任何原因。我如何在所有窗口关闭之前而应用程序即将退出时获取一个信号?按下Cmd+Q或右键单击任务栏图标时会发生什么:
  1. (我想在此时获得信号)<-
  2. 所有窗口都会收到closeEvent()
  3. 触发aboutToQuit()信号
  4. 应用程序退出
我想要的是在所有这些操作发生之前并且所有窗口仍然打开时获取一个信号。编辑:最小示例。
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget

class Win(QWidget):
    def closeEvent(self, event: QCloseEvent):
        # implementing a custom closeEvent doesn't help me
        # this is called for every normal manual window close and when the application quits
        # I only want to run something when the full application gets shut down, not just this window
        # can't see any way to differentiate between the two
        print("closeEvent", event.type(), event)
        return super().closeEvent(event)


if __name__ == '__main__':
    app = QApplication([])
    app.aboutToQuit.connect(lambda :print("about to quit, this is too late, by now all windows are closed"))

    #What I need
    # app.beforeQuitSignal.connect(lambda :print("signal that it's gonna quit, before any of the windows are closed"))

    #stuff I've tried that didn't work either
    app.quit = lambda *args:print("quit, doesnt get called")
    app.exit = lambda *args:print("exit, doesnt get called")
    app.closeAllWindows = lambda *args:print("closeAllWindows, doesnt get called")

    mainwins = []
    for i in range(5):
        win = Win()
        win.setGeometry(100*i,100*i, 400,400), win.show(), win.raise_()
        mainwins.append(win)

    app.exec_()

请提供您的应用程序的最小完整可验证示例(不是完整的!)。 我建议您为每个QMainWindow重新实现closeEvent(),并从此处发出自定义信号,并在您的QApplication中使用一个槽,但也许这对您来说不太合适。 - Loïc G.
@LoïcG. 现在已经添加了示例,但 closeEvent 并没有帮助,因为它每次用户关闭窗口时都会被调用,我希望它只在整个应用程序在按下 Alt F4 等键后退出时运行。 - Stitch95
@Stitch95,你想要一个信号,在所有窗口关闭之前提醒你,并告诉你最后一个窗口是通过关闭还是其他方式关闭的。 - eyllanesc
@eyllanesc 是的,我只想要一个关于即将退出的信号,但在任何窗口实际关闭之前。我不关心最后一个窗口是如何关闭的,我只是在示例中添加了closeEvent,因为那被建议作为解决方案,所以我想展示为什么它不适用于我想要的方式。 - Stitch95
你不能只是将 QApplication.quitOnLastWindowClosed 设置为 False,以便在最后一个窗口关闭后不立即退出,并在退出应用程序之前执行想要的操作吗? - Loïc G.
显示剩余2条评论
3个回答

2

如果你想在用户关闭一个窗口时触发一个信号,但在最后一个窗口关闭后不立即退出QApplication,你可以使用QApplication.setQuitOnLastWindowClosed()

from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget


class Win(QWidget):
    closing = pyqtSignal()

    def closeEvent(self, event: QCloseEvent):
        print("Window {} closed".format(self))
        self.closing.emit()
        return super().closeEvent(event)


class MyApp(QApplication):
    def __init__(self, *args):
        super(MyApp, self).__init__(*args)

        self.setQuitOnLastWindowClosed(False)
        self.lastWindowClosed.connect(self.onLastClosed)

        self.mainwins = []
        for i in range(5):
            win = Win()
            win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
            self.mainwins.append(win)

    def onLastClosed(self):
        print("Last windows closed, exiting ...")
        self.exit()


if __name__ == '__main__':
    app = MyApp([])
    app.exec_()

当窗口关闭时,会发出一个信号。

谢谢但我只想在应用程序即将关闭时发出信号(因为CMD + Q或因为用户在任务栏上单击退出,或者由于任何原因应用程序关闭),但在任何窗口关闭之前。 - Stitch95

1

如果CloseEventspontaneous,则可以进行过滤:

from PyQt5.QtCore import pyqtSignal, QEvent
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QApplication, QWidget


class Win(QWidget):
    closing = pyqtSignal()
    def closeEvent(self, event: QCloseEvent):
        self.closing.emit()
        if event.type() == QEvent.Close and event.spontaneous():
            event.ignore()
        else:
            return super().closeEvent(event)


class MyApp(QApplication):
    def __init__(self, *args):
        super(MyApp, self).__init__(*args)

        self.mainwins = []
        for i in range(5):
            win = Win()
            win.closing.connect(self.beforeQuit)
            win.setGeometry(100 * i, 100 * i, 400, 400), win.show(), win.raise_()
            self.mainwins.append(win)

    def beforeQuit(self):
        print("Exiting")
        # Do what you want here ...
        self.exit()  # Terminate the QApplication

if __name__ == '__main__':
    app = MyApp([])
    app.exec_()

在我的OSX上这个不起作用,当我关闭任何窗口时它也会退出,而不仅仅是关闭整个应用程序。 - Stitch95
是的,但我不希望它在任何窗口关闭时运行,我只希望当整个应用程序因为按下alt+f4而关闭时才运行。 - Stitch95
Alt+F4只能关闭窗口。使用您的代码,应用程序仅在关闭5个窗口后才会关闭,因此需要按5次Alt+F4。 - Loïc G.
如果您想关闭每个 QWidget 但不希望在最后一个窗口小部件关闭时退出 QApplication,则可以查看 QApplication.setQuitOnLastWindowClosed() 方法。 - Loïc G.
@Stitch95,我基于setQuitOnLastWindowClosed()添加了另一种解决方案。希望它能有所帮助。 - Loïc G.
显示剩余3条评论

0
使用installEventFilter在任何窗口关闭之前拦截应用程序的退出事件:
class AppManager(QObject):
    def __init__(self):
        super().__init__()
        qApp.installEventFilter(self)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Quit:
            self.saveWindowStates()
        return False

    def saveWindowStates(self):
        # code to save the application's state

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接