I want to make my app laptop friendly. I can tab to everywhere, but if I tab to a QPushButton I can't press it with Enter, only with space.
What's the problem? How to make it pressable for Enter?
问题:
回答1:
tl;dr
- In the Qt Creator's UI view, select the button you want to make pressable for Enter.
- In the right side at the Property Editor scroll down to the blue part titled
QPushButton
. - Check the checkbox by
autoDefault
ordefault
.
Most of the cases the main different between autoDefault
and default
is how the button will be rendered. But there are cases where it can cause unexpected things so for more information read more below.
Full review
Overview
Every QPushButton
has 3 properties that are not inherited. From these, two (default
and autoDefault
) have a major role when we place buttons on QDialog
s, because these settings (and the focus on one of the buttons) decides which button will be pressed if we hit Enter.
All of these properties are set false by default. Only expection is autoDefault
that will be true if the button has a QDialog
parent.
Every time you press space the button with the focus on it will be pressed. The followings will describe what happens if you press Enter.
Default property
If this is set true, the button will be a default button.
If Enter is pressed on the dialog, than this button will be pressed, except when the focus is on an autoDefault button.
There should be only one default button. If you add more then the last one added will be the default button.
AutoDefault property
If this is set true, the button will be an autoDefault button.
If Enter is pressed on the dialog, than this button will be pressed if the focus is on it.
If the focus is not on an autoDefault button and there is no default button than the next autoDefault button will be pressed for Enter.
Flat property
If this is set true, then the border of the button will not be raised.
Example tables
The following tables show which button will be pressed with different buttons on different focus. The buttons are added from left to right.
Test code
The following code is a way to add buttons to a dialog. It can be used for testing by changing the boolean
values at setDefault()
and/or setAutoDefault()
.
You just need to create a window, add a QPushButton
called pushButton
and a QLabel
called label
(that is the default naming). Don't forget to #include <QMessageBox>
. Then copy this code to the button's clicked()
signal:
void MainWindow::on_pushButton_clicked()
{
QMessageBox msgBox;
QPushButton button("Button");
button.setDefault(false);
button.setAutoDefault(false);
msgBox.addButton(&button, QMessageBox::ActionRole);
QPushButton autodefaultbutton("AutoDefault Button");
autodefaultbutton.setDefault(false);
autodefaultbutton.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton, QMessageBox::ActionRole);
QPushButton autodefaultbutton2("AutoDefault Button2");
autodefaultbutton2.setDefault(false);
autodefaultbutton2.setAutoDefault(true);
msgBox.addButton(&autodefaultbutton2, QMessageBox::ActionRole);
QPushButton defaultbutton("Default Button");
defaultbutton.setDefault(true);
defaultbutton.setAutoDefault(false);
msgBox.addButton(&defaultbutton, QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == &button) {
ui->label->setText("Button");
} else if (msgBox.clickedButton() == &defaultbutton) {
ui->label->setText("Default Button");
} else if (msgBox.clickedButton() == &autodefaultbutton) {
ui->label->setText("AutoDefault Button");
} else if (msgBox.clickedButton() == &autodefaultbutton2) {
ui->label->setText("AutoDefault Button2");
}
}
Display
If you compile the code you can get this window. You doesn't even have to click to the buttons because the way they are rendered by the OS shows which one will be pressed if you hit Enter or space.
Official documentation
Most of this answer was made according to the official documentation.
The QPushButton documentation made by Qt states these:
Default and autodefault buttons decide what happens when the user presses enter in a dialog.
A button with this property set to true (i.e., the dialog's default button,) will automatically be pressed when the user presses enter, with one exception: if an autoDefault button currently has focus, the autoDefault button is pressed. When the dialog has autoDefault buttons but no default button, pressing enter will press either the autoDefault button that currently has focus, or if no button has focus, the next autoDefault button in the focus chain.
In a dialog, only one push button at a time can be the default button. This button is then displayed with an additional frame (depending on the GUI style).
The default button behavior is provided only in dialogs. Buttons can always be clicked from the keyboard by pressing Spacebar when the button has focus.
If the default property is set to false on the current default button while the dialog is visible, a new default will automatically be assigned the next time a pushbutton in the dialog receives focus.
It's also worth to check QDialog and QMessageBox.
回答2:
According to Qt's documentation Enter should work
Command buttons in dialogs are by default auto-default buttons, i.e. they become the default push button automatically when they receive the keyboard input focus. A default button is a push button that is activated when the user presses the Enter or Return key in a dialog. You can change this with setAutoDefault().
http://qt-project.org/doc/qt-4.8/qpushbutton.html
回答3:
totymedli's answer is great. I added a small program to test various combinations of isDefault, autoDefault, setDefault and setAutoDefault functions.
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
autoDefaultInitialState = True
defaultInitialState = False
self.lineEdit1 = QLineEdit(self)
self.lineEdit2 = QLineEdit(self)
self.lineEdit3 = QLineEdit(self)
# if we create a new button (e.g. "Print state"), with the same function,
# it doesn't work, because adding a new button (apart from our 3 buttons)
# produces total mess, so we use this lineedit for this purpose
self.lineEdit1.returnPressed.connect(self.printState)
#------------------------------------
self.chkAutoDefaultOk = QCheckBox('OK setAutoDefault', self)
self.chkAutoDefaultCancel = QCheckBox('Cancel setAutoDefault', self)
self.chkAutoDefaultApply = QCheckBox('Apply setAutoDefault', self)
self.chkDefaultOk = QCheckBox('OK setDefault', self)
self.chkDefaultCancel = QCheckBox('Cancel setDefault', self)
self.chkDefaultApply = QCheckBox('Apply setDefault', self)
self.chkAutoDefaultOk.setChecked(autoDefaultInitialState)
self.chkAutoDefaultCancel.setChecked(autoDefaultInitialState)
self.chkAutoDefaultApply.setChecked(autoDefaultInitialState)
self.chkDefaultOk.setChecked(defaultInitialState)
self.chkDefaultCancel.setChecked(defaultInitialState)
self.chkDefaultApply.setChecked(defaultInitialState)
#------------------------------------
self.pushButtonOk = QPushButton(self)
self.pushButtonOk.setText("Ok")
self.pushButtonOk.clicked.connect(lambda : print('ok'))
self.pushButtonCancel = QPushButton(self)
self.pushButtonCancel.setText("Cancel")
self.pushButtonCancel.clicked.connect(lambda : print('cancel'))
self.pushButtonApply = QPushButton(self)
self.pushButtonApply.setText("Apply")
self.pushButtonApply.clicked.connect(lambda : print('apply'))
#------------------------------------
self.pushButtonOk.setAutoDefault(autoDefaultInitialState)
self.pushButtonCancel.setAutoDefault(autoDefaultInitialState)
self.pushButtonApply.setAutoDefault(autoDefaultInitialState)
self.pushButtonOk.setDefault(defaultInitialState)
self.pushButtonCancel.setDefault(defaultInitialState)
self.pushButtonApply.setDefault(defaultInitialState)
#------------------------------------
self.chkAutoDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkAutoDefaultApply.stateChanged.connect(self.chkChangeState)
self.chkDefaultOk.stateChanged.connect(self.chkChangeState)
self.chkDefaultCancel.stateChanged.connect(self.chkChangeState)
self.chkDefaultApply.stateChanged.connect(self.chkChangeState)
#------------------------------------
self.layout = QGridLayout(self)
self.layout.addWidget(self.lineEdit1, 0, 0, 1, 3)
self.layout.addWidget(self.lineEdit2, 1, 0, 1, 3)
self.layout.addWidget(self.lineEdit3, 2, 0, 1, 3)
self.layout.addWidget(self.chkAutoDefaultOk, 3, 0)
self.layout.addWidget(self.chkAutoDefaultCancel, 3, 1)
self.layout.addWidget(self.chkAutoDefaultApply, 3, 2)
self.layout.addWidget(self.chkDefaultOk, 4, 0)
self.layout.addWidget(self.chkDefaultCancel, 4, 1)
self.layout.addWidget(self.chkDefaultApply, 4, 2)
self.layout.addWidget(self.pushButtonOk, 5, 0)
self.layout.addWidget(self.pushButtonCancel, 5, 1)
self.layout.addWidget(self.pushButtonApply, 5, 2)
def chkChangeState(self):
obj = self.sender()
if (obj == self.chkAutoDefaultOk):
self.pushButtonOk.setAutoDefault(self.chkAutoDefaultOk.isChecked())
elif (obj == self.chkAutoDefaultCancel):
self.pushButtonCancel.setAutoDefault(self.chkAutoDefaultCancel.isChecked())
elif (obj == self.chkAutoDefaultApply):
self.pushButtonApply.setAutoDefault(self.chkAutoDefaultApply.isChecked())
elif (obj == self.chkDefaultOk):
self.pushButtonOk.setDefault(self.chkDefaultOk.isChecked())
elif (obj == self.chkDefaultCancel):
self.pushButtonCancel.setDefault(self.chkDefaultCancel.isChecked())
elif (obj == self.chkDefaultApply):
#print('sender: self.chkDefaultApply')
#print('before: ', self.pushButtonApply.isDefault())
self.pushButtonApply.setDefault(self.chkDefaultApply.isChecked())
#print('after: ', self.pushButtonApply.isDefault())
def printState(self):
print(self.pushButtonOk.autoDefault(), self.pushButtonCancel.autoDefault(), self.pushButtonApply.autoDefault())
print(self.pushButtonOk.isDefault(), self.pushButtonCancel.isDefault(), self.pushButtonApply.isDefault())
print('-' * 50)
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
回答4:
Set the tab order for your widgets. This will allow usage of return key for clicking. Its in there by default inside Qt.