How do I prevent the enter key from closing my QDialog (Qt 4.8.1)
Asked Answered
R

10

22

I have a QDialog with a QDialogButtonBox. The OK and Cancel buttons are active. Occasionally I disable or hide the OK button based on the state of my dialog. It seems, no matter what I do, the Enter key always activates the OK button. I really DON'T want this to happen. I have tried:

  • Setting default and autoDefault properties to false every time I show/hide/enable/disable/whatever the button
  • installing an event filter on the OK button to intercept key events (pressed and released) for return, enter and space
  • Setting the focus policy on the button to NoFocus

And with all combinations of those things above, the Enter key still accepts the dialog. Does anyone have any clue how to block this? It seems like I should be able to block something as simple as this?

Ringler answered 6/4, 2013 at 0:5 Comment(4)
Can you post the post event filter code?Connor
Is it your own dialog or you talk about the standard dialogs Qt provides?Cairn
Don't use Ok and Cancel button, add your own button to QDialogButtonBox.Mapel
@MuhammadMinhazulHaque but then I don't get the advantage of the buttons being positioned correctly on different OS'sRingler
M
21

The key press event filtering should be done on the dialog itself, because the code handling the forwarding of the Return and Enter keys to the default button is in QDialog::keyPressEvent.

void Dialog::keyPressEvent(QKeyEvent *evt)
{
    if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)
        return;
    QDialog::keyPressEvent(evt);
}

Or

theDialog−>installEventFilter(anotherClassObject);

bool AnotherClass::eventFilter(QObject *obj, QEvent *evt)
{
    if(evt->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent*>(evt);
        if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return )
            return true; // mark the event as handled
    }
    return false;
}
Mcgurn answered 6/4, 2013 at 0:15 Comment(7)
should I ignore() the event?Ringler
This didn't work. I don't get a keyPressEvent call in my QDialog based class when Enter is pressed.Ringler
You shouldn't ignore() the event, but accept it and do nothing. But if the cancel button is still enabled, and can get the focus, it might still be activated by the Enter key and close the dialog.Mcgurn
strangely this did not work either. Neither the filter method or overriding the key press event get called.Ringler
The first pattern worked for me in Python using pyside 1.2.4 / Qt 4.8.6Incredible
The first pattern worked for me on Windows using Qt 5.5.1 in C++. I'm not using a button box, but I have other buttons--none of them marked as either default or autoDefault--and Qt seems to be selecting the most top-left button as the default button. The problem is that I have a situation where the user is entering rows of data on the dialog, and I want Return to activate adding a row when the user is in that area. Finding a way to deactivate the default button handling for the whole dialog was critical, and this worked.Venture
Overriding keyPressEvent in the derived dialog class worked for me, using C++ Qt 4.7.4.Linnell
C
7

If you have normal QPushButtons on the dialog then if the buttons have the autoDefault and/or default properties set on them then you get a default button - which is what the enter key triggers. In that case, get rid of autoDefault on the buttons and pressing enter in another widget no longer closes the dialog.

In the case of a QDialogButtonBox you can probably iterate over the buttons to turn this stuff off in the ctor of your dialog. Not tested here but ought to work. If not then you'll need to also see if there is a default button that gets set on the QDialog itself too.

Cheekpiece answered 27/1, 2015 at 16:11 Comment(0)
T
4

One option is to override your dialog's show event so as to allow the QDialogButtonBox to be shown, after which it will have set a default button with an AcceptRole, and to then set all buttons to not be defaults.

void MyDialog::showEvent(QShowEvent* event)
{
    // When a QDialogButtonBox is shown, it will set a default button if none are found so we need to disable the
    // default buttons after the button box has been shown.
    QDialog::showEvent(event);

    // For example, with a dialog which has two buttons, Save and Cancel, we remove all defaults
    // It might be good enough to remove the default on just the buttons with have the AcceptRole, but
    // I didn't test extensively enough to see what would happen if any buttons had "autoDefault" set or
    // verify this behavior on all platforms.
    ui->buttonBox->button(QDialogButtonBox::Save)->setDefault(false);
    ui->buttonBox->button(QDialogButtonBox::Cancel)->setDefault(false);
}

Attempting to remove defaults before the QDialogButtonBox is shown, such as in the constructor of your dialog, will just be overriden by the code in the QDialogButtonBox::showEvent().

