滑块示例

Sliders 示例展示了如何使用 Qt 中不同类型的滑块:QSliderQScrollBarQDial

Qt 提供了三种类似滑块的部件:QSlider,QScrollBarQDial 。它们都从QAbstractSlider 继承了大部分功能,理论上可以在应用程序中相互替代,因为它们的区别只在于外观和感觉。本示例展示了它们的外观、工作方式以及如何通过属性来操作它们的行为和外观。

该示例还演示了如何使用信号和槽来同步两个或多个部件的行为,以及如何覆盖resizeEvent() 来实现响应式布局。

滑块示例截图

滑块示例由两个类组成:

  • SlidersGroup 是一个自定义部件。它结合了 、 和 。QSlider QScrollBar QDial
  • Window 是主窗口部件,结合了 和 SlidersGroup。 包含几个控制滑块类 widget 行为的 widget。QGroupBox QGroupBox

首先,我们将回顾一下Window 类,然后再看看SlidersGroup 类。

窗口类定义

class Window : public QWidget
{
    Q_OBJECT

public:
    Window(QWidget *parent = nullptr);

private:
    void createControls(const QString &title);
    void resizeEvent(QResizeEvent *e);

    SlidersGroup *slidersGroup;

    QGroupBox *controlsGroup;
    QLabel *minimumLabel;
    QLabel *maximumLabel;
    QLabel *valueLabel;
    QCheckBox *invertedAppearance;
    QCheckBox *invertedKeyBindings;
    QSpinBox *minimumSpinBox;
    QSpinBox *maximumSpinBox;
    QSpinBox *valueSpinBox;
    QBoxLayout *layout;
};

Window 类继承自QWidget 。该类显示滑块部件,允许用户设置其最小值、最大值和当前值,并自定义其外观、按键绑定和方向。我们使用一个私有的createControls() 函数来创建提供这些控制机制的部件,并将它们连接到滑块部件。

窗口类的实现

Window::Window(QWidget *parent)
    : QWidget(parent)
{
    slidersGroup = new SlidersGroup(tr("Sliders"));

    createControls(tr("Controls"));

在构造函数中,我们首先创建显示滑块部件的SlidersGroup 部件。通过createControls() ,我们创建了控制 widget,并将它们连接到滑块。

    layout = new QBoxLayout(QBoxLayout::LeftToRight);
    layout->addWidget(controlsGroup);
    layout->addWidget(slidersGroup);
    setLayout(layout);

    minimumSpinBox->setValue(0);
    maximumSpinBox->setValue(20);
    valueSpinBox->setValue(5);

    setWindowTitle(tr("Sliders"));
}

在初始化最小值、最大值和当前值之前,我们将控制 widget 组和滑块置于水平布局中。当前值的初始化将通过valueSpinBoxSlidersGroup widget 之间的连接传播到滑块 widget。最小值和最大值将通过我们在createControls() 创建的连接传播。

