Search code examples
pythonpyside2

pyside2 connect close by Window "X" to custom exit method


I am using Qt Designer for the GUI layout and do load the .ui file into python with the help of QUILoader.

I managed to connect a 'Quit' Button to my 'quit_app' method.

My question is how to connect this method in the event that the user tries to close the Window with the 'X'.

Thanks in advance!

---> PYTHON CODE <---

import sys
from PySide2.QtUiTools import QUiLoader
from PySide2.QtWidgets import QApplication
from PySide2.QtCore import QFile

class main_w(object):
    def __init__(self):
        # load ui; create w
        self.file = QFile("simple_quit.ui")
        self.file.open(QFile.ReadOnly)
        self.loader = QUiLoader()
        self.w = self.loader.load(self.file)

        # connections

        # Quit Button
        self.w.QUITButton.clicked.connect(self.quit_app)

        # ??? Window's X Button ???
        # THROWS ERROR:
        self.w.closeEvent.connect(self.quit_app)

    def quit_app(self):
        # some actions to perform before actually quitting:
        print('CLEAN EXIT')
        app.exit()

    def show(self):
        self.w.show()

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

---> UI XML <---

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="QUITButton">
    <property name="geometry">
     <rect>
      <x>100</x>
      <y>100</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>quit</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Solution

  • In Qt there are 2 concepts: the signal and the event, the signal is connected to a slot but in the case of events you can not, and then you have the latter. In the case of the signal, it is known who is the receiver since it is declared in the connection, but the events are not, this is sent through the parent-child relationship tree of the QObjects and can be accepted or ignored according to the case.

    So that's why you have that problem.


    The solution in general would be to overwrite the closeEvent method:

    C++ version:

    void MainWindow::closeEvent(QCloseEvent *event)
    {
        if (maybeSave()) {
            writeSettings();
            event->accept();
        } else {
            event->ignore();
        }
    }
    

    Python version:

    def closeEvent(self, event):
        if maybeSave():
            writeSettings()
            event.accept()
        else:
            event.ignore()
    

    But for this it is necessary to inherit from the class, but in your case it is not possible so there is another solution, install an event filter:

    import sys
    from PySide2 import QtCore, QtWidgets, QtUiTools
    
    
    class Manager(QtCore.QObject):
        def __init__(self):
            super(Manager, self).__init__()
            # load ui; create w
            file = QtCore.QFile("simple_quit.ui")
            file.open(QtCore.QFile.ReadOnly)
            loader = QtUiTools.QUiLoader()
            self.w = loader.load(file)
    
            # connections
            # Quit Button
            self.w.QUITButton.clicked.connect(self.quit_app)
    
            self.w.installEventFilter(self)
    
        def eventFilter(self, obj, event):
            if obj is self.w and event.type() == QtCore.QEvent.Close:
                self.quit_app()
                event.ignore()
                return True
            return super(Manager, self).eventFilter(obj, event)
    
        @QtCore.Slot()
        def quit_app(self):
            # some actions to perform before actually quitting:
            print('CLEAN EXIT')
            self.w.removeEventFilter(self)
            app.quit()
    
        def show(self):
            self.w.show()
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        w = Manager()
        w.show()
        sys.exit(app.exec_())