Search code examples
pythoneventspysideqtabwidget

Trigger Close Event for Tabs in PySide QTabWidget


I've written 3 separate widgets and each one contains a'closeEvent'. However the close events are NOT triggered when the widgets are placed as tabs inside the MainWindow. How can I emit a signal to properly fire the 'closeEvent' for each tab when the MainWindow closes?

I've done my best to simplify the code to focus on the main goal. I'd like to emit the close event for each tab so in the future when I add more tabs it will be more flexible and each tool will still be self contained.

enter image description here

import sys
from PySide import QtGui, QtCore

class TabA(QtGui.QWidget):
    def __init__(self, parent=None):
        super(TabA, self).__init__(parent)
        self.resize(300, 300)
        self.setWindowTitle('App A')
        self.btn = QtGui.QPushButton("Button A")
        grid = QtGui.QGridLayout()
        grid.addWidget(self.btn)
        self.setLayout(grid)

    def closeEvent(self, event):
        print "closing Tab A"
        event.accept()

class TabB(QtGui.QWidget):
    def __init__(self, parent=None):
        super(TabB, self).__init__(parent)
        self.resize(300, 300)
        self.setWindowTitle('App A')
        self.btn = QtGui.QPushButton("Button A")
        grid = QtGui.QGridLayout()
        grid.addWidget(self.btn)
        self.setLayout(grid)

    def closeEvent(self, event):
        print "closing Tab A"
        event.accept()

class TabC(QtGui.QWidget):
    def __init__(self, parent=None):
        super(TabC, self).__init__(parent)
        self.resize(300, 300)
        self.setWindowTitle('App A')
        self.btn = QtGui.QPushButton("Button A")
        grid = QtGui.QGridLayout()
        grid.addWidget(self.btn)
        self.setLayout(grid)

    def closeEvent(self, event):
        print "closing Tab A"
        event.accept()

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.initUI()

    def initUI(self):
        self.resize(300, 300)
        self.setWindowTitle('Tabs')

        # Tabs
        main_tabWidget = QtGui.QTabWidget()
        main_tabWidget.addTab(TabA(), "TabA")
        main_tabWidget.addTab(TabB(), "TabB")
        main_tabWidget.addTab(TabC(), "TabC")

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(main_tabWidget)
        vbox.setContentsMargins(0, 0, 0, 0)

        main_widget = QtGui.QWidget()
        main_widget.setLayout(vbox)
        self.setCentralWidget(main_widget)


def main():
    app = QtGui.QApplication(sys.argv)
    ex = MainWindow()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Solution

  • The closeEvent method is only called for a top-level window when a close request is received, so it is not suitable for your use-case. However, there are several signals that are emitted while the application is shutting down that you can connect to.

    So for instance you could do this:

    class TabA(QtGui.QWidget):
        def __init__(self, parent=None):
            super(TabA, self).__init__(parent)
            ...
            QtGui.qApp.aboutToQuit.connect(self.handleClose)
    
        def handleClose(self):
            print "closing Tab A"
    

    This signal is emitted just before the event-loop exits, and there's also a QtGui.qApp.lastWindowClosed signal which is emitted just after the main window closes.

    (PS: don't be tempted to use __del__ for this purpose, because the exact behaviour during shutdown is strictly undefined in PyQt, and can change between versions).