void Window::createControls(const QString &title)
{
    controlsGroup = new QGroupBox(title);

    minimumLabel = new QLabel(tr("Minimum value:"));
    maximumLabel = new QLabel(tr("Maximum value:"));
    valueLabel = new QLabel(tr("Current value:"));

    invertedAppearance = new QCheckBox(tr("Inverted appearance"));
    invertedKeyBindings = new QCheckBox(tr("Inverted key bindings"));

在私有createControls() 函数中,我们让QGroupBox (controlsGroup) 显示控制 widget。组框可以提供一个框架、一个标题和一个键盘快捷键,并在其内部显示各种其他部件。这组控制 widget 由两个复选框和三个带标签的旋转框组成。

创建标签后,我们创建两个复选框。复选框通常用于表示应用程序中可启用或禁用的功能。当invertedAppearance 启用时,滑块的值会反转。下表显示了不同滑块类部件的外观:

QSliderQScrollBarQDial
正常反转正常反转正常反转
Qt::Horizontal从左向右右至左左至右右至左顺时针逆时针
Qt::Vertical从下至上上至下上至下底对顶顺时针逆时针

垂直QSlider 的外观颠倒是很常见的。例如,控制音量的垂直滑块通常是从下至上(非颠倒外观),而控制屏幕上对象位置的垂直滑块可能是从上至下,因为屏幕坐标是从上至下。

启用invertedKeyBindings 选项(与QAbstractSlider::invertedControls 属性相对应)后,滑块的滚轮和按键事件将反转。正常的按键绑定意味着滚动鼠标滚轮 "向上 "或使用翻页键等按键将使滑块的当前值增加到最大值。反转时,相同的滚轮和按键事件将使滑块的值向最小值移动。如果滑块的外观是倒置的,这将非常有用:有些用户可能希望按键仍以相同方式作用于数值,而另一些用户可能希望PageUp 在屏幕上表示 "向上"。

请注意,对于水平和垂直滚动条,按键绑定默认是反向的:PageDown 增加当前值,而PageUp 则减少当前值。

    minimumSpinBox = new QSpinBox;
    minimumSpinBox->setRange(-100, 100);
    minimumSpinBox->setSingleStep(1);

    maximumSpinBox = new QSpinBox;
    maximumSpinBox->setRange(-100, 100);
    maximumSpinBox->setSingleStep(1);

    valueSpinBox = new QSpinBox;
    valueSpinBox->setRange(-100, 100);
    valueSpinBox->setSingleStep(1);

然后,我们创建旋转框。QSpinBox 允许用户通过单击向上和向下按钮或按键盘上的UpDown 键来选择一个值,以修改当前显示的值。用户也可以手动输入数值。旋转框控制QSliderQScrollBarQDial 部件的最小值、最大值和当前值。

    connect(slidersGroup, &SlidersGroup::valueChanged,
            valueSpinBox, &QSpinBox::setValue);
    connect(valueSpinBox, &QSpinBox::valueChanged,
            slidersGroup, &SlidersGroup::setValue);
    connect(minimumSpinBox, &QSpinBox::valueChanged,
            slidersGroup, &SlidersGroup::setMinimum);
    connect(maximumSpinBox, &QSpinBox::valueChanged,
            slidersGroup, &SlidersGroup::setMaximum);
    connect(invertedAppearance, &QCheckBox::toggled,
            slidersGroup, &SlidersGroup::invertAppearance);
    connect(invertedKeyBindings, &QCheckBox::toggled,
            slidersGroup, &SlidersGroup::invertKeyBindings);

    QGridLayout *controlsLayout = new QGridLayout;
    controlsLayout->addWidget(minimumLabel, 0, 0);
    controlsLayout->addWidget(maximumLabel, 1, 0);
    controlsLayout->addWidget(valueLabel, 2, 0);
    controlsLayout->addWidget(minimumSpinBox, 0, 1);
    controlsLayout->addWidget(maximumSpinBox, 1, 1);
    controlsLayout->addWidget(valueSpinBox, 2, 1);
    controlsLayout->addWidget(invertedAppearance, 0, 2);
    controlsLayout->addWidget(invertedKeyBindings, 1, 2);
    controlsGroup->setLayout(controlsLayout);

}

然后,我们将slidersGroupvalueSpinBox 连接起来,这样,当其中一个部件的当前值发生变化时,滑块部件和控制部件就会同步运行。valueChanged() 信号的参数是新值。setValue() 槽将 widget 的当前值设置为新值,如果新值与旧值不同,则发出valueChanged()

我们通过控制 widget 和滑块 widget 的信号和槽来同步它们的行为。我们将每个控制 widget 连接到水平和垂直滑块 widget 组。我们还将orientationCombo 连接到QStackedWidget ,以便显示正确的 "页面"。最后,我们将控制 widget 布局在controlsGroup 组框中的QGridLayout 中。

void Window::resizeEvent(QResizeEvent *)
{
    if (width() == 0 || height() == 0)
        return;

    const double aspectRatio = double(width()) / double(height());

    if (aspectRatio < 1.0) {
        layout->setDirection(QBoxLayout::TopToBottom);
        slidersGroup->setOrientation(Qt::Horizontal);
    } else if (aspectRatio > 1.0) {
        layout->setDirection(QBoxLayout::LeftToRight);
        slidersGroup->setOrientation(Qt::Vertical);
    }
}

最后,我们覆盖QWidget 中的 resizeEvent() 。我们要防止除以零,否则会计算 widget 的宽高比。如果窗口为纵向格式,则我们将布局设置为纵向组织控制 widget 和滑块组,并赋予滑块水平方向。如果窗口为横向格式,那么我们将改变布局,使滑块和控制 widget 并排显示,并赋予滑块垂直方向。

滑块组类定义

