每个 Widget 所在的范围都是一个矩形区域(无规则窗口也是一个矩形,只是有的地方是透明的,看上去不是一个矩形),像是一个盒子一样。QSS 支持盒子模型(Box Model),和 CSS 的盒子模型是一样的,由 4 个部分组成:content, padding, border, margin,也就是说,Widget 的矩形区域,用这 4 个矩形表示

  • content: 绘制内容的矩形区域(如绘制文本、图片),Qt 自带的 widget 都是在 content 区里绘制内容,这只是一个约定,只要你愿意,也可以在绘制到 padding, border, margin 区
  • padding: 内容区和边框之间的间隔
  • border: 边框,可视化的显示一个 widget 的逻辑范围,而不一定是 widget 所占矩形区域的实际大小
  • margin: 想像 widget 的矩形区域有一个隐形的边框,margin 就是 border 和这个隐形边框之间的间隔

QWidget 的 content, padding, border, margin 的矩形区域都是一样大的,也就是说,margin, border, padding 的值为 0,content 的矩形和 QWidget 的矩形一样大,但是 QPushButton 默认的 margin, border, padding 的值不为 0(可以试试 setFlat(true) 后再看看这几个值是什么)。

Margin,Border,Padding 都分为 4 个部分:上、右、下、左,它们的值可以不同:

padding 的语法

padding: 2px 3px 4px 5px,表示:

  • padding-top 为 2px
  • padding-right 为 3px
  • padding-bottom 为 4px
  • padding-left 为 5px

padding: 2px 4px 表示:

  • padding-top 和 padding-bottom 为 2px
  • padding-right 和 padding-left 为 4px

padding: 2px 表示:

  • padding-top、padding-right、padding-bottom、padding-left 都为 2px

margin 的语法和 padding 的一样,border 除了分成 4 个部分外,还有颜色,圆角等。

也许你也有和我一样的疑问,为什么上面表示的顺序不是从左开始,而是从上开始?对我来说,从左开始更习
惯一些,但是 CSS 和 QSS 里就是这么规定的,没办法,既然不能反抗,那么就享受吧!

padding 是什么

可能你还是不明白 margin, padding 具体是什么,下面用例子具体的的介绍它们。在 Qt Designer 里用 QGridLayout 布局,拖放一个 QLabel 到窗口上,让其占据整个窗口,用下面的 QSS 把 QLabel 的 margin, border, padding 都设置为 50px:

1
2
3
4
5
6
QLabel {
margin: 50px;
border: 50px solid rgb(74, 74, 74);
padding: 50px;
background: white;
}

在 Qt Designer 里选中 QLabel:

  • 8 个蓝色小方块内就是 QLabel,外面是 parent widget 的,不属于 QLabel
  • 标记为 margin 的部分是 margin,为 50px
  • 标记为 padding 的部分是 padding,为 50px
  • 用工具测量一下,得到 border 的宽也是 50px
  • 小虚线方框内是 content rectangle,QLabel 就是在它里面绘制文本,图片等,不会绘制到 padding, border, margin 等上面(如果你自己想继承 QLabel 然后这么绘制,当然没问题)
  • 当拖动修改窗口的大小后,QLabel 的大小随着改变了,但是 margin, border(宽), padding 的大小都不会变,变化的只有 content rectangle
  • Margin 是不可见的,不绘制任何东西
  • Padding 是不可见的,但是 QLabel 的背景会绘制到它里面
  • Border 是可见的,在背景上面绘制 border(如果 border 是半透明的时候可以看到和背景的融合效果),也就是说,背景会绘制到 padding, border, content 3 个部分上
  • Content 是可见的,在背景上绘制 QLabel 的内容:文本,图片

计算 border 和 content 的矩形

Padding 和 margin 都是不可见的,用它们来控制间隔,让绘制的效果更好,只有 border 和 content 是可见的。Border 和 content 要绘制在它们自己的矩形区域内,那么这些矩形怎么计算呢?Content 的矩形一定要在 border 的矩形内吗?

