浅析 Qt 布局系统

Qt 布局系统介绍

布局系统

做为一名 iOS 开发人员, 见证着 iOS 布局系统的不断完善, 从绝对布局, Autoresizing 到 Autolayout. 使得开发人员的工做效率愈来愈高, 项目界面的可读性和易维护性愈来愈强. 现在 IDE 中的可视化界面工具已经很是强大, 许多网友"戏称" iOS 开发者为"UI 拖拽师", 可见, iOS 开发中界面布局系统的高效. 因此, 优秀的布局系统的使命在于让开发者花更少的时间来完成更易维护的界面.浏览器

一样的, 在 Qt 中, 系统提供了强大的排版机制来为窗口中的视图进行布局排版, 通过了对 Qt 布局一个初步的探索, 不得不对 Qt 布局系统的简洁高效而又功能强大表示赞叹.函数

布局系统的功能

在 Qt 中, 布局系统能够完成工具

  • 定位子控件
  • 得知窗体默认大小
  • 得知窗体最小大小
  • 窗体大小变化时进行布局排版
  • 内容改变(字体大小文本等, 隐藏或显示, 移除)时进行布局排版

布局系统的结构

Qt 提供了 QLayout 类及其子类来为界面进行排版布局. 结构以下图:布局

布局系统结构图

QLayout 是布局系统中的抽象基类, 继承自 QObject 和 QLayoutItem, 其中四个子类分别为学习

  • QBoxLayout(箱式布局)
  • QFormLayout(表单布局)
  • QGridLayout(网格布局)
  • QStackedLayout(栈布局)

在真实使用场景中, 每每须要经过多种布局的相结合来完成界面的设计, 接下来将分别介绍四中布局.字体

QBoxLayout 箱式布局

箱式布局提供了两个子类分别处理水平(QHBoxLayout)和垂直(QVBoxLayout)两个方向的排版, 可使视图排成一行或者一列来显示. 简单说, 就是可让控件进行排排站, 好比在咱们的 AlphaBox 中, 顶部的头像, 姓名, 和刷新按钮排成了一排, 这就是水平箱式布局:spa

什么叫排排站

你觉得我要讲一下这个东西如何实现? NO, 我恰恰要以垂直箱式布局为例, 用一个最简单的例子来介绍箱式布局的使用, 首先建立一个基于 QWidget 的界面, 添加咱们须要使用的头文件:翻译

#include <QVBoxLayout>
#include <QPushButton>
复制代码

并在构造函数中添加以下代码设计

// 添加两个按钮
    QPushButton *okBtn  = new QPushButton;
    okBtn ->setText(tr("我在上面, 我最牛"));
    QPushButton *celBtn = new QPushButton;
    celBtn->setText(tr("我在下面, 我不服"));

    // 建立一个垂直箱式布局, 将两个按钮扔进去
    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(okBtn);
    layout->addWidget(celBtn);

    // 设置界面的布局为垂直箱式布局
    setLayout(layout);
复制代码

运行看一下效果, 什么? 这就能够运行了? 坐标呢? 尺寸呢? 是的, 没看错...点击运行:3d

最简单的箱式布局

两个按钮已经一上一下, 乖乖的在垂直方向本身站好了位置, 就是这么强大, 就是这么省心.

QFormLayout 表单布局

强大的 AlphaBox 是很外向的, 能够很轻松的将你的资料分享给其余用户, 当咱们分享的时候, 会有这样一个界面:

在 AlphaBox 中共享资料

看到这个界面, 聪明的你可能会说, 这很简单啊, 好几个水平箱式布局就能够实现, 但是, 更聪明的 Qt 提供了更高效的方式帮助你完成这样一个界面, 那就是 QFormLayout.

在我所学习 Qt 所使用的书籍中, 将 QFormLayout 翻译为窗体布局, 我我的认为, 将其翻译为表单布局更为贴切, 由于 QFormLayout 的强大之处正是可使用最快的速度完成一个用户输入的表单界面的搭建.

那么, 让咱们揭开 AlphaBox 的神秘面纱, 看看这样一个界面是怎么实现的.

首先, 拖拽一个 Form Layout 到 Widget 中.

添加表单布局

双击以后便可为表单增长一行.

为表单增长一行