class SlidersGroup : public QGroupBox
{
    Q_OBJECT

public:
    SlidersGroup(const QString &title, QWidget *parent = nullptr);

signals:
    void valueChanged(int value);

public slots:
    void setValue(int value);
    void setMinimum(int value);
    void setMaximum(int value);
    void invertAppearance(bool invert);
    void invertKeyBindings(bool invert);
    void setOrientation(Qt::Orientation orientation);

private:
    QSlider *slider;
    QScrollBar *scrollBar;
    QDial *dial;
    QBoxLayout *slidersLayout;
};

SlidersGroup 类继承于QGroupBox 。它提供了一个框架和一个标题,并包含QSliderQScrollBarQDial

我们提供了一个valueChanged() 信号和一个公共setValue() 插槽,其功能与QAbstractSliderQSpinBox 中的功能相当。此外,我们还实现了其他几个公共插槽,用于设置最小值和最大值、反转滑块部件的外观和按键绑定以及设置方向。

滑块组类的实现

SlidersGroup::SlidersGroup(const QString &title, QWidget *parent)
    : QGroupBox(title, parent)
{
    slider = new QSlider;
    slider->setFocusPolicy(Qt::StrongFocus);
    slider->setTickPosition(QSlider::TicksBothSides);
    slider->setTickInterval(10);
    slider->setSingleStep(1);

    scrollBar = new QScrollBar;
    scrollBar->setFocusPolicy(Qt::StrongFocus);

    dial = new QDial;
    dial->setFocusPolicy(Qt::StrongFocus);

首先,我们创建具有相应属性的滑块类部件。Qt::FocusPolicy 是一个枚举类型,它定义了部件在获取键盘焦点时的各种策略。Qt::StrongFocus 策略表示该部件通过制表和点击两种方式获取焦点。

    connect(slider, &QSlider::valueChanged, scrollBar, &QScrollBar::setValue);
    connect(scrollBar, &QScrollBar::valueChanged, dial, &QDial::setValue);
    connect(dial, &QDial::valueChanged, slider, &QSlider::setValue);
    connect(dial, &QDial::valueChanged, this, &SlidersGroup::valueChanged);

然后,我们将这些部件相互连接起来,这样当其中一个部件的当前值发生变化时,它们就会保持同步。

我们将dialvalueChanged() 信号连接到SlidersGroupvalueChanged() 信号,以便将改变的值通知应用程序中的其他部件(即控制部件)。

    slidersLayout = new QBoxLayout(QBoxLayout::LeftToRight);
    slidersLayout->addWidget(slider);
    slidersLayout->addWidget(scrollBar);
    slidersLayout->addWidget(dial);
    setLayout(slidersLayout);
}

最后,我们在组框中创建滑块部件的布局。我们首先对滑块进行水平排列。

void SlidersGroup::setValue(int value)
{
    slider->setValue(value);
}

setValue() 槽设置了QSlider 的值。我们不需要在QScrollBarQDial 部件上明确调用setValue() ,因为QSlider 会在其值发生变化时发出valueChanged() 信号,从而引发多米诺骨牌效应。

void SlidersGroup::setMinimum(int value)
{
    slider->setMinimum(value);
    scrollBar->setMinimum(value);
    dial->setMinimum(value);
}

void SlidersGroup::setMaximum(int value)
{
    slider->setMaximum(value);
    scrollBar->setMaximum(value);
    dial->setMaximum(value);
}

Window 类使用setMinimum()setMaximum() 插槽来设置QSliderQScrollBarQDial 部件的范围。

void SlidersGroup::invertAppearance(bool invert)
{
    slider->setInvertedAppearance(invert);
    scrollBar->setInvertedAppearance(invert);
    dial->setInvertedAppearance(invert);
}

void SlidersGroup::invertKeyBindings(bool invert)
{
    slider->setInvertedControls(invert);
    scrollBar->setInvertedControls(invert);
    dial->setInvertedControls(invert);
}

invertAppearance()invertKeyBindings() 插槽控制子部件的invertedAppearanceinvertedControls 属性。

void SlidersGroup::setOrientation(Qt::Orientation orientation)
{
    slidersLayout->setDirection(orientation == Qt::Horizontal
                                ? QBoxLayout::TopToBottom
                                : QBoxLayout::LeftToRight);
    scrollBar->setOrientation(orientation);
    slider->setOrientation(orientation);
}

setOrientation() 槽控制布局方向和滑块的方向。在水平组中,滑块的方向是水平的,并且是相互排列的。在垂直组中,滑块的方向是垂直的,并且彼此相邻。

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.