设定

  • Widget 的矩形为 widgetRect,其实就是 (0, 0, width, height)
  • Border 的矩形为 borderRect
  • Content 的矩形为 contentRect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 计算 border 的矩形
borderRect.x = marginLeft
borderRect.y = marginTop
borderRect.width = widgetRect.width - marginLeft - marginRight
borderRect.height = widgetRect.height - marginTop - marginBottom
 
// 计算 content 的矩形
contentRect.x = marginLeft + borderLeft + paddingLeft
contentRect.y = marginTop + borderTop + paddingTop
contentRect.width = widgetRect.width -
marginLeft - borderLeft - paddingLeft -
paddingRight - borderRight - marginRight;
contentRect.height = widgetRect.height -
marginTop - borderTop - paddingTop -
paddingBottom - borderBottom - marginBottom;

为了简单起见,下面的 margin 指的是它对应的 4 个值,而且都一样大,padding 也一样:

  • Margin 大于 0 时,borderRect 在 widgetRect 内
  • Margin 小于 0 时,borderRect 包含 widgetRect
  • Padding 大于 0 时,contentRect 在 borderRect 内
  • Padding 小于 0 时,contentRect 和 borderRect 中绘制 border 的区域相交,甚至包含 borderRect

Qt 绘制自带的 Widget 时,先绘制 border,然后才绘制 content 的内容,所以,padding 小于 0 时,可以看到 content 绘制到了 border 上,在 Border-Image 一节里,就会使用这个特点,使得实现的效果更好。

需要注意的是:

  • QWidget::contentsRect() 和上面的 contentRect 一样
  • QWidget::contentsMargins() 不是 margin,而是 margin + border + padding 的和
  • QWidget::size() 返回的是 margin + border + padding + content 的和

可视化盒子模型

下面的程序,可以直观地验证盒子模型的理论,有助于加深理解,界面虽然俗气了点,但效果却是不凡,正所谓:大俗即大雅。顶级窗口的 content margin 和 spacing 设置为 0,有 4 个 QLabel,它们只有 border 的颜色不一样,还有一个 QPushButton,按下 QPushButton 时输出左上角 flagLabel 的 size(), contentsRect() 和 contentsMargins()

1
2
3
4
5
6
7
8
9
10
11
MainWidget::MainWidget(QWidget *parent) :
QWidget(parent), ui(new Ui::MainWidget) {
ui->setupUi(this);
 
// 为了简化代码,使用了 C++11 才支持的 Lambda 语法的信号槽连接
connect(ui->dumpButton, &QPushButton::clicked, [this]() {
qDebug() << "Label size: " << ui->flagLabel->size();
qDebug() << "Contents rect: " << ui->flagLabel->contentsRect();
qDebug() << "Contents margins: " << ui->flagLabel->contentsMargins();
});
}
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
28
29
30
31
32
33
QLabel {
margin: 0px;
padding: 0px;
border-width: 30px;
border-style: solid;
background: white;
min-width: 140px; /* 设置 content rect 的最小 width,不是 widget 的 width() */
min-height: 140px; /* 设置 content rect 的最小 heightd,不是 widget 的 height()*/
}
 
#flagLabel {
border-color: qlineargradient(
spread:pad, x1:0,
y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 255),
stop:0.33 rgba(0, 0, 0, 255),
stop:0.34 rgba(255, 30, 30, 255),
stop:0.66 rgba(255, 0, 0, 255),
stop:0.67 rgba(255, 255, 0, 255),
stop:1 rgba(255, 255, 0, 255));
}
 
#greenLabel {
border-color: green;
}
 
#blueLabel {
border-color: blue;
}
 
#yellowLabel {
border-color: yellow;
}

初始 QSS 如上,效果为:

点击 Dump 按钮,输出:
Label size: QSize(200, 200)
Contents rect: QRect(30,30 140x140)
Contents margins: QMargins(30, 30, 30, 30)

