Qt QSS 选择器和 CSS2、CSS3 的选择器类似,建议先学习或者复习 CSS 选择器的语法

QSS 虽然源自 CSS ,但也有稍许不同,而且支持的语法也没 CSS 那么多。

一、选择器类型

1.1 类选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
匹配QPushButton的实例及其子类
*/
QPushButton {
color: blue;
}

/*
匹配QPushButton的实例,但不包含子类
*/
.QPushButton {
color: blue;
}

1.2 选择器分组

1
2
3
4
5
6
/*同时匹配QPushButton、QLineEdit的实例
*/
QPushButton,
QLineEdit {
color: blue;
}

1.3 ID 选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
匹配所有objectName为btnOK的实例
*/
*#btnOK {
font-weight: bold;
}

/*
和*#btnOK一样,匹配所有objectName为btnOK的实例
*/
#btnOK {
font-weight: bold;
}

/*
匹配所有objectName为btnOK的QPushButton实例
*/
QPushButton#btnOK {
font-weight: bold;
}

1.4 属性选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
匹配包含flat属性的所有实例
*/
*[flat] {
color: red;
}

/*
匹配包含flat属性的所有QPushButton的实例
*/
QPushButton[flat] {
color: red;
}

/*
匹配flat属性为true的所有QPushButton的实例
*/
QPushButton[flat="true"] {
color: red;
}

/*
匹配falt属性为true且default属性为false的所有QPushButton的实例
*/
QPushButton[flat="true"][default="false"] {
color: red;
}

1.5 后代选择器

1
2
3
4
5
6
/*
匹配属于QDialog后代(孩子,孙子等)的QPushButton所有实例
*/
QDialog QPushButton {
color: red;
}

1.6 子元素选择器

1
2
3
4
5
6
/*
匹配属于QDialog直接子类的QPushButton所有实例
*/
QDialog > QPushButton {
color: red;
}

1.7 伪状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
匹配hover状态的所有QPushButton的实例
*/
QPushButton:hover {
color: white;
}

/*
匹配同时为hover和enabled状态的所有QPushButton的实例
*/
QPushButton:hover:enabled {
color: white;
}

/*
匹配不为hover状态的所有QPushButton的实例
*/
qradiobutton: !hover {
color: red;
}

1.8 子控件选择器

1
2
3
QRadioButton::indicator::unchecked:disabled {
image: url(:/qss/radiobutton_unchecked_disable.png);
}

二、选择器优先级

一句话归纳为:优先使用更具体的选择器。

具体实例如下:

