topic logo
Qt ⇢ Unit 10

Huston, come in!

radiobuttons

Summary

A radio button is a common control type that provides exclusive selection states.

Background

This unit is the first one that demonstrates common widgets that are part of the Qt widgets module. It’s also the first step to real-world applications and touches some of the core concepts of the Qt framework.

New Classes

  • QRadioButton: A derivative of QAbstractButton that brings the typical radio button looks and default mutual selection exclusivity.
  • QAbstractButton: Abstract base class for all button types.
  • QTextEdit: A multi-line widget to display and edit plain text.
  • QObject: The root class most of the Qt framework builds upon.

The RadioGroups App

The origin of the “radio button” term goes back in time to mechanical radios that used buttons to select preset channels. When the user pressed one, any other button that was previously pressed got released. Though radios got digital and most of the historical context went to the museums, this UI paradigm is still widely used in software applications.

This sample application steps up the difficulty a bit and introduces a few new concepts. While the focus lies on the introduction of the radio button, this unit also uses a plain text edit to provide feedback when the selection changes. This is also the first unit to implement an new signal.

Two Columns of Independent Exclusive Switches
Two Columns of Independent Exclusive Switches

The structure of the source code is slightly more complex than in previous units. Instead of only one, we implement two custom classes. One derives from QWidget and serves as the application root window while the other enhances the QRadioButton class and is used repeatedly within the application window.

A New Radio Button

Checkable buttons emit a signal when they are toggled. They send a boolean argument along which tells if the button is checked or not. The radio buttons in this example application additionally emit a new signal called “selected(msg)”. It sends along a message containing the radio button’s name.

namedradiobutton.h - declare a new signal
class NamedRadioButton : public QRadioButton
{
    Q_OBJECT

public:
    explicit NamedRadioButton(QString radioName);

signals:
    void selected(QString msg);

private slots:
    void onToggled(bool checked);
};

The constructor of the modified button requires a string argument providing the button name. Additionally it declares a new signal and a new slot.

namedradiobutton.cpp - the toggled() slot
void NamedRadioButton::onToggled(bool checked)
{
    if (checked == true) {
        emit selected(objectName() + " is checked..");
    }
}

The slot emits the newly declared “selected()” signal with the class name as argument, but only when the “checked” argument is “true”. This slot will be triggered both on selection and on de-selection, but only when selected will the argument be “true”.

The “QObject::objectName()” is a curiosity here. The QObject root class is at the very top of the Qt class hierarchy. This unit will only scratch this class. Each object that derives from the root class can have an associated name. It is set with the “QObject::setObjectName()” method and read with the “QObject::objectName()” method. By default it’s empty. Multiple objects can have the same name.

This class uses it as a convenience. Instead it could have declared a variable and use that, but it’s more convenient this way.

namedradiobutton.cpp - constructor
NamedRadioButton::NamedRadioButton(QString radioName)
{
    setText(radioName);
    setObjectName(radioName);

    connect(this, &QAbstractButton::toggled,
            this, &NamedRadioButton::toggled);
}

The constructor of the radio button class takes the button’s name as argument and sets it as the text that is displayed. It also sets it as the object name that is used by the slot discussed above.

At the end it connects the parent classes toggled signal to the new slot, triggering it whenever the radio button is selected or de-selected.

The Application Window

The header declares a QWidget derivative class called “RadioGroups” and includes the header of the “NamedRadioButton” class. The “RadioGroups” class has a single constructor.

radiogroups.cpp -- initializing the application
RadioGroups::RadioGroups()
{
    auto leftGroup = new QWidget();
    auto rightGroup = new QWidget();

    auto textEdit = new QPlainTextEdit();
    textEdit->setReadOnly(true);

    auto leftLayout = new QVBoxLayout(leftGroup);
    auto rightLayout = new QVBoxLayout(rightGroup);

The constructor starts off creating two container widgets. This demonstrates that radio buttons belonging to the same parent depend on each other and have a mutually exclusive selection. But radio buttons will only care about selections in the same container, changes to the selection in the other container are ignored. The text edit is used to display changes in the radio button selections.

    for (int i=0; i<5; i++) {
        auto leftName = "left " + QString().setNum(i);
        auto rightName = "right " + QString().setNum(i);

        auto radioBtnLeft = new NamedRadioButton(leftName);
        auto radioBtnRight = new NamedRadioButton(rightName);

        leftLayout->addWidget(radioBtnLeft);
        rightLayout->addWidget(radioBtnRight);

        connect(radioBtnLeft, &NamedRadioButton::selected,
                textEdit,     &QPlainTextEdit::appendPlainText);
        connect(radioBtnRight, &NamedRadioButton::selected,
                textEdit,      &QPlainTextEdit::appendPlainText);

        if (i == 0) {
            radioBtnLeft->setChecked(true);
            radioBtnRight->setChecked(true);
        }
    }

The loop first computes the names of the radio buttons that will be placed in one of the groups based on the loop counter. Then follows with creating object of the extended radio button class implemented earlier.

The buttons are added to one of the containers and their “selected()” signal connected to the “appendPlainText()” slot of the text box. This will append the message the buttons send to the text box providing feedback about the event outside the button group. In a more serious application this could be used to trigger some related action, like changing some display element or the measurement scale of a data monitor.

Event-chain of Radio Button Selection
Event-chain of Radio Button Selection

The last part of the loop only runs on the first pass and selects the first radio button in each group. Although the application is still in the initialization, this will already trigger the “QAbstractButton::toggled()” signal which will trigger the “NamedRadioButton::selected()” signal that is connected to the text edit, resulting in the first messages already populating it on startup.

    auto mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget(leftGroup);
    mainLayout->addWidget(rightGroup);
    mainLayout->addWidget(textEdit);
}

The constructor wraps up creating a horizontal main layout that is applied to the application window and adding the two radio groups and the text edit to it.

When the application runs, the two radio groups will switch independently and each time a button is switched a message with it’s name is appended to the text edit.

Conclusion

While the example in this unit isn’t particularly useful, it does demonstrate the structure and messaging that practical applications do. The following units will continue to introduce new widget controls and carry on illustrating how real-world applications are wired. However they won’t dig too deep into the foundation of the Qt framework. That’s up to the intermediate units.