由于 margin 为 0px,padding 为 0px,所以:

  • label width:0 + 30 + 0 + 140 + 0 + 30 + 0,为 200
  • label height:0 + 30 + 0 + 140 + 0 + 30 + 0,为 200
  • content rect 的 x: 0 + 30 + 0,为 30
  • content rect 的 y: 0 + 30 + 0,为 30
  • content margin:0 + 30,为 30

修改 margin: 20px; padding: 0px;,效果为:

点击 Dump 按钮,输出:
Label size: QSize(240, 240)
Contents rect: QRect(50,50 140x140)
Contents margins: QMargins(50, 50, 50, 50)

由于 margin 为 20px,padding 为 0px,所以

  • label width:20 + 30 + 0 + 140 + 0 + 30 + 20,为 240
  • label height:20 + 30 + 0 + 140 + 0 + 30 + 20,为 240
  • content rect 的 x: 20 + 30 + 0,为 50
  • content rect 的 y: 20 + 30 + 0,为 50
  • content margin:20 + 30 + 0,为 50

左上角 QLabel 的大小为红框括起来的范围,而不是可见的 border 括起来的范围,因为 margin 为 20px。


修改 margin: 0px; padding: -20px;,效果为:

点击 Dump 按钮,输出:
Label size: QSize(160, 160)
Contents rect: QRect(10,10 140x140)
Contents margins: QMargins(10, 10, 10, 10)

由于 margin 为 0px,padding 为 -20px,所以:

  • label width:0 + 30 + -20 + 140 + -20 + 30 + 0,为 160
  • label height:0 + 30 + -20 + 140 + -20 + 30 + 0,为 160
  • content rect 的 x: 0 + 30 + -20,为 10
  • content rect 的 y: 0 + 30 + -20,为 10
  • content margin:0 + 30 + -20,为 10

contentRect 和 borderRect 中绘制 border 的区域相交,所以 QLabel 的文本 Flag 被绘制到了 border 上面。


修改 margin: -10px; padding: -20px;,效果为:

点击 Dump 按钮,输出:
Label size: QSize(140, 140)
Contents rect: QRect(0,0 140x140)
Contents margins: QMargins(0, 0, 0, 0)

由于 margin 为 -10px,padding 为 -20px,所以

  • label width:-10 + 30 + -20 + 140 + -20 + 30 + -10,为 140
  • label height:-10 + 30 + -20 + 140 + -20 + 30 + -10,为 140
  • content rect 的 x: -10 + 30 + -20,为 0
  • content rect 的 y: -10 + 30 + -20,为 0
  • content margin:-10 + 30 + -20,为 0

因为 margin 为 -10px,border-width 为 30px,所以上边的 border 应该从 (-10, -10) 开始绘制,有三分之一给绘制到了 QLabel 的不可见区域,所以从图中看不到,这里我们使用了一个三色的 border,能直观的看到 border 的变化。

QSS 里盒子模型是很关键的,相信大家现在已经明白了,但是,古人云:纸上得来终觉浅,绝知此事要躬行,请自己尝试修改不同的 margin, border, padding, min-width, min-height 等,计算、对比输出的结果,这样才会有更深刻的体会。

http://www.qtdebug.com/qtbook-qss-boxmodel/

