closeEvent детского виджета не вызывается при закрытии - PullRequest
1 голос
/ 31 мая 2019

Либо я не совсем понимаю, как работает распространение событий Qt, либо что-то еще, но я не могу понять, почему точно closeEvent не вызывается ни для класса, производного от QPushButton, ни для самого класса, производного от QWidget.

Разве 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 не вызывается для них?

1 Ответ

0 голосов
/ 31 мая 2019

В следующих примерах, когда вы визуально нажимаете кнопку, вы наблюдаете то же поведение: окно закроется, но мы увидим разницу, в первом оно называется closeEvent (), а во втором - нет. .

Example1:

#!/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()

Почему вы не вызываете closeEvent при вызове QCoreApplication :: quit? Поскольку этот метод используется для выхода из цикла событий 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 () завершает цикл событий.

Обновление:

почему wid.closeEvent не вызывается, когда пользователь нажимает кнопку "X" в окне Qt? Разве главное окно не должно вызывать 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_())

Есть две кнопки, в случае первой кнопки, которая вызывает QCoreApplication :: quit (), которая завершит цикл события, так что все виджеты будут уничтожены, и в этом случае не будет вызываться closeEvent, в случае вторая кнопка будет вызываться рядом с окном, поэтому она будет вызывать свое closeEvent, но не closeEvents своих дочерних элементов.

Моя настоящая проблема в том, что у меня есть функция saveUI () в closeEvent, и она не вызывается при иерархическом разрушении виджетов при закрытии окна

Если вы хотите, чтобы метод 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_())
...