pythonexitmessageboxpyqt5terminate

pyqt5 - closing/terminating application


I'm working though the pyqt5 tutorial found here Zetcode, PyQt5

As an exercise for myself I'm trying to expand on an example so that I am presented with the same dialog message box regardless of method used to close the app:

The dialog message box is implemented in the closeEvent method, full script provided at the end.

I'm having two issues:

1. When clicking 'Close' button, instead of just quitting, I want to call closeEvent method including message box dialog.

I have replaced a line of the example code for the 'Close' push button:

btn.clicked.connect(QCoreApplication.instance().quit)

And instead am trying to call the closeEvent method which already implements the dialog I want:

btn.clicked.connect(self.closeEvent)

However when i run the script and click the 'Close' button and select the resulting 'Close' option in the dialog i get the following:

Traceback (most recent call last):
File "5-terminator.py", line 41, in closeEvent
    event.accept()
AttributeError: 'bool' object has no attribute 'accept'
Aborted

Can anyone advise what I'm doing wrong and what needs to be done here?

2. When hitting the escape key somehow the message box dialog is presented and works just fine.

Ok, it's great that it works, but I'd like to know how and why the message box functionality defined in CloseEvent method is called within the keyPressEvent method.

Full script follows:

import sys
from PyQt5.QtWidgets import (
    QApplication, QWidget, QToolTip, QPushButton, QMessageBox)
from PyQt5.QtCore import QCoreApplication, Qt


class Window(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        btn = QPushButton("Close", self)
        btn.setToolTip("Close Application")
        # btn.clicked.connect(QCoreApplication.instance().quit)
        # instead of above button signal, try to call closeEvent method below
        btn.clicked.connect(self.closeEvent)

        btn.resize(btn.sizeHint())
        btn.move(410, 118)
        self.setGeometry(30, 450, 500, 150)
        self.setWindowTitle("Terminator")
        self.show()

    def closeEvent(self, event):
        """Generate 'question' dialog on clicking 'X' button in title bar.

        Reimplement the closeEvent() event handler to include a 'Question'
        dialog with options on how to proceed - Save, Close, Cancel buttons
        """
        reply = QMessageBox.question(
            self, "Message",
            "Are you sure you want to quit? Any unsaved work will be lost.",
            QMessageBox.Save | QMessageBox.Close | QMessageBox.Cancel,
            QMessageBox.Save)

        if reply == QMessageBox.Close:
            event.accept()
        else:
            event.ignore()

    def keyPressEvent(self, event):
        """Close application from escape key.

        results in QMessageBox dialog from closeEvent, good but how/why?
        """
        if event.key() == Qt.Key_Escape:
            self.close()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    w = Window()
    sys.exit(app.exec_())

Hope someone can take the time to enlighten me.


Solution

  • Your second question answers the first question.

    The reimplemented keyPressEvent method calls close(), which sends a QCloseEvent to the widget. Subsequently, the widget's closeEvent will be called with that event as its argument.

    So you just need to connect the button to the widget's close() slot, and everything will work as expected:

        btn.clicked.connect(self.close)