Qt: How to Handle the Event of the User Pressing the 'X' (Close) Button

Qt: How do I handle the event of the user pressing the 'X' (close) button?

If you have a QMainWindow you can override closeEvent method.

#include <QCloseEvent>
void MainWindow::closeEvent (QCloseEvent *event)
{
QMessageBox::StandardButton resBtn = QMessageBox::question( this, APP_NAME,
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
if (resBtn != QMessageBox::Yes) {
event->ignore();
} else {
event->accept();
}
}


If you're subclassing a QDialog, the closeEvent will not be called and so you have to override reject():

void MyDialog::reject()
{
QMessageBox::StandardButton resBtn = QMessageBox::Yes;
if (changes) {
resBtn = QMessageBox::question( this, APP_NAME,
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);
}
if (resBtn == QMessageBox::Yes) {
QDialog::reject();
}
}

How to detect that the close button of QWidget is pressed?

Cause

QWidget does not have a triggered signal.

Solution

I would suggest you to:

  1. Subclass QWidget and reimplement QWidget::closeEvent

  2. Check QEvent::spontaneous to differentiate between a click of the close button and the call to QWidget::close

  3. According to your app's logic either call QWidget::closeEvent(event); to close the widget, or QEvent::ignore to leave it open

Example

I have prepared an example for you of how to implement the proposed solution:

#include <QMainWindow>
#include <QCloseEvent>
#include <QPushButton>

class FooWidget : public QWidget
{
Q_OBJECT
public:
explicit FooWidget(QWidget *parent = nullptr) :
QWidget(parent) {
auto *button = new QPushButton(tr("Close"), this);
connect(button, &QPushButton::clicked, this, &FooWidget::close);
resize(300, 200);
setWindowTitle("Foo");
}

protected:
void closeEvent(QCloseEvent *event) override {

if (event->spontaneous()) {
qDebug("The close button was clicked");
// do event->ignore();
// or QWidget::closeEvent(event);
} else {
QWidget::closeEvent(event);
}
}
};

class MainWindow : public QMainWindow
{
Q_OBJECT
FooWidget *pWindow;
public:
explicit MainWindow(QWidget *parent = nullptr) :
QMainWindow(parent),
pWindow(new FooWidget()) {
pWindow->show();
}
};

Proper way to handle the close button in a main window PyQt, (Red X)

You shouldn't ever modify the class that was generated from your ui file. Instead you should subclass and modify the subclass.

From the looks of your code, you are actually creating two QMainWindows and the closeEvent is being caught for the wrong one (presumably that one is hidden?). That is self.ui is a QMainWindow that is not being shown, and is not added to the UI of GUIForm. Instead you are using the Ui_MainWindow.setupUi() method explicitly yourself, to add the widgets to your own QMainWindow, 'GUIForm`.

Instead, what you should to do is leave your Ui_MainWindow class as it was when it was generated from the ui file, and then modify your main python file to be:

class GUIForm(Ui_MainWindow):
def __init__(self, parent=None):
Ui_MainWindow.__init__(self, parent)
self.threadData()

def closeEvent(self, event):
print "User has clicked the red x on the main window"
event.accept()

if __name__ == "__main__":

app = QtGui.QApplication(sys.argv)
myapp = GUIForm()
myapp.show()
ret = app.exec_()
sys.exit(ret)

This way you are extending the behaviour of the auto-generated UI file. This makes it easy to regenerate the python file from the .ui file without having to re-add code (this is precisely why you should never modify the auto-generated Python file)

Qt Dialog X Button Override Reject Not Working As Expected

Override the virtual function "virtual void closeEvent(QCloseEvent * e)" in your dialog class. The code comment will explain in detail.

Dialog::Dialog(QWidget *parent) :QDialog(parent), ui(new Ui::Dialog){
ui->setupUi(this);
}
Dialog::~Dialog(){
delete ui;
}
//SLOT
void Dialog::fnShow(){
//Show the dialog
this->show();
}
void Dialog::closeEvent(QCloseEvent *e){
QMessageBox::StandardButton resBtn = QMessageBox::question( this, "APP_NAME",
tr("Are you sure?\n"),
QMessageBox::Cancel | QMessageBox::No | QMessageBox::Yes,
QMessageBox::Yes);

if (resBtn != QMessageBox::Yes){
//Hiding the dialog when the close button clicked
this->hide();
//event ignored
e->ignore();
//Testing. To show the dialog again after 2 seconds
QTimer *qtimer = new QTimer(this);
qtimer->singleShot(2000,this,SLOT(fnShow()));
qtimer->deleteLater();
}
//below code is for understanding
//as by default it is e->accept();
else{
//close forever
e->accept();
}
}

How to handle/override when the red x button in the corner is pressed in PyQt5 for QtWidget?

The closeEvent is fired whenever a widget is closed, for example by clicking the "X" in the corner. Just override it with your own code to show the main window again, or emit a signal to inform your Controller.

from PyQt5 import QtWidgets
app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([])

class W1(QtWidgets.QWidget):
def closeEvent(self, event):
print("closing w1")

class W2(QtWidgets.QWidget):
def __init__(self, w1):
super().__init__()
self.w1 = w1

def closeEvent(self, event):
print("closing w2")
self.w1.show()

w1 = W1()
w2 = W2(w1)

w2.show()
app.exec_()

QT C++ Ask to user before close window

This is what I do in my preference dialog:

PreferencesDialog::PreferencesDialog(QWidget *parent)
: QDialog(parent, Qt::Tool)
{
...
QDialogButtonBox* buttonBox = new QDialogButtonBox(this);
buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults);
buttonBox->setFocusPolicy(Qt::TabFocus);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);

