A common practice in a GUI application is the ability to press the tab key and move from one widget to another in some logical sequence. The tab ordering is initially set to mimic the order widgets are created. However, this might not match the tab order users expect. Let’s look at how we can create several widgets and then adjust the tab sequence, without rearranging the widget constructors.
#include <QtCore>
#include <QtGui>
class MainWindow : public QWidget
{
public:
MainWindow();
};
The only declaration we need in this example is the public constructor for our MainWindow.
MainWindow::MainWindow()
{
setMinimumSize(600, 350);
//
QLabel *label_1 = new QLabel("Name:");
QLineEdit *text_1 = new QLineEdit();
text_1->setPlaceholderText("Enter your full name...");
//
QLabel *label_2 = new QLabel("Activities/Hobbies:");
label_2->setAlignment(Qt::AlignLeft | Qt::AlignTop);
QCheckBox *cb_1 = new QCheckBox{"Cooking"};
QCheckBox *cb_2 = new QCheckBox{"Gardening"};
QCheckBox *cb_3 = new QCheckBox{"Hiking"};
QCheckBox *cb_4 = new QCheckBox{"Photography"};
QCheckBox *cb_5 = new QCheckBox{"Reading"};
QCheckBox *cb_6 = new QCheckBox{"Swimming"};
QCheckBox *cb_7 = new QCheckBox{"Traveling"};
QCheckBox *cb_8 = new QCheckBox{"Wood Working"};
//
QLabel *label_3 = new QLabel("Favorite City:");
QComboBox *comboBox_3 = new QComboBox();
comboBox_3->setMinimumSize(150, 0);
comboBox_3->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Fixed);
comboBox_3->addItems( {"London", "Paris", "Munich",
"Berlin", "Stuttgart", "Brussels", "Amsterdam",
"Copenhagen", "Zürich", "Barcelona", "San Francisco",
"New Orleans", "New York", "Chicago", "Boston",
"Sydney", "Tokyo"} );
comboBox_3->model()->sort(0);
comboBox_3->setCurrentIndex(0);
//
QLabel *label_4 = new QLabel("Biography:");
QPlainTextEdit *text_4 = new QPlainTextEdit();
text_4->setPlaceholderText("Multiple lines of text...");
text_4->setMaximumHeight(100);
text_4->setTabChangesFocus(true);
QPushButton *pb_1 = new QPushButton();
pb_1->setText("Close");
QGridLayout *layout_cb = new QGridLayout();
layout_cb->setVerticalSpacing(5);
layout_cb->addWidget(cb_1, 0, 0);
layout_cb->addWidget(cb_2, 1, 0);
layout_cb->addWidget(cb_3, 2, 0);
layout_cb->addWidget(cb_4, 3, 0);
layout_cb->addWidget(cb_5, 0, 1);
layout_cb->addWidget(cb_6, 1, 1);
layout_cb->addWidget(cb_7, 2, 1);
layout_cb->addWidget(cb_8, 3, 1);
QFormLayout *layout1 = new QFormLayout();
layout1->setVerticalSpacing(20);
layout1->addRow(label_1, text_1);
layout1->addRow(label_2, layout_cb);
layout1->addRow(label_3, comboBox_3);
layout1->addRow(label_4, text_4);
QHBoxLayout *layout2 = new QHBoxLayout();
layout2->addStretch();
layout2->addWidget(pb_1);
layout2->addStretch();
QVBoxLayout *layoutMain = new QVBoxLayout(this);
layoutMain->setContentsMargins(45, 35, 45, 15);
layoutMain->addLayout(layout1);
layoutMain->addSpacing(20);
layoutMain->addLayout(layout2);
QWidget::setTabOrder(text_1, cb_1);
QWidget::setTabOrder(cb_1, cb_5);
QWidget::setTabOrder(cb_5, cb_2);
QWidget::setTabOrder(cb_2, cb_6);
QWidget::setTabOrder(cb_6, cb_3);
QWidget::setTabOrder(cb_3, cb_7);
QWidget::setTabOrder(cb_7, cb_4);
QWidget::setTabOrder(cb_4, cb_8);
QWidget::setTabOrder(cb_8, comboBox_3);
QWidget::setTabOrder(comboBox_3, text_4);
connect(close_pb, &QPushButton::clicked,
this, &QWidget::close);
}
The majority of this code declares a variety of widgets which make up the user interface for our program. There are four types of widgets and each one is preceded by a QLabel control with text explaining the purpose of each widget.
The first control is a QLineEdit for the user to enter their full name. When the program starts this field will contain text describing what information should be entered. As the user enters the first character the prompt will be deleted.
The next sections of the UI shows eight check boxes. This is followed by a drop down combo box containing a list of cities in alphabetic order. Maintaining the city initializer list in alphabetic order would be annoying, so we mixed it up on purpose. The code on line 38 will instruct the model to sort the strings. Line 39 is used to highlight and select the first city in the sorted list, although you could start on any name. If line 39 is removed then “London” will be selected first, instead of “Amsterdam”.
The last control is a QPlainTextEdit which gives the user a place to enter multiple lines of data. By default, this widget allows tabs as part of the text. In this example we decided pressing the tab key should exit the widget and move to the “Close” button. This is accomplished by calling the method setTabChangesFocus() on line 47 and passing true.
So what happens when the user presses the tab key to move from widget to widget? Currently the order matches when the widgets were constructed. The problem is this may not be the order the user expects so we need a mechanism to change the tab ordering of controls.
In this example the eight check boxes were declared from top to bottom, left to right. So based on the widget construction, the tab order would move from “cooking” down to “photography”, then up to “reading” and down to “wood working”. The user requested a tab ordering which moves from “cooking” over to the right column for “reading”. Then back to the left column for “gardening” followed by “swimming”. The idea is to move from the left column to the right column and then drop down a row.
Lines 83 through 92 are the ten lines of code which define the tab order of the check boxes. Even though we only need to change the tab ordering for the check boxes, it is a good policy to include all of the UI controls located on this window. This may not be complicated code, however the order of these ten calls to setTabOrder() must appear in the specific sequence of the desired tab order.
Signal / Slot Connections
There is only one call to connect() which is from the close button clicked signal to the close method of the QWidget. When the user clicks the close button the program will terminate.
Main Function
Since the source code for main() has not changed there is no need to show it again. Refer to example 3 or download the full source for this example.
Running the Example
To build and run this example use the same CMake build file and commands as we showed for the first example. The only suggested modification is on line 2 of the CMakeLists.txt file, change “example_1” to “example_31”.