Qt QSS 选择器和 CSS2、CSS3 的选择器类似,建议先学习或者复习 CSS 选择器的语法。
QSS 虽然源自 CSS ,但也有稍许不同,而且支持的语法也没 CSS 那么多。
一、选择器类型
1.1 类选择器
1  | /*  | 
1.2 选择器分组
1  | /*同时匹配QPushButton、QLineEdit的实例  | 
1.3 ID 选择器
1  | /*  | 
1.4 属性选择器
1  | /*  | 
1.5 后代选择器
1  | /*  | 
1.6 子元素选择器
1  | /*  | 
1.7 伪状态
1  | /*  | 
1.8 子控件选择器
1  | QRadioButton::indicator::unchecked:disabled {  | 
二、选择器优先级
一句话归纳为:优先使用更具体的选择器。
具体实例如下:
1  | /*  | 
1  | /*  | 
1  | /*  | 
1  | /*  | 
1  | /*  | 
三、类型、属性、伪状态、子控件
关于 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  | QPushButton#btnTest::menu-indicator {  | 
同时使用伪状态和子控件时,先指定子控件,后指定伪状态:
1  | QComboBox::down-arrow:disabled{  | 
四、盒子模型
在使用 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-image和image属性,二者区别如下:
- 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  | #pushButtonMax[isMax="false"] {  | 
六、Q_PROPERTY
在 Qt 中可以使用Q_PROPERTY宏为 QObject 对象(含子对象)声明属性,任何被Q_PROPERTY声明的属性都能在 QSS 中使用qproperty-<property name>语法进行设置。
以 QToolButton 为例,QToolButton 继承至 QAbstractButton,QAbstractButton 拥有以下被 Q_PROPERTY 声明的属性:
1  | class Q_WIDGETS_EXPORT QAbstractButton : public QWidget  | 
在 QSS 中可以直接对属性赋值,如:
1  | QToolButton {  | 
七、Padding和Margin的使用
Margin 指控件和其他控件的间距,而 Padding 指控件内的内容与边框的间距。
二者的语法与CSS中的一样:
1  | margin: 25px 50px 75px 100px;  | 
从左到右依次为:上、右、下、左。
支持简写形式:
1  | margin: 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  | 先绘制 background-image  | 
8.5 示例
1  | QPushButton#pushButton4 {  | 

九、QSS编辑器
QSS 样式的编写是一个熟能生巧的过程,不仅不需要熟记常用的样式属性,还需要勤加练习,多多验证,灵活运用。
为了方便学习和验证 QSS 样式,我开发了一个 QSS 编辑器,通过该编辑器可以实时预览 QSS 的生效样式,而且编辑器还内置了两套完整的QSS主题,方便初学者学习 QSS 属性。
项目地址: