警告

本节包含自动从C++翻译为Python的代码片段,可能存在错误。

滑动条示例#

滑动条示例演示了如何在Qt中使用不同类型的滑动条:QSliderQScrollBarQDial

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

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

../_images/sliders-example.png

滑动条示例截图

滑动条示例包含两个类

  • SlidersGroup是一个自定义控件。它结合了一个QSlider,一个QScrollBar和一个QDial

  • Window是主要控件,结合了一个QGroupBox和一个SlidersGroup。该QGroupBox包含几个控件,用于控制类似滑动条的控件的行为。

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

窗口类定义#

class Window(QWidget):

    Q_OBJECT
# public
    Window(QWidget parent = None)
# private
    def createControls(title):
    def resizeEvent(e):
    slidersGroup = SlidersGroup()
    controlsGroup = QGroupBox()
    minimumLabel = QLabel()
    maximumLabel = QLabel()
    valueLabel = QLabel()
    invertedAppearance = QCheckBox()
    invertedKeyBindings = QCheckBox()
    minimumSpinBox = QSpinBox()
    maximumSpinBox = QSpinBox()
    valueSpinBox = QSpinBox()
    layout = QBoxLayout()

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

窗口类实现#

def __init__(self, parent):
    super().__init__(parent)

    slidersGroup = SlidersGroup(tr("Sliders"))
    createControls(tr("Controls"))

在构造函数中,我们首先创建一个显示滑动控件的 SlidersGroup 控件。使用 createControls() 创建控制控件,并将它们连接到滑动控件。

layout = 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() 创建的连接传播。

def createControls(self, title):
controlsGroup = QGroupBox(title)
minimumLabel = QLabel(tr("Minimum value:"))
maximumLabel = QLabel(tr("Maximum value:"))
valueLabel = QLabel(tr("Current value:"))
invertedAppearance = QCheckBox(tr("Inverted appearance"))
invertedKeyBindings = QCheckBox(tr("Inverted key bindings"))

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

在创建标签后,我们创建两个复选框。复选框通常用于表示应用程序中可启用或禁用的功能。当 invertedAppearance 被启用时,滑动器的值被反转。下表显示了不同类型滑动器的样式。

QSlider

QScrollBar

QDial

正常

反转

正常

反转

正常

反转

Qt::Horizontal

从左到右

从右到左

从左到右

从右到左

顺时针

逆时针

Qt::Vertical

从下到上

从上到下

从上到下

从下到上

顺时针

逆时针

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

invertedKeyBindings 选项被启用(对应于 invertedControls 属性)时,滑动器的滚动和键盘事件被反转。正常键绑定意味着滚动鼠标滚轮“向上”或使用像 page up 这样的键会增加滑动器的当前值。反转时,相同的滚动和键盘事件会将值移动到滑动器的最小值。如果滑动器的 外观 被反转,这可能会很有用:一些用户可能期望按键以相同的方式工作,而另一些用户可能期望 PageUp 在屏幕上表示“向上”。

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

minimumSpinBox = QSpinBox()
minimumSpinBox.setRange(-100, 100)
minimumSpinBox.setSingleStep(1)
maximumSpinBox = QSpinBox()
maximumSpinBox.setRange(-100, 100)
maximumSpinBox.setSingleStep(1)
valueSpinBox = QSpinBox()
valueSpinBox.setRange(-100, 100)
valueSpinBox.setSingleStep(1)

然后我们创建旋转框。 QSpinBox 允许用户通过点击上下按钮或按键盘上的上箭头和下箭头来修改当前显示的值。用户也可以手动输入值。旋转框控制 QSliderQScrollBarQDial 控件的 minimum、maximum 和当前值。

slidersGroup.valueChanged.connect(
        valueSpinBox.setValue)
valueSpinBox.valueChanged.connect(
        slidersGroup.setValue)
minimumSpinBox.valueChanged.connect(
        slidersGroup.setMinimum)
maximumSpinBox.valueChanged.connect(
        slidersGroup.setMaximum)
invertedAppearance.toggled.connect(
        slidersGroup.invertAppearance)
invertedKeyBindings.toggled.connect(
        slidersGroup.invertKeyBindings)
controlsLayout = 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() 信号。

我们通过它们的信号和槽来同步控件和滑块控件的行为。我们将每个控件连接到水平组和垂直组的滑块控件。我们还连接 orientationComboQStackedWidget,以显示正确的“页面”。最后,我们在 controlsGroup 组框内使用 QGridLayout 安排控件。

def resizeEvent(self, arg__0):

    if width() == 0 or height() == 0:
        return
    aspectRatio = double(width()) / double(height())
    if aspectRatio < 1.0:
        layout.setDirection(QBoxLayout.TopToBottom)
        slidersGroup.setOrientation(Qt.Horizontal)
     elif aspectRatio > 1.0:
        layout.setDirection(QBoxLayout.LeftToRight)
        slidersGroup.setOrientation(Qt.Vertical)