相信你们看到这张图时, 就已经能理解到表单布局是如何使用的, 提供了标签做为用户输入内容的指引, 提供字段类型做为用户输入的控件, 做为 iOS 开发者, 深知这样一个界面的搭建所须要的繁杂的工做量. 当我第一次打开这个界面时, 被这样建立界面的方式所惊呆了.

  1. 按照图中, 建立表单的第一行, 共享给哪一个用户的输入框, 能够为输入框填写占位文字.
  2. 双击 Form Layout 建立字段类型为 QComboBox (多选框)的一行. 填写容许的权限内容.
  3. 设置整个 Widget 布局为垂直箱式布局
  4. 在 Form Layout 下拖拽过去一个 Horizontal Layout(水平箱式布局)
  5. 在箱式布局中添加 Horizontal Spacer (水平占位) 后拖拽两个 Push Button 完成界面布局

共享界面的布局

快不快? 快不快! 快不快!!!

一样的, 若是是使用纯代码表单布局的话可使用addRow()的方法来添加一行.

QGridLayout 网格布局

强大的 AlphaBox 是这样的

事实上, 强大的 AlphaBox 是这样的, 咱们能够共享给多个用户, 并且, 下方会有一个列表, 展现共享的用户以及权限列表. 这时, 表单布局就没办法知足咱们, 只好另求新欢 QGridLayout - 网格布局.

网格布局顾名思义, 能够将界面分割成行列来进行布局管理, 在每一个单元格中来摆放控件. 因此 AlphaBox 分享的界面使用了一个 两行三列 的网格布局来实现的.

QGridLayout - 网格布局

固然, 更更复杂的界面, 用 Qt 布局的效率也是很是高的, 我作了一个外链分享的布局 Demo, 能够将内部资料生成一个下载连接共享给任何人去下载.

外链分享界面

这个界面中, 我在Tab以内使用了网格布局, 布局如图:

外链分享界面布局

从图中能够看出, 网格布局像是在操做一个 Excel 同样简单, 布局单元格, 合并单元格, 等等.

在这个界面中, 更灵活的使用了 QLayout 的属性来完成了界面布局排版.

一样的, 在代码中, 可使用以下等的 Api 来为网格视图添加一个从几行几列开始占据几行几列的控件:

void addWidget(QWidget *, int row, int column, int rowSpan, int columnSpan) 复制代码

QStackedLayout 栈布局

如在 AlphaBox 中, 咱们能够经过云端文件浏览器直接查看和操做云端文件, 在加载的过程当中, 会有一个转菊花的界面.

在转菊花的 AlphaBox

加载失败时的错误提示:

菊花转失败了的 AlphaBox

以及加载成功时:

一般状况下咱们能看见的 AlphaBox

一般应用的界面会根据不一样的状态有不一样的内容, 这时就可使用 QStackedLayout 栈布局, 栈布局提供了一个页面的栈, 每一个页面有彻底独立的界面布局. 能够很是清晰的对不一样状态下的界面进行布局管理.

在 Qt 的可视化布局工具中, 经过 Stacked Widget 来完成界面的栈布局

Stacked Widget

经过右键来进行页面的插入移除和排序等操做.

布局相关属性

控件大小

对于控件大小, 最重要的两个属性是 sizeHintminimumSizeHint , 这是 QWidget 的属性, 是只读属性. 其中, sizeHint 属性为控件的建议大小, 对于不一样的控件, 有不一样的建议大小, 同理 minimumSizeHint 为建议的最小大小. 知道了这两个属性才能够理解布局中控件的大小是如何控制的. 若是手动设置了最小尺寸的话(minimumSize), minimumSizeHint 是会被忽略的.

大小策略

大小策略属性 sizePolicy 也是 QWidget 类的属性, 这个属性在水平和垂直两个方向分别起做用, 控制着控件大小变化的策略.

在可视化工具中能够直观的看到几种大小策略, 以垂直为例如图:

大小策略

  • QSizePolicy::Fixed 只能使用 sizeHint 的大小, 任何操做都不会改变控件大小
  • QSizePolicy::Minimum sizeHint 为最小大小, 控件能够被拉伸
  • QSizePolicy::Maximum sizeHint 为最大大小, 控件能够被压缩
  • QSizePolicy::Preferred sizeHint 为建议大小, 控件既能够被压缩也能够被拉伸
  • QSizePolicy::MinimumExpanding sizeHint 为最小大小, 不能被压缩, 被拉伸的优先级更高
  • QSizePolicy::Expanding sizeHint 为建议大小, 能够被压缩, 被拉伸的优先级更高
  • QSizePolicy::Ignored sizeHint 的值将会被忽略

