我要么不完全了解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_())