我要么不完全了解Qt的事件传播是如何工作的,要么不了解什么,但是我不明白为什么对于QPushButton派生的类和QWidget派生的类本身都没有精确调用closeEvent。

wid.closeEvent()是否不应该触发所有子小部件的closeEvents?

#!/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from Qt.QtCore import *
from Qt.QtWidgets import *
from Qt.QtGui import *

class butt(QPushButton):
    def __init__(self, parent, name='Button'):
        super(self.__class__, self).__init__(parent)
        self.name = name

    def closeEvent(self, e):
        print('butt closeevent')
        e.accept()


class wid(QWidget):
    def __init__(self, parent=None):
        super(self.__class__, self).__init__(parent)
        self.initUI()

    def initUI(self):
        #self.setAttribute(Qt.WA_DeleteOnClose)
        self.vl = QVBoxLayout(self)
        self.button = butt(self)
        self.button.setText('test1')
        self.vl.addWidget(self.button)
        self.button.clicked.connect(QCoreApplication.quit)

    def closeEvent(self, e):
        print('wid closeevent')
        e.accept()


def show():
    app = QApplication(sys.argv)
    win = QMainWindow()
    widget = wid(win)
    win.setCentralWidget(widget)
    win.show()
    app.exec_()


if __name__ == "__main__":
    show()


我预计会看到2行
    关闭事件
    对接closeevent
作为输出,但我什么也看不到。为什么closeEvent不被他们调用?

最佳答案

在下面的示例中,当您直观地按下按钮时,将观察到相同的行为:窗口将关闭,但是我们看到了区别,在第一个窗口中,它称为closeEvent(),在第二个窗口中,它不是。

范例1:

#!/bin/env python
# -*- coding: utf-8 -*-

from Qt import QtCore, QtGui, QtWidgets


class Button(QtWidgets.QPushButton):
    def closeEvent(self, event):
        print("button closeEvent")
        event.accept()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    button = Button(text="Press me")
    button.clicked.connect(button.close)
    button.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()


范例2:

#!/bin/env python
# -*- coding: utf-8 -*-

from Qt import QtCore, QtGui, QtWidgets


class Button(QtWidgets.QPushButton):
    def closeEvent(self, event):
        print("button closeEvent")
        event.accept()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    button = Button(text="Press me")
    button.clicked.connect(QtCore.QCoreApplication.quit)
    button.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()


为什么在调用QCoreApplication :: quit时不调用closeEvent?
因为此方法用于退出Qt的事件循环,并且如果没有事件循环,则事件(QCloseEvent)不起作用。



关闭窗口小部件时,不会关闭子窗口小部件,即,仅当窗口小部件关闭时,才会调用其自己的closeEvent。因此,如果您希望调用小部件的closeevent,请调用方法close。

#!/bin/env python
# -*- coding: utf-8 -*-

from Qt import QtCore, QtGui, QtWidgets


class Button(QtWidgets.QPushButton):
    def __init__(self, name="Button", parent=None):
        super(Button, self).__init__(parent)
        self.m_name = name

    def closeEvent(self, event):
        print("button closeEvent")
        event.accept()


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.initUI()

    def initUI(self):
        vl = QtWidgets.QVBoxLayout(self)
        button = Button()
        button.setText("test1")
        vl.addWidget(button)
        button.clicked.connect(self.close)

    def closeEvent(self, event):
        print("Widget closeevent")
        event.accept()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QMainWindow()
    widget = Widget()
    w.setCentralWidget(widget)
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()


在前面的示例中,根据您与窗口小部件的交互方式,您将获得以下行为:


如果按下按钮,则该小部件将关闭,因此将其称为closeEvent,并且不会调用该按钮的closeEvent,因为即使您的孩子也没有关闭它。
如果在窗口中按下“ X”按钮,则不会将其称为小部件的closeEvent,而是QMainWindow,其解释与上一个相同。


结论:


每种类型的事件都有其自己的工作流程,某些事件仅由窗口小部件接收,而子项则不接收,而其他事件则将信息发送给子项。
close方法使用事件循环来通知该窗口小部件将被关闭,但是QCoreApplication :: quit()终止事件循环。


更新:

当用户在Qt窗口上按下“ X”按钮时,为什么不调用wid.closeEvent?主窗口不是应该在所有子控件上调用closeEvent然后正确销毁它们吗?

不,一件事是关闭窗口小部件,而另一件事是破坏窗口小部件,它可以在不关闭窗口的情况下被破坏,而关闭窗口不涉及破坏对象。

正如已经指出的那样,关闭窗口并不意味着将其删除,但是可能会打开其他窗口,但是如果默认关闭了最后一个QApplication窗口,则暗示终止小部件的eventloop将终止,这不一定意味着调用close方法。

要理解,让我们使用以下代码:

from Qt import QtCore, QtGui, QtWidgets


class Button(QtWidgets.QPushButton):
    def closeEvent(self, event):
        print("closeEvent Button")
        super(Button, self).closeEvent(event)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        button_quit = Button(
            text="quit",
            clicked=QtCore.QCoreApplication.quit
        )
        button_close = Button(
            text="close",
            clicked=self.close
        )

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(button_quit)
        lay.addWidget(button_close)

    def closeEvent(self, event):
        print("closeEvent Widget")
        super(Widget, self).closeEvent(event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())


有2个按钮,对于第一个调用QCoreApplication :: quit()的按钮,它将结束事件循环,因此所有小部件都将被销毁,在这种情况下,对于第二个按钮,将不调用closeEvent它将在窗口附近被调用,因此它将调用其closeEvent而不是其子级的closeEvents。

我的实际问题是在closeEvent中具有saveUI()函数,并且在关闭窗口时未调用小部件分层销毁

如果您想按层次结构调用closeEvent方法,则必须手动调用close方法,因为Qt并不是那样设计的。在下一部分中,有一个示例:

from PyQt5 import QtCore, QtGui, QtWidgets


class PushButton(QtWidgets.QPushButton):
    def closeEvent(self, event):
        for children in self.findChildren(
            QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
        ):
            children.close()
        print("closeEvent PushButton")
        super(PushButton, self).closeEvent(event)


class LineEdit(QtWidgets.QLineEdit):
    def closeEvent(self, event):
        for children in self.findChildren(
            QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
        ):
            children.close()
        print("closeEvent LineEdit")
        super(LineEdit, self).closeEvent(event)


class ComboBox(QtWidgets.QComboBox):
    def closeEvent(self, event):
        for children in self.findChildren(
            QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
        ):
            children.close()
        print("closeEvent ComboBox")
        super(ComboBox, self).closeEvent(event)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        button_close = PushButton(text="close", clicked=self.close)

        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(button_close)
        lay.addWidget(LineEdit())
        lay.addWidget(ComboBox())

    def closeEvent(self, event):
        for children in self.findChildren(
            QtWidgets.QWidget, options=QtCore.Qt.FindDirectChildrenOnly
        ):
            children.close()
        print("closeEvent Widget")
        super(Widget, self).closeEvent(event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

08-20 04:37