topic logo
Qt ⇢ Unit 12

Sliders

all the widgets with range values

Summary

This unit introduces a set of widgets that set or display a value within a range.

Background

This unit continues to introduce new widgets. This time it’s four widgets that allow the user to change or simply display an integer value within a set range.

New Classes

  • QSlider: A simple slider that can be dragged from minimum to maximum value.
  • QScrollBar: A scroll bar is often used in scroll views to pan across something that does not fit in the window. They can be used standalone too, same as the slider.
  • QSpinBox: The spin box shows the value in numerical form, and allows the user to either enter a new value or use the side spinners to change the value by single units.
  • QProgressBar: The progress is similar as the above controls, but it only allows display, not setting of values by the user.

ConnectedSliders Example Application

The application creates an instance of the four new widgets and puts them in a vertical layout. Then it connects the “valueChanged()” signals, so that all four controls change the value simultaneously.

Four Boundary Value Controls
Four Boundary Value Controls
connectedsliders.cpp - constructor
ConnectedSliders::ConnectedSliders(QWidget *parent)
	: QWidget(parent)
{
    setWindowTitle("Sliders");

    slider = new QSlider(Qt::Horizontal);
    scrollBar = new QScrollBar(Qt::Horizontal);
    spinBox = new QSpinBox();
    progressBar = new QProgressBar();

First the four widgets are initialized. The slider and scroll bar can have vertical and horizontal orientations. These are passed with the constructor. The others are always horizontal.

    slider->setMinimum(0);
    slider->setMaximum(100);
    scrollBar->setMinimum(0);
    scrollBar->setMaximum(100);
    spinBox->setMinimum(0);
    spinBox->setMaximum(100);
    progressBar->setMinimum(0);
    progressBar->setMaximum(100);

All four new widget types have a setMinimum() and setMaximum() method to define the value boundaries that they can display and control.

    auto layout = new QVBoxLayout(this);
    layout->addWidget(slider);
    layout->addWidget(scrollBar);
    layout->addWidget(spinBox);
    layout->addWidget(progressBar);

The widgets are added to a vertical layout. No size has been specified anywhere, so the layout will adjust the application window based on it’s contents. The controls it aligns based on the minimum sizes they require.

    connectSignals();
}

The ConnectedSliders class declares a connectSignals() and disconnectSignals() method. These two methods connect and disconnect the valueChanged() signal from the control to the onValueChanged() slot this class declares (see below).

connectedsliders.cpp - connecting valueChanged signals
void ConnectedSliders::connectSignals()
{
    connect(slider, &QSlider::valueChanged,
            this, &ConnectedSliders::onValueChanged);
    connect(scrollBar, &QScrollBar::valueChanged,
            this, &ConnectedSliders::onValueChanged);
    connect(spinBox,
            static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            this, &ConnectedSliders::onValueChanged);
    connect(progressBar, &QProgressBar::valueChanged,
            this, &ConnectedSliders::onValueChanged);
}

All of the four new widget classes come with a signal called “valueChanged(int newValue)”. This method connects them to a slot this class declares, that syncs the value of all controls.

The QSpinBox is different, in that it has the signal overloaded (there is an int and string variant). For this reason, an explicit call is required to address the signal with the integer argument. If this is not specified, the compiler will return an error message and abort. This has a cryptic syntax, so it’s generally advised to avoid declaring overloaded signals. There are only few of them in the Qt framework.

connectedsliders.cpp - disconnecting valueChanged signals
void ConnectedSliders::disconnectSignals()
{
    disconnect(slider, &QSlider::valueChanged,
            this, &ConnectedSliders::onValueChanged);
    disconnect(scrollBar, &QScrollBar::valueChanged,
            this, &ConnectedSliders::onValueChanged);
    disconnect(spinBox,
            static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            this, &ConnectedSliders::onValueChanged);
    disconnect(progressBar, &QProgressBar::valueChanged,
            this, &ConnectedSliders::onValueChanged);
}

The disconnectSignals() method mirrors the previous method, but it does exactly the opposite. When this method is called, all valueChanged() signals from the four controls is ignored.

connectedsliders.cpp - sync values when changed
void ConnectedSliders::onValueChanged(int newValue)
{
    disconnectSignals();
    slider->setValue(newValue);
    scrollBar->setValue(newValue);
    spinBox->setValue(newValue);
    progressBar->setValue(newValue);
    connectSignals();
}

The onValueChanged() slot is triggered any time a value is changed from either the spinbox, the scrollbar or the slider. The progress bar can’t be used to change the value by the user. Because the “valueChanged()” signal is also emitted, when the value is set programmatically, this would normally result in an infinite loop if the signals would not be disconnected at the top. The middle four lines change the value of all the controls to the new value (including the control that triggered it, but that’s not a problem). Then it re-connects all the signals, so when the user uses one of the controls to change a value next time, it syncs to the other controls again.

Conclusion

This was yet another unit to introduce new widgets. It also dealt with the possibility of disconnecting signals when setting widget states to avoid infinite signal loops. Finally it showed how to deal with overloaded signals, though these should be avoided. These are a few basic tools in any developers toolbox.