connect(buttonBox, &QDialogButtonBox::rejected, this, &PreferencesDialog::reject);
connect(buttonBox, &QDialogButtonBox::accepted, this, &PreferencesDialog::accept);
connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &PreferencesDialog::revert);
...
}

You can then do something like this in the accept slot, although I think it is a bad idea. This would result in distractive user experience. The common behaviour that an average user expects is just the above. So, I would ignore the below code. But I will provide it for you for completeness:

QMessageBox messageBox;
messageBox.setWindowTitle("Close the preferences?");
messageBox.setIcon(QMessageBox::Warning);
messageBox.setText(...);
messageBox.setInformativeText(tr("Are you sure you want to close the preferences?"));
messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::No);

const int ret = messageBox.exec();
switch (ret) {
case QMessageBox::Yes:
...
case QMessageBox::No:
...
break;
default:
break;
}

Qt: how can one close a window upon another window closed by the user

This modified version of your program shows how you could do it by overriding the closeEvent(QCloseEvent *) method on your w1 widget:

#include <QApplication>
#include <QtGui>
#include <QtCore>
#include <QtWidgets>

class MyWidget : public QWidget
{
public:
MyWidget(QWidget * closeHim) : _closeHim(closeHim)
{
// empty
}

virtual void closeEvent(QCloseEvent * e)
{
QWidget::closeEvent(e);
if (_closeHim) _closeHim->close();
}

private:
QWidget * _closeHim;
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QWidget w2;
w2.setWindowTitle("w2");
w2.show();

MyWidget w1(&w2);
w1.setWindowTitle("w1");
w1.show();

return a.exec();
}

If you wanted to do it more elegantly, you could have your closeEvent() method-override emit a signal instead of calling a method on a pointer; that way you wouldn't have a direct dependency between the two classes, which would give you more flexibility in the future.

How to have a PyQt window call a method when its X close button is selected

One approach is to reimplement the closeEvent method of the QWidget, having it call the method that joins (or terminates) subprocesses and then having it call either deleteLater() or destroy().

    def closeEvent(self, event):
logger.info("stopping spin")
self.stylusProximityControlOff()
self.deleteLater()

Trying to to close app when dialog window is closed by pressing X but getting error no reject member function found (Qt app)

Seems like you didn't add a declaration of the reject() method in your MyDialog header. If you want reimplement it, you should declare in the header:

public slots:
virtual void reject();

How to use connect in Qt to know when the X (exit) button is clicked

If you want to create a signal when you press the X button you can create the signal and emit it in the closeEvent method.

protected:
void closeEvent(QCloseEvent * event){
emit clicked();
event->ignore();
}

signals:
void clicked();


Related Topics



Leave a reply



Submit