QSS 盒子模型的更多相关文章

  1. Qt样式表之盒子模型(以QSS来讲解,而不是CSS)

    说起样式表,不得不提的就是盒子模型了,今天小豆君就来给大家介绍下盒子模型. 上图是一张盒子模型图. 对于一个窗口,其包括四个矩形边框.以中间的边框矩形(border)为基准,在border外侧的是外边 ...

  2. 谈谈一些有趣的CSS题目(二)-- 从条纹边框的实现谈盒子模型

    开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...

  3. JS学习:第二周——NO.3盒子模型

    1.CSS盒子模型包括四个部分组成:设定的宽高+padding+border+margin: 2.JS盒子模型:通过系统提供的属性和方法,来获取当前元素的样式值   JS提供的属性和方法: clien ...

  4. html学习第三天—— 第11章 盒子模型 div

    盒模型--边框(一) 盒子模型的边框就是围绕着内容及补白的线,这条线你可以设置它的粗细.样式和颜色(边框三个属性). 如下面代码为div来设置边框粗细为2px.样式为实心的.颜色为红色的边框: div ...

  5. css 大话盒子模型

    什么是盒子模型? CSS中, Box Model叫盒子模型(或框模型),Box Model规定了元素框处理元素内容(element content).内边距(padding).边框(border) 和 ...

  6. padding标准盒模型和怪异盒子模型

    我们都知道padding是为块级元素设置内边距 但是在使用过程中,我们却会遇到一些问题.padding的标准盒模型和怪异盒模型 padding盒子模型 我们通过demo来讲这个问题,用文字干讲第一没意 ...

  7. CSS系列:CSS中盒子模型

    盒子模型是CSS控制页面时一个很重要的概念.所有页面中的元素都可以看成是一个盒子,占据着一定的页面空间.可以通过调整盒子的边框和距离等参数,来调节盒子的位置和大小. 1. 盒子的内部结构 在CSS中, ...

  8. 深入理解CSS盒子模型

    在CSS中浮动.定位和盒子模型,都是很核心的东西,其中盒子模型是CSS很重要基石之一,感觉还是很有必要把CSS盒子模型相关知识更新一下...... CSS盒子模型<BoxModel>示意图 ...

  9. jQuery CSS操作及jQuery的盒子模型

    jQuery CSS-jQuery CSS方法 jQuery CSS-jQuery盒子模型

随机推荐

  1. 【u031】租用游艇

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 长江游艇俱乐部在长江上设置了n 个游艇出租站1,2,-,n.游客可在这些游艇出租站租用游艇,并在下游的 ...

  2. 谈谈android缓存文件

    ##内部存储 总是可用的 这里的文件默认是只能被你的app所访问的. 当用户卸载你的app的时候,系统会把internal里面的相关文件都清除干净. Internal是在你想确保不被用户与其他app所 ...

  3. hadoop 3.x 配置历史服务器

    修改$HADOOP_HOME/etc/hadoop/mapred-site.xml,加入以下配置(修改主机名为你自己的主机或IP,尽量不要使用中文注释) <!--history address- ...

  4. 关于HierarchyViewer的使用

    在学习ViewGroup和Layout时我们可能会有一个疑问,如果我在Xml布局文件中不放置Layout,直接放TextView等组件的时候,它是用什么方式布局的?还有要学习别人优秀的布局怎么办? H ...

  5. pushbutton成为可点击的图标(实现全透明,不论点击与否都只显示Icon)(也就是一个万能控件)

    需求 需要2个按钮,一个是音乐开关,一个是关闭窗口,此文章关闭pushButton的透明问题(hovered+pressed都不会有背景色和边框的变化) 原理 使窗口完全透明 代码 _pPushBut ...

  6. 学术论文写作的 paper、code 资源

    (机器学习/计算机视觉/深度学习)代码 0. 核心期刊 Best paper awards at - CV NIPS: JMLR COLT & ICML(每年度的官网) 1. Computin ...

  7. react 父组件向子组件传递函数

    这段时间一直在使用react,由于这react是单向数据绑定,总感觉有点不适用,毕竟之前一直都在使用angular,但学习还是要继续,做了一个迭代的项目,都差点忘记要总结一下这个react了,现在可以 ...

  8. IOC介绍及其简单实现

    预备知识: Java反射原理,XML及其解析   IOC:Inversion of Control,控制反转,它最主要反映的是与传统面向对象(OO)编程的不同.通常我们编程实现某种功能都需要几个对象相 ...

  9. Swift 分类 结构体

    感谢原作者:http://www.cocoachina.com/newbie/basic/2014/0612/8780.html 类和结构体是人们构建代码所用的一种通用且灵活的构造体.为了在类和结构体 ...

  10. python reversed

    reversed()函数是返回序列seq的反向访问的迭代子.参数可以是列表,元组,字符串,不改变原对象. 例题: 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事 ...