QSS独门秘籍:subcontrol
QSS是C++ Qt中的界面美化神器,其语法和CSS区别不大,但是QSS有一个独有的功能——subcontrol,这是CSS所没有的,一个widget往往由多个子部件构成,利用subcontrol可以对窗口部件的某些子部件做精细处理,从而使得界面美化达到定制最大化。
以下内容来源于http://qtdebug.com/QSS-Subcontrol.html
http://qtdebug.com/index.html上有很多不错的教程
Subcontrol 的绘制位置由 subcontrol-origin、subcontrol-position, top, left 来指定,就先从这几个属性开始入手。
Subcontrol-Origin
subcontrol-origin 定义在 parent widget 中绘制 subcontrol 的参考矩形,默认在 padding 的矩形中绘制。
The origin rectangle of the subcontrol within the parent element. If this property is not specified, the default is padding.
subcontrol-origin 有 4 个值可选:
- margin
- border
- padding
- content
下图展示了 subcontrol-origin 的值不同时,在 parent widget 的不同位置进行绘制 subcontrol:
Subcontrol-Position
已经知道 subcontrol 要在 parent widget 的某个矩形区域里绘制,如 padding rectangle,这个矩形这么大,具体要在这个矩形的哪个位置绘制呢?使用 subcontrol-position 来指定,不同的 subcontrol 的 subcontrol-position 默认值不同,例如 QSlider 的 handle 的默认值是 center center,QSpinBox 的 up-button 的默认值是 right top。
The alignment of the subcontrol within the origin rectangle specified by subcontrol-origin. If this property is not specified, it defaults to a value that depends on the subcontrol.
subcontrol-position 水平方向有 3 个值可选:
- left
- center
- right
subcontrol-position 垂直方向有 3 个值可选:
- top
- center
- bottom
用 Top 和 Left 微调 Subcontrol 的位置
Top 和 left 的主要作用是 :hover,:pressed 等发生时,用 top 和 left 偏移一点 subcontrol,这样就看到 subcontrol 的鼠标动作了,偏移是相对于 subcontrol-orign 和 subcontrol-position 确定的位置,top 和 left 的默认值是 0。
用下面的 QSS 总结一下 subcontrol-origin, subcontrol-position, top, left:
- QSpinBox 的 up-button 放置在 QSpinBox 的左边垂直剧中
- 当鼠标放到 up-button 上时,将其向右下角偏移 1px
- 当鼠标离开 up-button 后,up-button 移回到原来的位置
QSpinBox::up-button {
subcontrol-origin: margin;
subcontrol-position: left center;
}
QSpinBox::up-button:hover {
top: 1px;
left: 1px;
}
接下来就具体的介绍每一个 Widget 有哪些 subcontrol,怎么 QSS 它们。
QCheckBox
QCheckBox 的 subcontrol 有 ::indicator,比较有意思的是,text 总是显示在 indicator 右边,所以如果 indicator 靠右边显示的话,text 很可能就看不到了。
QRadioButton 的 QSS 和 QCheckBox 的一样,所以就不在重复介绍。

下面 QSS 的效果如图:
QCheckBox {
color: lightgray;
background: rgb(44, 44, 44);
border: 10px solid rgb(76, 76, 76);
spacing: 10px; /* indicator 和 text 的间隔 */
padding: 10px;
}
QCheckBox::indicator {
subcontrol-origin: border;
subcontrol-position: left center;
background: white;
border: 2px solid rgb(170, 170, 170);
}
QCheckBox::indicator:checked {
background: rgb(76, 76, 76);
}
修改
subcontrol-origin和subcontrol-position为不同的值看看效果是什么。
QComboBox
QComboBox 的 subcontrol 有 drop-down。

下面 QSS 的效果如图:
QComboBox {
color: lightgray;
background: rgb(44, 44, 44);
border: 10px solid rgb(76, 76, 76);
spacing: 10px; /* indicator 和 text 的间隔 */
padding: 10px;
}
QComboBox::drop-down {
width: 15px;
height: 10px;
subcontrol-origin: border;
subcontrol-position: right center;
background: white;
border: 2px solid rgb(170, 170, 170);
border-radius: 3px;
}
QComboBox::drop-down:hover {
background: rgb(76, 76, 76);
}
QComboBox::drop-down:on {
background: black;
top: 1px;
left: 1px;
}
QSpinBox, QDateEdit, QTimeEdit, QDateTimeEdit
QSpinBox 的 subcontrol 有 ::up-button, ::down-button, ::up-arrow, ::down-arrow。
- up-button 显示在 QSpinBox 里,它的 subcontrol-origin 是相对于 QComboBox 的
- down-button 显示在 QSpinBox 里,它的 subcontrol-origin 是相对于 QComboBox 的
- up-arrow 显示在 up-button 里,它的 subcontrol-origin 是相对于 up-button 的
- down-arrwo 显示在 down-button 里,它的 subcontrol-origin 是相对于 down-button 的

