滑动条示例

滑动条示例展示了如何在Qt中使用各种类型的滑动条:QSliderQScrollBarQDial

Qt提供了三种类型的类似滑动条的控件:QSliderQScrollBarQDial。它们大多数功能都继承自 QAbstractSlider,在理论上,可以在应用程序中相互替换,因为它们之间的区别仅限于外观和感觉。本例展示了它们的外观、工作原理以及如何通过其属性来操作其行为和外观。

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

滑动条示例截图

滑动条示例由两个类组成

  • SlidersGroup 是一个自定义控件。它将一个 QSlider、一个 QScrollBar 和一个 QDial 结合在一起。
  • Window 是主控件,结合了一个 QGroupBox 和一个 SlidersGroup。该 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() 创建控制控件,并将它们连接到滑动条。

    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"));
}

在初始化最大、最小和当前值之前,我们将控件组的控件和滑动条放入一个水平布局中。当前值的初始化将通过我们在 valueSpinBoxSlidersGroup 控件之间建立的连接传播到滑动条控件。最小和最大值的传播将通过 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() 函数中,我们允许一个 QGroupBoxcontrolsGroup)显示控制小部件。组合框可以提供框架、标题和键盘快捷键,并在其内部显示各种其他小部件。控制小部件的组由两个复选框和三个带有标签的微调框组成。

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

QSliderQScrollBarQDial
正常反转正常反转正常反转
Qt::Horizontal从左到右从右到左从左到右从右到左顺时针逆时针
Qt::Vertical从下到上从上到下从上到下从下到上顺时针逆时针

通常反转垂直 QSlider 的外观。例如,控制音量的垂直滑块将通常从底部到顶部(非反转外观),而控制屏幕上对象位置的水平滑块可能会从顶部到底部,因为屏幕坐标从顶部到底部。

invertedKeyBindings 选项被启用(对应于 QAbstractSlider::invertedControls 属性)时,滑块的小轮和键盘事件被反转。正常按键绑定意味着滚动鼠标滚轮 "向上" 或使用 PageUp 等键将增加滑块的当前值。反转后,相同的鼠标滚轮和键盘事件将值移动到滑块的最小值。这可以很有用,如果滑块的 外观 被反转:一些用户可能期望键对值仍然按相同的方式工作,而其他用户可能期望 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() 插槽将小部件的当前值设置为新值,如果新值与旧值不同,则发射 valueChanged()

我们通过信号和插槽同步控制小部件和滑块小部件的行为。我们将每个控制小部件连接到水平和垂直滑块小部件组。我们还将 orientationCombo 连接到 QStackedWidget,以便显示正确的 "页面"。最后,我们在组合框 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() 函数。我们防止了除以零的错误,并计算了小部件的宽高比。如果窗口是纵向格式,则将布局设置为垂直组织控制小部件和滑动条的组,并给滑动条一个水平方向。如果窗口是横向格式,则将布局改为显示滑动条和控制小部件并排,并给滑动条一个垂直方向。

SlidersGroup 类定义

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。它提供了一个框架和一个标题,并包含一个 QSlider,一个 QScrollBar 和一个 QDial

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

SlidersGroup 类实现

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 的值。我们不需要显式地调用 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);
}

setMinimum()setMaximum() 槽由 Window 类用于设置 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

© 2024 Qt 公司有限。包含在本处的文档贡献是各自所有者的版权。本处提供的文档是在自由软件基金会发布、 GNU 自由文档许可第 1.3 版的条款下许可的。Qt 和相应的标志是芬兰的 The Qt Company Ltd. 和/或世界其他国家的商标。所有其他商标均为各自所有者的财产。