1
2
3
4
5
6
7
8
9
/*
QPushButton#okButton选择器比QPushButton更具体,所以选择QPushButton#okButton
*/
QPushButton#okButton {
color: gray;
}
QPushButton {
color: red;
}
1
2
3
4
5
6
7
8
9
10
/*
如下2个规则应用于QPushButton实例时:
因为QPushButton继承QAbstractButton,所以QPushButton比QAbstractButton更具体,选择QPushButton
*/
QPushButton {
color: red;
}
QAbstractButton {
color: gray;
}
1
2
3
4
5
6
7
8
9
/*
指定伪状态比不指定伪状态的选择器更具体,所以当鼠标悬停时选择QPushButton:hover,否则选择QPushButton
*/
QPushButton:hover {
color: white;
}
QPushButton {
color: red;
}
1
2
3
4
5
6
7
8
9
10
11
/*
如下2个规则应用于处于Enabled状态的QPushButton实例时:
鼠标悬停时,QPushButton:hover更能具体描述当前状态,所以选择QPushButton:hover
其他时候选择QPushButton:enabled
*/
QPushButton:enabled {
color: red;
}
QPushButton:hover {
color: white;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
如下2个规则应用于处于Enabled状态的QPushButton实例时:
鼠标悬停时,QPushButton:enabled:hover更能具体描述当前状态,所以选择QPushButton:enabled:hover
其他时候选择QPushButton:enabled
*/
QPushButton:enabled {
color: red;
}
QPushButton:hover {
color: white;
}
QPushButton:enabled:hover {
color: white;
}

三、类型、属性、伪状态、子控件

关于 QSS 支持哪些类型、每种类型支持哪些属性、伪状态和子控件,在 Qt 的官方的英文文档中有详细的介绍:
《Qt Style Sheets Reference》

也可以在Qt Assistant中搜索”Qt Style Sheets Reference”打开帮助文档。

3.1 伪状态列表

为了方便查阅,这里列举出QSS目前支持的所有伪状态。

伪状态都是以一个冒号:开头,如:active

伪状态 描述
:active 此状态在Widget驻留在活动窗口时设置
:adjoins-item 此状态在QTreeView的::branch与一个item相邻时设置
:alternate 当QAbstractItemView::alternatingRowColors()设置为真时,在绘制QAbstractItemView的行时,为每个交替行设置此状态
:bottom 此item位于底部时设置。例如,QTabBar有位于底部的选项卡
:checked 此item被选中时设置。例如,QAbstractButton的checked状态
:closable 此item可以被关闭时设置。例如,QDockWidget的QDockWidget::DockWidgetClosable特性开启时
:closed 此item处于关闭状态时设置。例如,QTreeView中未展开的item
:default 此item的默认状态时设置。例如,一个default的QPushButton或QMenu中的一个默认动作
:disabled 此item被禁用时设置
:editable 如QComboBox是可编辑时设置
:edit-focus 此item具有编辑焦点(参考QStyle::State_HasEditFocus)时设置。此状态仅对Qt扩展应用程序可用
:enabled 此item已启用时设置
:exclusive 此item是属于某个独占组时设置。例如,独占QActionGroup中的菜单项
:first 此item是列表中的第一项时设置。例如,QTabBar中的第一个选项卡
:flat 此item是扁平时设置。例如,一个扁平的QPushButton
:floatable 此item可以浮动时设置。例如,QDockWidget的QDockWidget::DockWidgetFloatable的特性开启时
:focus 此item具有输入焦点时设置
:has-children 此item具有子对象时设置。例如,QTreeView中具有子项的项
:has-sibling 此item具有兄弟对象时设置。例如,QTreeView中与之相邻的项
:horizontal 此item处于水平方向时设置
:hover 鼠标悬浮在此item上时设置
:indeterminate 此item处于不确定状态时设置。例如,QCheckBox或QRadioButton被部分选中
:last 此item是列表中的最后一项时设置。例如,QTabBar中的最后一个选项卡
:left 此item位于左侧时设置。例如,QTabBar有位于左侧的选项卡
:maximized 此item处于最大化状态时设置。例如,一个最大化的QMdiSubWindow
:middle 此item是列表中的中间一项时设置。例如,一个不在QTabBar中的开头或结尾的选项卡
:minimized 此item处于最小化状态时设置。例如,一个最小化的QMdiSubWindow
:movable 此item可以被移动时设置。例如, QDockWidget的QDockWidget::DockWidgetMovable特性开启时
:no-frame 此item没有边框时设置。例如,没有边框的QSpinBox或QLineEdit
:non-exclusive 此item是属于非独占组时设置。例如,非独占QActionGroup中的菜单项
:off 对可以切换的items,这适用于处于off状态的item
:on 对可以切换的items,这适用于处于on状态的widget
:only-one 此item是列表中的唯一项时设置。例如,一个在QTabBar中单独的选项卡
:open 此item处于打开状态时设置。例如,QTreeView中的展开项,或带有菜单的QComboBox或QPushButton
:next-selected 此item是列表中的下一个被选中的项时设置。例如,在QTabBar中当前选项卡的下一个要选中的选项卡
:pressed 鼠标正在按压在此item上时设置
:previous-selected 此item是列表中的上一个被选中的项时设置。例如,在QTabBar中当前选项卡的上一个要选中的选项卡
:read-only 此item处于只读或不可编辑状态时设置。例如,一个只读QLineEdit或不可编辑的QComboBox
:right 此item位于右侧时设置。例如,QTabBar有位于右侧的选项卡
:selected 此item处于选中状态时设置。例如,一个在QTabBar中被选中的选项卡或一个在菜单中被选中的菜单项
:top 此item位于顶部时设置。例如,QTabBar有位于顶部的选项卡
:unchecked 此item处于未被选中状态时设置
:vertical 此item处于垂直方向时设置
:window Widget是一个窗口(例如,一个顶层Widget)时设置

3.2 子控件列表

伪状态都是以两个冒号::开头,如::item

子控件 描述
::add-line 在QScrollBar中跳转下一行的按钮
::add-page 在QScrollBar中滑动条和add-line之间的区域
::branch 在QTreeView中的分支指示器
::chunk 在QProgressBar中的进度块
::close-button 在QDockWidget或QTabBar选项卡的关闭按钮
::corner 在QAbstractScrollArea中两个滚动条之间的角落
::down-arrow 在QComboBox、QHeaderView(排序指示器)、QScrollBar或QSpinBox的向下箭头
::down-button 在QScrollBar或QSpinBox中的向下按钮
::drop-down 在QComboBox中的下拉框
::float-button 在QDockWidget中的浮动按钮
::groove 在QSlider中的滑动槽
::indicator 在QAbstractItemVIew、QCheckBox、QRadioButton、可选中的菜单项或可选中的QGroupBox中的指示器
::handle 在QScrollBar、QSplitter和QSlider中的操作条(滑动条)
::icon 在QAbstractItemVIew或QMenu中的图标
::item 在QAbstractItemVIew、QMenuBar、QMenu或QStatuBar中的一项
::left-arrow 在QScrollBar中的向左箭头
::left-corner 在QTabWidget中的左上角
::menu-arrow 带有菜单的QToolButton中的箭头
::menu-button 在QToolButton中的菜单按钮
::menu-indicator 在QPushButton中的菜单指示器
::right-arrow 在QMenu或QScrollBar中的向右箭头
::pane 在QTabWidget中的边或框
::right-corner 在QTabWidget中的右上角
::scroller 在QMenu或QTabBar中的滚动条
::section 在QHeaderView中的区块
::separator 在QMenu或QMainWIndow中分隔条
::sub-line 在QScrollBar中跳转上一行的按钮
::sub-page 在QScrollBar中滑动条和sub-line之间的区域
::tab 在QTabBar或QToolBox中选项卡
::tab-bar 在QTabWidget中的选项卡栏
::tear 在QTabBar中的tear指示器
::tearoff 在QMenu中的tear-off指示器
::text 在QAbstractItemView中的文本
::title 在QGroupBox或QDockWidget中的标题栏
::up-arrow 在QComboBox、QHeaderView(排序指示器)、QScrollBar或QSpinBox的向上箭头
::up-button 在QScrollBar或QSpinBox中的向上按钮

使用示例:

1
2
3
QPushButton#btnTest::menu-indicator {
image: url(indicator.png);
}