QDateEdit, QTimeEdit, QDateTimeEdit 的 subcontrol 和 QSpinBox 是一样的,只需要把下面 QSS 里的 QSpinBox 换成 QDateEdit,QTimeEdit 或 QDateTimeEdit 即可。
下面 QSS 的效果如图,down-button 靠左垂直居中,up-button 靠右垂直居中:
QSpinBox {
color: lightgray;
background: rgb(44, 44, 44);
border: 10px solid rgb(76, 76, 76);
padding: 5px;
}
QSpinBox::down-button, QSpinBox::up-button {
subcontrol-origin: border;
width: 16px;
height: 10px;
background: white;
border: 2px solid rgb(170, 170, 170);
}
QSpinBox::down-button {
subcontrol-position: center left;
}
QSpinBox::up-button {
subcontrol-position: center right;
}
QSpinBox::up-arrow, QSpinBox::down-arrow {
subcontrol-origin: content;
subcontrol-position: center center;
width: 6px;
height: 6px;
background: rgb(76, 76, 76);
}
QSlider
QSlider 的 subcontrol 有 ::groove(槽),::handle,::add-page 和 ::sub-page。
- groove 显示在 QSlider 里,它的 subcontrol-origin 是相对于 QSlider 的
- handle 显示在 groove 里,它的 subcontrol-origin 是相对于 groove 的
- sub-page 显示在 groove 里,它的 subcontrol-origin 是相对于 groove 的
- add-page 显示在 groove 里,它的 subcontrol-origin 是相对于 groove 的
- handle, sub-page, add-page 虽然都显示在 groove 里,但是都可以把它们扩展到 groove 外

下面 QSS 的效果如图:
QSlider {
background: rgb(170, 170, 170);
padding: 2px;
height: 40px;
}
QSlider::groove:horizontal {
subcontrol-origin: content;
background: rgb(76, 76, 76);
/* the groove expands to the size of the slider by default.
by giving it a height, it has a fixed size */
height: 20px;
}
QSlider::handle:horizontal {
background-color: rgb(50, 50, 50);
width: 40px;
border-radius: 20px;
/* handle is placed by default on the contents rect of the groove.
Expand outside the groove */
margin: -10px 0;
}
QSlider::sub-page:horizontal {
background: #999;
margin: 5px;
border-radius: 5px;
}
QSlider::add-page:horizontal {
background: #666;
margin: 5px;
border-radius: 5px;
}
Groove 的默认高度和 QSlider content rectangle 的高度一样,给它一个高度值就可以让他有固定的高度了,把 groove 的 height 去掉试试。
Handle 的默认高度和 groove content rectangle 的高度一样,为了让起显示超出 groove,需要设置 margin 为负值,如果这个负值太小,显示超出 QSlider 的部分将看不到。Handle 的 subcontrol-position 没有作用,因为 handle 不是固定在一个地方的,而是根据 QSlider 的值动态计算显示的位置。
QProgressBar
QProgressBar 的 subcontrol 有 ::chunk。

对于 QProgressBar 的 QSS,大多数都是想把 chunk 定义为圆角矩形的样子,但是当它的 value 比较小时,chunk 的圆角会变成直角,即使使用图片都不行,效果很不理想,所以如果要修改 QProgressBar 的外观的话,推荐继承 QProgressBar 自己绘制或者使用 QStyle。
QGroupBox
QGroupBox 的 subcontrol 有 ::title 和 ::indicator。
- title 相对于 QGroupBox
- indicator 的 subcontrol-origin 和 subcontrol-position 自定义无效