最后,我们重写了 QWidget 的 resizeEvent()。我们防止除以零,并在其他情况下计算控件的宽高比。如果窗口为纵向格式,则将布局设置为垂直组织控件和滑块的组,并将滑块的方向设置为水平。如果窗口为横向格式,则更改布局以显示滑块和控件并排,并将滑块方向设置为垂直。

SlidersGroup 类定义链接到本标题

class SlidersGroup(QGroupBox):

    Q_OBJECT
# public
    SlidersGroup(QString title, QWidget parent = None)
# signals
    def valueChanged(value):
# public slots
    def setValue(value):
    def setMinimum(value):
    def setMaximum(value):
    def invertAppearance(invert):
    def invertKeyBindings(invert):
    def setOrientation(orientation):
# private
    slider = QSlider()
    scrollBar = QScrollBar()
    dial = QDial()
    slidersLayout = QBoxLayout()

SlidersGroup 类继承自 QGroupBox。它提供了一个框架和标题,并包含一个 QSlider,一个 QScrollBar 和一个 QDial

我们提供了一个 valueChanged() 信号和一个具有等效功能的外部槽函数 setValue(),与 QAbstractSliderQSpinBox 中的函数相同。此外,我们还实现了其他几个公共槽函数,用于设置最小值和最大值,以及修改滑块小部件的显示外观、键盘绑定和设置方向。

SlidersGroup 类实现#

def __init__(self, title, parent):
    super().__init__(title, parent)

    slider = QSlider()
    slider.setFocusPolicy(Qt.StrongFocus)
    slider.setTickPosition(QSlider.TicksBothSides)
    slider.setTickInterval(10)
    slider.setSingleStep(1)
    scrollBar = QScrollBar()
    scrollBar.setFocusPolicy(Qt.StrongFocus)
    dial = QDial()
    dial.setFocusPolicy(Qt.StrongFocus)

首先,我们创建了具有适当属性的类似滑块的小部件。特别是我们为每个小部件设置焦点策略。Qt::FocusPolicy 是一个枚举类型,定义了小部件可以拥有的获取键盘焦点的一系列策略。Qt::StrongFocus 策略意味着小部件可以通过制表符键移动和鼠标点击接受焦点。

slider.valueChanged.connect(scrollBar.setValue)
scrollBar.valueChanged.connect(dial.setValue)
dial.valueChanged.connect(slider.setValue)
dial.valueChanged.connect(self.valueChanged)

然后,我们将小部件相互连接,以实现在其中一个小部件的当前值改变时,其他小部件保持同步。

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

slidersLayout = QBoxLayout(QBoxLayout.LeftToRight)
slidersLayout.addWidget(slider)
slidersLayout.addWidget(scrollBar)
slidersLayout.addWidget(dial)
setLayout(slidersLayout)

最后,我们为组框内的滑块小部件创建了布局。我们以水平排列开始布局滑块。

def setValue(self, value):
slider.setValue(value)

setValue() 槽函数设置了 QSlider 的值。我们不需要显式调用 setValue() 方法在 QScrollBarQDial 小部件上,因为当 QSlider 的值改变时,会发出 valueChanged() 信号,进而触发多米诺效应。

def setMinimum(self, value):
slider.setMinimum(value)
scrollBar.setMinimum(value)
dial.setMinimum(value)

def setMaximum(self, value):
slider.setMaximum(value)
scrollBar.setMaximum(value)
dial.setMaximum(value)

setMinimum()setMaximum() 槽用于由 Window 类设置 QSliderQScrollBarQDial 小部件的范围。

def invertAppearance(self, invert):
slider.setInvertedAppearance(invert)
scrollBar.setInvertedAppearance(invert)
dial.setInvertedAppearance(invert)

def invertKeyBindings(self, invert):
slider.setInvertedControls(invert)
scrollBar.setInvertedControls(invert)
dial.setInvertedControls(invert)

invertAppearance()invertKeyBindings() 槽用于控制子小部件的 invertedAppearanceinvertedControls 属性。

def setOrientation(self, orientation):

    slidersLayout.setDirection(orientation == Qt.Horizontal
                                ? QBoxLayout.TopToBottom
    super().__init__()
    scrollBar.setOrientation(orientation)
    slider.setOrientation(orientation)

setOrientation() 槽控制布局的方向和小部件的倾向。在水平组中,滑块具有水平倾向,并可在彼此之上布局。在垂直组中,滑块具有垂直倾向,并可在彼此旁边布局。

示例项目 @ code.qt.io