同时使用伪状态和子控件时,先指定子控件,后指定伪状态:

1
2
3
QComboBox::down-arrow:disabled{
image:none;
}

四、盒子模型

在使用 QSS 设置样式时,有一个关键的概念需要知晓,那就是“盒子模型”(即Box Model)。

每个 Widget 都被视为具有 4 个同心矩形的框:

MARGIN矩形、BORDER矩形、PADDING矩形和 CONTENT矩形,上图标注了每个矩形的区域。

默认情况下MARGIN矩形、BORDER矩形、PADDING矩形的宽度都为 0,这样在默认情况下,4 个矩形就重合为 1 个CONTENT矩形了。

同样,默认情况下 background-image 指定的背景,只在 border 内的区域绘制,但我们也可以使用 background-clip 或 background-origin 属性来更改这种默认行为。

如何实现背景图像随 Widget 大小自动缩放?

background-image 指定的背景图像无法随 Widget 大小自动缩放,要提供可以随 Widget 大小缩放的背景图像可以使用border-imageimage属性,二者区别如下:

  • border-image 属性指定的图像从 border 及其内的区域开始绘制,会导致 border 属性被覆盖。
  • image 属性指定的图像从绘制到 content 区域内,image 指定的 url 为 SVG 图像,则支持自动缩放,非 SVG 图像仅支持自动缩小。

五、动态属性

通过 setProperty 方法设置 QWidget 对象属性,在 QSS 中可以根据不同的属性值应用不同的样式。

例如,设置 pushButtonMax 按钮的 isMax 属性,表示当前窗口是否最大化:

1
pushButtonMax->setProperty("isMax", this->isMaximized() ? true : false);

