2

How does one properly capture the close event coming out of a PySide QtUiTools.QUiLoader() setup?

I can get the instanced class to connect to widgets and everything else, but I am not sure how to intercept the signals in this setup.

Ideally, I want all close calls to pass through my closeEvent (obviously) so that I can ensure that it's safe to close the window. But since my self.closeEvent() is tied to my View(QtWidgets.QMainWindow) and not the self._qt.closeEvent(), I don't know how to get to the self._qt.closeEvent() method to override it in this case.

Or is there a better way to set this up to capture those window events?

# Compatible enough with Pyside 2
from PySide import QtGui as QtWidgets
from PySide import QtUiTools
from PySide import QtCore

class View(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(View, self).__init__(parent=parent)
        self.setup()

    def closeEvent(self, event):

        # Do things
        event.accept()

    def setup(self):
        loader = QtUiTools.QUiLoader()
        fy = QtCore.QFile('example.ui')
        fy.open(QtCore.QFile.ReadOnly)
        self._qt = loader.load(fy, self)
        fy.close()

        self._qt.pCanceled.clicked(self._qt.close)

Doesn't apply:

PySide / PyQt detect if user trying to close window

Close, but PySide doesn't use PyQt's uic and appears to run differently (and didn't work):

PyQt: clicking X doesn't trigger closeEvent

ooklah
  • 491
  • 1
  • 5
  • 16

1 Answers1

2

closeEvent is not a signal, it is a method that is called when the QCloseEvent event is sent. A signal and an event are different things. Going to the problem, in Qt there are 2 ways to listen to events, the first one is overwriting the fooEvent() methods and the second one using an event filter as I show below:

from PySide import QtGui as QtWidgets
from PySide import QtUiTools
from PySide import QtCore

class View(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(View, self).__init__(parent=parent)
        self._qt = None
        self.setup()

    def closeEvent(self, event):
        self.do_things()
        super(View, self).closeEvent(event)

    def do_things(self):
        print("do_things")

    def setup(self):
        loader = QtUiTools.QUiLoader()
        fy = QtCore.QFile('example.ui')
        fy.open(QtCore.QFile.ReadOnly)
        self._qt = loader.load(fy, self)
        fy.close()
        self._qt.pCanceled.clicked.connect(self._qt.close)
        self._qt.installEventFilter(self)

    def eventFilter(self, watched, event):
        if watched is self._qt and event.type() == QtCore.QEvent.Close:
            self.do_things()
        return super(View, self).eventFilter(watched, event)


if __name__ == '__main__':
    import sys

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

Update:

Normally in the eventFilter it is enough to return True for the event to be ignored but in the case of QCloseEvent you must ignore the event and return True as shown below:

def eventFilter(self, watched, event):
    if watched is self._qt and event.type() == QtCore.QEvent.Close:
        self.do_things()
        event.ignore()
        return True
    return super(View, self).eventFilter(watched, event)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • That's definitely helpful, though I can't ignore the event through the event filter if it is determined that the window can't close yet. Is that still possible at this stage? Normally, an overridden closeEvent method takes care of that, and here we're just catching the event. – ooklah Oct 10 '18 at 20:33
  • @ooklah try my solution. – eyllanesc Oct 10 '18 at 23:10
  • Yes, that does what I want and I can tweak it as needed. – ooklah Oct 25 '18 at 21:24