在网上或者书中, 关于这些策略的说明会有不少, 但是若是不是真的本身尝试一下, 很难很好的理解在复杂布局的状况下, 大小策略是如何控制布局的, 尤为是 MinimumExpanding, Expanding, Ignored 这三种.

对于优先级的概念你们确定不会陌生, 这里我认为优先级来解释这些策略是更清晰易懂的.

关于拉伸 Expanding , MinimumExpanding 优先级相同, 同时要比 PreferredIgnored 拉伸优先级更高, PreferredIgnored 相同.

换句话说, 若是两个控件在一个水平箱式布局中管理, 其中一个水平大小策略为 Preferred 另外一个为 Expanding 或者 MinimumExpanding 若是水平拉伸窗体, 则 Preferred的控件大小不会改变, Expanding 或者是 MinimumExpanding 会被拉伸.

同理, 若是两个控件水平大小策略一个为Expanding, 一个是MinimumExpanding, 这时拉伸窗体, 则两个控件均会拉伸.

多说一句, 若是两个控件都为 Fixed 没法拉伸时, 控件间的间隙会被拉伸.

关于压缩 若是达到了minimumSizeHint是不会被继续压缩了, 可是Ignored是会忽略 sizeHintminimumSizeHint 的属性的, 因此是会继续被压缩的.

伸缩性

在 QLayout 中提供了一个和控件大小策略相关的属性, layoutStretch 布局伸缩性, 这个值是一个比例, 在可视化工具中能够更直观的看到这个值的设置, 若是在布局中有三个控件, 则是三个控件的占比, 用逗号分隔, 如: 1, 1, 1 .

伸缩性就好理解一些了, 可是要注意的是, 只有会被压缩或者拉伸的控件才会受该属性值影响(如 Fixed 是不会受该属性影响)

还有一点很是重要的是设置了伸缩性的比值(若是都为0, 则表示不设置) 刚刚提到的大小策略的优先级将会被忽略, 仍是刚刚的例子, 若是两个控件在一个水平箱式布局中管理, 其中一个水平大小策略为 Preferred 另外一个为 Expanding, 设置水平箱式布局的 layoutStretch2, 1 则拉伸时, 并不会像刚刚所说, 只有 Expanding 的控件会被拉伸, 而是都会被拉伸, 按照一个 2 : 1 的拉伸比例拉伸.

窗体大小约束策略

最后想介绍一下 QLayout 的 layoutSizeConstraint 属性, 用来约束窗体大小, 只影响窗体, 因此该属性只对最顶级的 QLayout 起做用.

layoutSizeConstraint

关于这几个属性一样的, 简单的介绍网上和书上会有不少, 若是不尝试一下, 浅显的字面意思没法理解这几个属性的做用. 根据个人尝试, 总结以下:

  • QLayout::SetDefaultConstraint 窗体最小值被设置为 minimumSize 值没法再缩小, 若是 QLayout 内控件有更大的minimumSize, 则会取更大的minimumSize.
  • QLayout::SetNoConstraint 窗体没有约束策略
  • QLayout::SetFixedSize,窗体大小被设定为 sizeHint 的大小,没法改变
  • QLayout::SetMinimumSize 窗体最小为 minimumSize 没法再缩小, 若是 QLayout 内控件有更小的minimumSize, 则会取更小的minimumSize., 总结就是的话, 和 Default 不一样的地方就是尽量的小.
  • QLayout::SetMaxmumSize 同理, 窗体最大值为 maxmumSize , 没法再放大
  • QLayout::SetMinAndMaxSize 窗体最小为 minimumSize 没法再缩小, 窗体最大值为 maxmumSize , 没法再放大

其余属性

关于表单布局和网格布局还有其余的属性约束单元格的一些策略, 如 layoutFieldGrowthPolicy 控件的变化方式策略等等有兴趣能够查看官方文档, 更多的属性间隙, 间隔, 对其方式等等都比较好理解了, 在此也不赘述了.

官方文档

Qt 官方文档点这里

参考

<< Qt Creator 快速入门>> 第三版, 霍亚飞著

相关文章
相关标签/搜索