下面 QSS 的效果如图:
QGroupBox {
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #E0E0E0, stop: 1 #EEEEEE);
border: 2px solid gray;
border-radius: 5px;
margin-top: 10px; /* leave space at the top for the title */
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top center; /* position at the top center */
padding: 2px 3px;
color: white;
margin-top: 2px;
background-color: gray;
border-radius: 3px;
spacing: 5px;
}
QGroupBox::indicator {
width: 13px;
height: 13px;
border: 1px solid black;
background: white;
}
QGroupBox::indicator:checked {
background: yellow;
}
QTableView
QTableView 相关的 subcontrol 有 QTableView 的 ::item,QHeaderView 的 ::section 和 左上角的 QTableCornerButton 的 ::section。
QTableView 的 QSS 对于 QTableWidget 也是生效的。

下面 QSS 的效果如图:
- QTableView, QHeaderView, QTableView::item {
- background: white;
- }
- QTableView::item:alternate {
- background: rgb(209, 231, 254);
- }
- QTableView::item:selected { /*被选中的index*/
- color: black;
- background: qlineargradient(
- x1: 0, y1: 0, x2: 0, y2: 1,
- stop: 0 #FAFBFE,
- stop: 1 #DCDEF1);
- }
- QHeaderView::section:horizontal, QTableCornerButton::section {
- background-color: qlineargradient(spread:reflect,
- x1:0, y1:0, x2:0, y2:1,
- stop:0 rgba(255, 255, 255, 255),
- stop:1 rgba(164, 164, 164, 255));
- border: 1px solid rgb(153, 153, 153);
- border-width: 0 1px 1px 0;
- }
- QHeaderView::section:vertical {
- background: #DDD;
- border: 1px solid rgb(153, 153, 153);
- border-width: 0 1px 1px 0;
- }
QTreeView
QTreeView 相关的 subcontrol 有 QHeaderView::section(和上面 QTableView 的 header view 的 QSS 一样,其实就是一个东西),QTreeView::item 和 QTreeView::branch。
QTreeView 的 QSS 对于 QTreeWidget 也是生效的。

QTreeView 的 subcontrol 并不多,但是 branch 有很多种不同的状态,关键就是理解这些状态,不同状态时使用不同的背景,下面 QSS 的效果如图:
QHeaderView::section:horizontal {
background-color: qlineargradient(spread:reflect,
x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 255, 255, 255),
stop:1 rgba(164, 164, 164, 255));
border: 1px solid rgb(153, 153, 153);
border-width: 0 1px 1px 0;
}
QTreeView::item {
border-bottom: 1px solid lightgray;
selection-color: black;
}
QTreeView::item:selected { /*被选中的index*/
background: qlineargradient(
x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #FAFBFE,
stop: 1 #DCDEF1);
}
QTreeView::branch {
background: yellow;
}
QTreeView::branch:has-siblings:!adjoins-item {
background: cyan;
}
QTreeView::branch:has-siblings:adjoins-item {
background: red;
}
QTreeView::branch:!has-children:!has-siblings:adjoins-item {
background: blue;
}
QTreeView::branch:closed:has-children:has-siblings {
background: black;
}
QTreeView::branch:has-children:!has-siblings:closed {
background: gray;
}
QTreeView::branch:open:has-children:has-siblings {
background: magenta;
}
QTreeView::branch:open:has-children:!has-siblings {
background: green;
}
上面的 QSS 是不实用的,直接这么用到项目里,肯定被咔嚓,但是把各种状态的 branch 标记为不同的颜色,这样在做设计的时候就能很容易的分辨出各种 branch,然后根据需求设计出不同的图片放到对应的 branch。其实 Qt 的帮助文档里对 QTreeView 的 QSS 已经有一个很完善的例子,在 QtCreator 的帮助里搜索 style sheets examples,定位到 Customizing QTreeView 就可以看到这个例子了,开发的时候,照葫芦画瓢的做就可以了:
QTabWidget
QTabWidget 相关的 subcontrol 有:
- QTabWidget::pane
- QTabWidget::tab-bar
- QTabBar::tab
- QTabBar::close-button
- QTabBar::tear
- QTabBar::scroller
- QTabBar QToolButton::left-arrow
- QTabBar QToolButton::right-arrow

不幸的是,在我看来,QTabWidget 还有一些 bug,例如:
- QTabBar::close-button 只能定位在 tab 的左边或者右边,不能调整和文本之间的间隔
- Tab 互相遮盖的情况下,如我们的例子的样式,拖动 tab 时某些 tab 会被截断
- QTabBar::scroller 的定位也是个问题,不能自由定位到需要的位置
- QTabBar 和 QTabWidget 不一样宽

上图是 SublimeText 的截图,很多应用中的 tab widget 也差不多是上面这个样,至少这几个 bug 决定了 QTabWidget 使用 QSS 我还做不到这么好,其实用性大受限制,所以就不对 QTabWidget 做太多介绍,只给出一个小例子,展示一下简单的定制 QTabWidget,满足一般的需求,复杂的需要我们自己实现一个 tab widget。
下面 QSS 的效果如图:
QTabWidget::pane { /* The tab widget frame */
border: 2px solid rgb(69, 69, 69);
margin-top: -2px;
}
QTabWidget::tab-bar {
left: 5px; /* move to the right by 5px */
}
QTabBar::tab {
color: gray;
min-width: 40px;
height: 28px;
border-width: 0 18px 0 18px;
border-image: url(:/resources/tab-inactive.png) 0 18 0 18 stretch stretch;
}
QTabBar::tab:selected {
color: #DDD;
height: 28px;
border-width: 0 18px 0 18px;
border-image: url(:/resources/tab-active.png) 0 18 0 18 stretch stretch;
}
QTabBar::tab:!first {
margin-left: -20px;
}
QTabBar::tab:hover {
color: #DDD;
}
| tab-active.png | tab-inactive.png |
|---|---|
![]() |
![]() |
QScrollBar
QScrollBar 相关的 subcontrol 挺多的,仔细观察的话,有点像 QSpinBox 和 QSlider 的合体:
- ::sub-line, ::add-line
- ::sub-page, ::add-page
- ::up-arrow, ::down-arrow
- ::left-arrow, ::right-arrow

很多时候并不是每一个 subcontrol 都需要处理的,例如下面的 QSS 中,arrow 没有处理,直接绘制在 ::sub-line 和 ::add-line 的背景里,也没有处理 ::sub-page 和 ::add-page,效果如图:
QScrollBar:horizontal {
height: 16px;
border-width: 0px 10px 0px 10px;
border-image: url(:/resources/horizontal-track.png) 0 10 0 10 repeat stretch;
margin-left: 6px;
margin-right: 16px;
padding-right: 4px;
}
QScrollBar::handle:horizontal {
min-width: 40px;
border-width: 0 17px 0 17px;
border-image: url(:/resources/horizontal-handle.png) 0 17 0 17 repeat repeat;
}
QScrollBar::sub-line:horizontal {
width: 20px;
height: 17px;
subcontrol-position: left;
subcontrol-origin: margin;
background-image: url(:/resources/horizontal-sub-line.png)
}
QScrollBar::add-line:horizontal {
width: 20px;
height: 17px;
subcontrol-position: right;
subcontrol-origin: border;
background-image: url(:/resources/horizontal-add-line.png)
}
QScrollBar:vertical {
width: 16px;
border-width: 10px 0px 10px 0px;
border-image: url(:/resources/vertical-track.png) 10 0 10 0 repeat repeat;
margin-top: 6px;
margin-bottom: 16px;
padding-bottom: 6px;
}
QScrollBar::handle:vertical {
min-height: 40px;
border-width: 17px 0px 17px 0px;
border-image: url(:/resources/vertical-handle.png) 17 0 17 0 repeat repeat;
}
QScrollBar::sub-line:vertical {
width: 17px;
height: 22px;
subcontrol-position: top left;
subcontrol-origin: margin;
background-image: url(:/resources/vertical-sub-line.png)
}
QScrollBar::add-line:vertical {
width: 17px;
height: 22px;
subcontrol-position: bottom;
subcontrol-origin: border;
background-image: url(:/resources/vertical-add-line.png)
}
QTableView 的 QSS 这里没有给出,请参考上面 QTableView 的 subcontrol 的内容。
QScrollBar 使用的背景图:

有 subcontrol 的常用 widget 基本上介绍完了,相关的 QSS 还有更多的细节都可以在 Qt 的帮助文档里找到。
https://blog.csdn.net/hechao3225/article/details/53914390
QSS独门秘籍:subcontrol的更多相关文章
- 小齐读者拿到快手、百度、网易等 offer 的独门秘籍!
小齐说: 这篇文章来自读者冰红茶,他刚结束了秋招,拿了很多家 offer. 和他聊完之后,我觉得他的备考思路也完全适用于美国的面试,只是分值要调整一下,但是具体每一块的内容,地球村通用.所以分享给大家 ...
- c# winform编程之多线程ui界面资源修改总结篇
单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么 ...
- WinForm/Silverlight多线程编程中如何更新UI控件的值
单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么 ...
- 【转】OpenStack和Docker、ServerLess能不能决定云计算胜负吗?
还记得在十多年前,SaaS鼻祖SalesForce喊出的口号『No Software』吗?SalesForce在这个口号声中开创了SaaS行业,并成为当今市值460亿美元的SaaS之王.今天谈谈『No ...
- 对于spark以及hadoop的几个疑问(转)
Hadoop是啥?spark是啥? spark能完全取代Hadoop吗? Hadoop和Spark属于哪种计算计算模型(实时计算.离线计算)? 学习Hadoop和spark,哪门语言好? 哪里能找到比 ...
- [Spring入门学习笔记][Spring的AOP原理]
AOP是什么? 面向切面编程 软件工程有一个基本原则叫做“关注点分离”(Concern Separation),通俗的理解就是不同的问题交给不同的部分去解决,每部分专注于解决自己的问题.这年头互联网也 ...
- 正本清源区块链——Caoz
正本清源区块链 说明:以下内容整理自Caoz的<正本清源区块链>,如有不妥,请联系我修改或删除. 简介 不讨论炒币!不讨论炒币!不讨论炒币! 本课程内容分为两部分: 第一部分,烧脑篇,介绍 ...
- 《15个提高Google搜索的技巧》
为了得到更加「多元化」的搜索结果,虽然 Google 目前访问起来并不是那么方便,但是仍然有很多人把它作为常用搜索引擎在使用. 其实除了最简单的关键词搜索之外,搜索引擎还提供了很多精细化的搜索功能,如 ...
- Java程序算法设计视频分享,需要的来
每年都会有人说,IT行业饱和了,根本就找不到工作,其实,我想说的是,不是工作难找,而是你自己不够好! 前几天看到一CEO在微博上吐槽: 前几天招一算法工程师我们给了8万月薪*14+奖金,人家去阿里拿5 ...
随机推荐
- Linux 搭建git 自己拉取本地 git pull,其他地方的git仓库拉取代码
Linux 下建立 Git 与 GitHub 的连接 Git 是一款开源的分布式版本控制系统,而 GitHub 是依托 Git 的代码托管平台. GitHub 利用 Git 极其强大的克隆和分支功能, ...
- MySQL技术内幕读书笔记(二)——InnoDB存储引擎
目录 InnoDB存储引擎 InnoDB存储架构 Checkpoint技术 Master Thread 工作方式 InnoDB关键特性(放一下,感觉看后面,再看总结吧) InnoDB存储引擎 Inno ...
- react学习笔记2之正确使用状态
//状态不要直接修改,比如: // 错的,这样写不会重新渲染组件 this.state.comment = 'Hello'; //修改状态正确的方式 this.setState({comment:'H ...
- pilicat-dfs 霹雳猫-分布式文件系统
pilicat-dfs 霹雳猫-分布式文件系统 一种可以将网站图片或上传的文件,进行分布式存放的服务,可自动复制到多台物理机器,可满足高可用和负载均衡 已编译好的程序包 http://git.osch ...
- 一次xxoo提权
数据库root权限.然并卵. 看了一下phpinfo得知是mysql 5.0 的 然后想要通过udf之类的提权一波,结果一执行sql语句就被狗拦截了. 然而数据库这条路是GG了 OS 名称: Micr ...
- Wifi OKC 验证
OKC(Opportunistic Key Caching) OKC,也叫OPC(Opportunistic PMK Caching),是微软定义的一套标准,并不在802.11标准中.不过多数厂商都支 ...
- [转] tomcat 7/8 启动非常慢的解决方法
在日志中发现启动慢的地方: -- ::] INFO o.s.c.s.DefaultLifecycleProcessor - Starting beans -- ::] INFO o.s.web.con ...
- Cannot create a session after the response has been committed
有时候在操作Session时,系统会抛出如下异常 java.lang.IllegalStateException: Cannot create a session after the response ...
- python程序打包成.exe
安装pyinstaller 方法一:使用pip install pyinstaller 方法二:如果是下载github上的包之后手动安装 包下载 亲测可用:Pyinstaller下载地址,GitHub ...
- linux执行python命令后没有反应,不打印日志信息
实际的python执行的软连接是路径是/opt/python2.7/bin/python,而设置的软连接错误如下: 修改该连接,是python指向/opt/python2.7/bin/python, ...