在 QSS 中根据不同的属性值应用不同的样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pushButtonMax[isMax="false"] {
width: 40px;
height: 40px;
background: gray;
}

#pushButtonMax[isMax="false"]:hover {
background: blue;
}

#pushButtonMax[isMax="true"] {
width: 40px;
height: 40px;
background: transparent;
}

#pushButtonMax[isMax="true"]:hover {
background: red;
}

六、Q_PROPERTY

在 Qt 中可以使用Q_PROPERTY宏为 QObject 对象(含子对象)声明属性,任何被Q_PROPERTY声明的属性都能在 QSS 中使用qproperty-<property name>语法进行设置。

以 QToolButton 为例,QToolButton 继承至 QAbstractButton,QAbstractButton 拥有以下被 Q_PROPERTY 声明的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Q_WIDGETS_EXPORT QAbstractButton : public QWidget
{
Q_OBJECT

Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(QIcon icon READ icon WRITE setIcon)
Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
#ifndef QT_NO_SHORTCUT
Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut)
#endif
Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable)
Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)
Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat)
Q_PROPERTY(bool autoExclusive READ autoExclusive WRITE setAutoExclusive)
Q_PROPERTY(int autoRepeatDelay READ autoRepeatDelay WRITE setAutoRepeatDelay)
Q_PROPERTY(int autoRepeatInterval READ autoRepeatInterval WRITE setAutoRepeatInterval)
Q_PROPERTY(bool down READ isDown WRITE setDown DESIGNABLE false)
....
}

在 QSS 中可以直接对属性赋值,如:

1
2
3
4
5
QToolButton {
qproperty-text: "文本";
qproperty-icon: url(://img/icon.png);
qproperty-iconsize: 100px 100px;
}

七、Padding和Margin的使用

Margin 指控件和其他控件的间距,而 Padding 指控件内的内容与边框的间距。

二者的语法与CSS中的一样:

1
2
margin: 25px 50px 75px 100px;
padding: 25px 50px 75px 100px;

从左到右依次为:上、右、下、左。

支持简写形式:

1
2
margin: 25px 50px;
padding: 25px 50px;

表示:上下间距为25px,左右间距为50px。

八、几种图片设置方法

大家在使用 QSS 进行图片设置时,也许被 image, border-image, background-image 这几个属性的差异困扰过,下面就来讲解一下这个几个属性的异同点。

8.1 background-image

background-image 按图片实际尺寸显示图片,超过控件显示区域的部分会被裁剪掉。

虽能显示 SVG,但无法对 SVG 进行无损缩放;

关于 background-image 的几个附属属性的作用,可以一句话概括为:

从 background-origin 区域的 background-position 位置开始绘制图像,并以 background-repeat 方式进行重复;最后将图像 background-clip 区域以外的范围裁剪掉(即不显示)。

8.2 image

image 会将图片按图片原长宽比进行缩放,并保证填充满控件 content 区域。

image 支持 SVG 矢量图显示和无损缩放。

image 可以使用 image-position 来指定图片开始显示的位置(参考上面background-position)。

8.3 border-image

控件 border 区域的长宽比来缩放图片,保证填充满控件 border 区域,支持 SVG 矢量图显示和无损缩放;

8.4 绘制顺序

如果在一个控件中同时指定background-image,border-image,image 这三个属性,会按照如下的顺序进行绘制:

1
2
3
先绘制 background-image
然后 border-image
最后 image

8.5 示例

1
2
3
4
5
6
7
8
9
10
11
QPushButton#pushButton4 {
border: 20px solid rgba(0,0,255,0.5);
padding: 20px;
margin: 20px;
background-image: url(:/QssUsage/Image/头像.png); /*下图中的圆形头像*/
background-origin: margin;
background-position:top left;
background-repeat:repeat-x;
background-clip: margin;
}

九、QSS编辑器

QSS 样式的编写是一个熟能生巧的过程,不仅不需要熟记常用的样式属性,还需要勤加练习,多多验证,灵活运用。

为了方便学习和验证 QSS 样式,我开发了一个 QSS 编辑器,通过该编辑器可以实时预览 QSS 的生效样式,而且编辑器还内置了两套完整的QSS主题,方便初学者学习 QSS 属性。

项目地址:

https://github.com/winsoft666/qss-editor