Tentation answered 18/6, 2021 at 16:3 Comment(0)
Y
2

The problem is the event filter shouldn't be installed on the OK button.

If your OK button is disabled, then it's not going to receive the enter event. Whichever widget has the focus will. And if they don't accept the enter event, then QDialog is going to accept() itself.

Two ways to solve the problem:

1) Override QDialog::accept(), and call QDialog's accept method in the new accept function only if OK is enabled

void MyDialog::accept() {
    if (okEnabled) {
        QDialog::accept();
    }
}

2) Install an event filter on every widget in the dialog that doesn't accept the enter key (line edits, ...).

The event filter would be like so:

class KeyPressEater : public QObject
{
    Q_OBJECT

protected:
    bool eventFilter(QObject *obj, QEvent *event);
};

bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        bool res = QObject::eventFilter(obj, event);

        if (keyEvent->key() == Qt::Key_Return) {
            return true; /* Always accept return */
        } else {
            return res;
        }
    } else {
        // standard event processing
        return QObject::eventFilter(obj, event);
    }
}

And in your code, for each widget in the dialog:

myWidget->installEventFilter(myKeyPressEater);
Yaekoyael answered 29/12, 2013 at 2:15 Comment(0)
M
1

QDialog has a private slot called accept(). Whenever QDialogButtonBox emits accepted() (by pressing return key or clicking Ok), that private slot is called. So try disconnecting them.

disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));

This worked for me.

Mapel answered 8/4, 2013 at 20:43 Comment(1)
I never explicitly set up that connection and removed that connection from the ui file long agoRingler
S
1

To avoid "OK" button or "Enter" key from closing dialog: in the ui xml file, remove the connect/slot for accept/reject. Then, in your code , emmit accept() when and as needed;

example from ui file which connects accept() slot:

 <connections>  
  <connection>  

   <sender>products_ButtonBox</sender>

    <signal>accepted()</signal>  

     <receiver>Products_Dialog</receiver>
      <slot>accept()</slot>
       <hints>
        <hint type="sourcelabel">
         <x>248</x>
         <y>254</y>
        </hint>
        <hint type="destinationlabel">
         <x>157</x>
         <y>274</y>
        </hint>e
       </hints>
      </connection>
Studio answered 8/7, 2017 at 21:2 Comment(0)
S
1

In your dialog's accept() method, check the Ok-button for focus:

void accept() override
{ if (!dialogButtonBox->button(QDialogButtonBox::Ok)->hasFocus())
  return;

...

  QDialog::accept();
}
Sussi answered 24/6, 2019 at 19:18 Comment(0)
D
0

The key is to set your own buttons, all with NoRole, and neither accept or reject signals from the buttonbox. This will allow you to set your own behavior for the default button.

class Dialog(QDialog):
    def __init__():
        super(Dialog, self).__init__()
        self.buttonBox = QDialogButtonBox()

        self.btn_save = self.buttonBox.addButton('Save', QDialogButtonBox.NoRole)
        self.btn_cancel = self.buttonBox.addButton('Cancel', QDialogButtonBox.NoRole)
        self.btn_save.clicked.connect(self.onAccept)
        self.btn_save.setMouseTracking(True)
        self.btn_cancel.clicked.connect(self.onReject)

        #  STATUS BAR
        self.status = QStatusBar()
        self.status.addPermanentWidget(self.buttonBox)

    def onAccept(self):
        if not self.btn_save.underMouse():
            return
        self.submitChanges(self.model)
        self.accept()

    def onReject(self):
        self.reject()
Debidebilitate answered 7/7, 2020 at 18:48 Comment(0)
R
0

The answers above did not work for me for a variety of reasons.

I ended up adding an invisible button and setting it as default.

Maybe this simple cheesy idea will help someone.

Raffaello answered 22/1 at 16:53 Comment(0)
P
-1

In PySide (and I imagine PyQt) I was able to redefine the accept and reject functions of the QDialog.

def custom_accept ():
    # perform custom actions when you hit open
    pass

def custom_reject ():
    # perform custom actions when you hit cancel
    pass

file_dialog = QtGui.QFileDialog(directory=".")
file_dialog.accept = custom_accept
file_dialog.reject = custom_reject

This kept the file dialog from closing and gave me access to the data when the 'ok' (accept) or 'cancel' (reject) functions were triggered (either with enter or by clicking the buttons)

Partridgeberry answered 23/9, 2013 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.