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

Posted

技术标签:

【中文标题】在QT中关闭窗口之前获取应用程序退出信号【英文标题】:Get application quit signal before windows are closed in QT 【发布时间】:2017-12-11 20:52:38 【问题描述】:

我有一个带有多个主窗口的 Qt 应用程序,我想在应用程序即将退出时收到一个信号,而所有窗口都还在运行。

QApplication.aboutToQuit 信号对我不起作用,因为它仅在所有窗口关闭后才会触发。

我看到的所有其他答案都建议实现主窗口的 closeEvent() 函数,但我有多个主窗口,我找不到任何方法来区分 CloseEvents 和普通 closeEvent 之间的区别窗口被关闭以及整个应用程序在QMD+Q 之后关闭或在任务栏中使用单击退出或出于任何原因退出时的关闭事件。

只有在整个应用程序即将退出但在任何窗口关闭之前,我如何才能获得信号?

当我按下 Cmd+Q 或右键退出任务栏图标时会发生什么:

    (此时我想要一个信号) 所有窗口都获得一个 closeEvent() aboutToQuit() 触发 应用退出

我想要的是在任何事情发生之前得到一个信号,并且所有的窗户仍然打开。

编辑:最小示例

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

【问题讨论】:

请提供您的申请的Minimal, complete, verifiable example(不是完整的!)。我建议您为每个 QMainWindow 重新实现 closeEvent() 并从这里发出自定义信号,并在您的 QApplication 中使用一个插槽,但它可能不适合您。 @LoïcG。现在添加了示例,但closeEvent 没有帮助,每次用户关闭窗口时都会调用它,我希望它仅在 Alt F4 等之后整个应用程序退出时运行。 @Stitch95 也就是说,您需要一个信号,在所有窗口关闭之前提醒您,并告诉您最后一个窗口是否关闭。 @eyllanesc 是的,我只想要一个 aboutToQuit 信号,但在任何窗口实际关闭之前。我不在乎最后一个窗口之前是如何关闭的,我只是在示例中添加了 closeEvent ,因为这是建议的解决方案,所以我想说明为什么它不适合我想要的方式 您不能将QApplication.quitOnLastWindowClosed 设置为False,以便在最后一个窗口关闭后不立即退出并在退出应用程序之前执行您想做的事情吗? 【参考方案1】:

如果您想在用户关闭窗口时触发信号,但在最后一个窗口关闭后不立即退出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_()

这样,当窗口关闭时,会发出closing() 信号

【讨论】:

谢谢,但我只想在应用程序即将关闭时发出信号(因为 CMD+Q 或因为用户在任务栏上单击退出或应用程序关闭的任何原因)但在此之前所有窗户都已关闭。【参考方案2】:

如果是spontaneous,您可以过滤CloseEvent

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 上不起作用,当我关闭任何窗口时也会退出,而不仅仅是在我关闭整个应用程序时。 我不知道“关闭整个应用程序”是什么意思,但是上面的代码退出了,因为在 beforeQuit() 中调用了 self.exit()(至少在 Windows 上)。所以beforeQuit 会在任何窗口关闭时被调用,从这里你可以做任何你想做的事情。 是的,但我不希望它在任何窗口关闭时运行,我只希望它在整个应用程序因 alt+f4 关闭时运行。 Alt+F4 只为我关闭窗口。使用您的代码,应用程序仅在 5 个窗口关闭时关闭,因此在 5 * Alt+F4 如果您希望能够关闭每个QWidget,但不希望QApplication 在最后一个小部件关闭时退出,您可以查看QApplication.setQuitOnLastWindowClosed() 方法【参考方案3】:

使用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

【讨论】:

以上是关于在QT中关闭窗口之前获取应用程序退出信号的主要内容,如果未能解决你的问题,请参考以下文章

winform中关闭退出和打开新窗口的几种方式

我可以从 Main 构造函数中关闭程序吗?

在 Qt 中关闭新的非子窗口

如何阻止用户在 Javascript 中关闭窗口?

在Qt中关闭串口之前发送串口消息

打开“模态”窗口并在基于选项卡组的应用程序中关闭