每个 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. php标准库中QplQueue队列如何使用?

    php标准库中QplQueue队列如何使用? 一.总结 1.new对象,然后通过enqueue方法和dequeue方法使用. 二.php标准库中QplQueue队列如何使用? 队列这种数据结构更简单, ...

  2. .net core 下监控Sql的执行语句

    原文:.net core 下监控Sql的执行语句 最近在编写.net core程序,因为数据库从Sql Server 切换到 MySql的原因,无法直接查看sql的具体语句,随着业务量的剧增,痛苦也与 ...

  3. 《写给大忙人看的Java SE 8》——Java8新特性总结

    阅读目录 接口中的默认方法和静态方法 函数式接口和Lambda表达式 Stream API 新的日期和时间 API 杂项改进 参考资料 回到顶部 接口中的默认方法和静态方法 先考虑一个问题,如何向Ja ...

  4. 【SSH2(理论+实践)】--图说Struts2的执行

        前几篇文章讨论了有关Struts2的核心机制及一些基础,但同一时候也遗留下了非常多问题.这些问题主要是针对Struts2的一些使用技巧的,该篇文章将会针对Struts2的使用技巧进行讨论, ...

  5. 解决离线Could not parse configuration:hibernate.cfg.xml错误

    离线使用hibernate tool 生成反向工程,在配置 配置文件完,生成配置文件后,会报出org.hibernate.HibernateException: Could not parse con ...

  6. 《iOS开发全然上手——使用iOS 7和Xcode 5开发移动与平板应用》之Objective-C新手训练营

    编写Hello World应用程序通常被觉得,是学习不论什么编程语言的第一步.在这一章,你将创建iOS版的Hello World应用程序作为起步,高速了解Xcode这个开发iOS应用程序的主要工具. ...

  7. BFKit:对常用 UIButton,UIColor,UIDevice,UIFont ,UIImage 等开发类进行了扩展

    BFKit对常用于开发的类进行了扩展,整合了多个常用的控件和开发所需要的功能,是一个通用性的类库.集成后可以帮助更快的App开发.有兴趣的同学可以看看哦. http://code4app.com/io ...

  8. 检索08- SQL语句中的go与use用法

    GO 1. 作用:向 SQL Server 实用工具发出一批 Transact-SQL 语句结束的信号.2. 语法:一批 Transact-SQL 语句 GO 如 Select 1 Select 2 ...

  9. 使用Opencv中均值漂移meanShift跟踪移动目标

    Mean Shift均值漂移算法是无参密度估计理论的一种,无参密度估计不需要事先知道对象的任何先验知识,完全依靠训练数据进行估计,并且可以用于任意形状的密度估计,在某一连续点处的密度函数值可由该点邻域 ...

  10. TensorFlow 学习(四)—— computation graph

    TensorFlow 的计算需要事先定义一个 computation graph(计算图),该图是一个抽象的结构,只有在评估(evaluate)时,才有数值解,这点和 numpy 不同.这张图由一组节 ...