求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
Qt 编程指南
第1章 Qt 开发环境
1.1 Qt 介绍
1.2 Qt 下载
1.3 Qt 在 Windows 下安装
1.4 Qt 在 Linux 下安装
1.5 认识开发工具
1.6 常见的名词术语
第2章 从Hello World开始
2.1 Hello World
2.2 Hello Qt
2.3 Hello Designer
2.4 Hello Creator
2.5 Qt程序调试
2.6 Qt帮助文档
第3章 字符串和字符编码
3.1 字符编码方式
3.2 Qt 程序字符编码
3.3 使用 QString
3.4 使用 QByteArray
第4章 信号和槽函数
4.1 元对象系统
4.2 使用原有的信号和槽
4.3 自定义信号和槽
4.4 系统属性
4.5 扩展阅读:ui_*.h代码
4.6 扩展阅读:moc_*.cpp代码
第5章 简单控件的使用
5.1 按钮类的控件
5.2 单行编辑控件
5.3 丰富文本编辑控件
5.4 其他输入控件
5.5 显示类的控件
5.6 Qt 资源文件
第6章 控件布局
6.1 传统窗口调整技术
6.2 水平和垂直布局器
6.3 网格布局器
6.4 表单布局器
 

 
目录
水平和垂直布局器
10 次浏览
1次  

关于 Qt 布局管理,有专门的帮助文档页面 Layout Management。本章的主要内容就是介绍布局管理的知识,Qt 设计师里面不仅有布局器的控件可以拖动使用,还可以在窗体里面选择控件,然后点击设计师上面的工具按钮自动添加布局器。本节首先大致介绍一下 Qt 设计师里面关于布局器的操作界面,主要介绍两个基本的水平布局器 QHBoxLayout 和垂直布局器 QVBoxLayout, 将控件和布局器由小到大搭成一个完整的界面。

1. 布局器概览

我们以下图的 Qt 设计师界面来说明布局功能,QtCreator 设计模式的布局功能与 Qt 设计师是一样的。

designer

在设计师左边列表,可以看到 Layouts 栏目里有四个布局器:

◆ 垂直布局器 QVBoxLayout:将内部的控件按照垂直方向排布,一行一个。

◆ 水平布局器 QHBoxLayout:将内部的控件按照水平方向排布,一列一个。

◆ 网格布局器 QGridLayout:按照多行、多列的网格排布内部控件,单个控件可以占一个格子或者占据连续多个格子。

◆ 表单布局器 QFormLayout:Qt 设计师里把这个布局器称为窗体布局器,窗体布局器这个叫法不准。这个布局器就是对应网页设计的表单,通常用于接收用户输入。该布局器就如它的图标一样,就是固定的两列控 件,第一列通常是标签,第二列是输入控件或含有输入控件的布局器。

◆ Qt 另外还有一个堆栈布局器 QStackedLayout,通常用于容纳多个子窗口布局,每次只显示其中一个。这个布局器隐含在堆栈部件 QStackedWidget 内部,一般直接用 QStackedWidget 就行了,不需要专门设置堆栈布局器。堆栈部件和堆栈布局器留到后面容器类控件的 章节讲解。

与布局紧密关联的是两个空白条(或叫弹簧条):Horizontal Spacer 水平空白条和 Vertical Spacer 垂直空白条,空白条的作用就是填充无用的空隙,如果不希望看到控件拉伸后变丑,就可以塞一个空白条到布局器里面,布局器通常会优先拉伸空白条。两种空白条的类名都是 QSpacerItem,两种空白条只是默认的拉伸方向不一样。

对界面进行布局有两种方式,第一种方式是预先设计好整体布局,先拖入布局器,后拖入功能控件到布局器里面,这种方式不太方便,因为脑海里得提前做好布局规划。第二 种方式才是是最常用的,先把所有功能控件拖入主界面,然后根据设置好的功能控件来决定如何进行布局。Qt 的布局器中既可以添加普通功能控件,也可以添加其他布局器,所以布局器的使用是非常灵活的。界面里的控件,可以先按行排列布局,再按列排列布局;或者反过来,先排好列,再 排好行;当然也可以直接用网格布局器或表单布局器。条条大道通罗马,可以按实际控件的关系和用户喜好进行布局。

Qt 设计师左边列的四个布局器,其实不是经常需要拖动它们到主界面,更为常见的操作是选中控件,然后点击设计师上面布局工具栏里的快捷按钮实现布局,这些快捷按钮的功能更丰 富,也更常用。布局工具栏有 8 个按钮,下面依次介绍:

① 将选中控件添加到水平布局器排列。

② 将选中控件添加到垂直布局器排列。

③ 将选中控件添加到水平分裂器排列。

④ 将选中控件添加到垂直分裂器排列。

⑤ 将选中控件添加到网格布局器排布,行列的数目不限。

⑥ 将选中控件添加到表单布局器排布,该布局器固定为两列控件。

⑦ 打破布局,即保留布局器内部的控件和子布局,消除当前选中的布局器。

⑧ 根据需要显示的内容,自动调整控件或窗体的尺寸,相当于调用一次 adjustSize() 函数。

这里需要说明一下,布局器和空白条的基类其实都是 QLayoutItem,布局器仅用于辅助功能,帮助自动调整窗口里的控件布局,并不是实体控件,没有 show() 之类的显示函数,不能单独存在,必须要有实体控件才能设置布局器。

我们上一章介绍的都是实体控件,基类都是 QWidget ,都可以单独存在,有 show() 之类的显示函数。

分裂器具有布局功能,但分裂器的基类是 QFrame,分裂器是一个实体控件,分裂器不同于布局器,我们到 6.6 节专门讲分裂器。

2. QBoxLayout

水平布局器 QHBoxLayout 和垂直布局器 QVBoxLayout 的基类都是 QBoxLayout,只是二者排列方向不同。水平和垂直布局器的主要功能函数都位于基类 QBoxLayout 里面,我们这里专门介绍一下这个基类的功能。

QBoxLayout 构造函数和 setDirection() 都可以指定布局器的方向:

QBoxLayout(Direction dir, QWidget * parent = 0) void setDirection(Direction direction) QBoxLayout 布局器的方向 QBoxLayout::​Direction 枚举不仅可以指定水平和垂直,还能指定反方向排列:

枚举常量 数值 描述
QBoxLayout::LeftToRight 0 水平布局,从左到右排列
QBoxLayout::RightToLeft 1 水平布局,从右到左排列
QBoxLayout::TopToBottom 2 垂直布局,从上到下排列
QBoxLayout::BottomToTop 3 垂直布局,从下到上排列

水平布局器 QHBoxLayout 和垂直布局器 QVBoxLayout 默认是其中的两种:QBoxLayout::LeftToRight 和 QBoxLayout::TopToBottom 。

布局器是一定要往里面添加控件才有用,添加控件的函数如下:

void addWidget(QWidget * widget, int stretch = 0, Qt::Alignment alignment = 0)
void insertWidget(int index, QWidget * widget, int stretch = 0, Qt::Alignment alignment = 0)

widget 就是要添加的控件指针,stretch 是伸展因子(到 6.5 节再讲这个,本节先不管),伸展因子越大,窗口变大时拉伸越 多,alignment 一般不需要指定,用默认的即可。第一个 addWidget() 是将控件添加到布局里面的控件列表末尾,第二个 insertWidget() 是将控件插入到布局里控件列表序号为 index 的位置。

对于布局器里的各个控件,可以设置相邻控件之间默认的间距:

void setSpacing(int spacing)

就是间隔的像素点数目。如果不设置 spacing,那么布局器会根据默认策略决定如何添加控件之间的间隙,一般是根据父窗口或父布局器的策略来 定。

布局器中不仅可以添加控件,还可以直接添加其他布局:

void addLayout(QLayout * layout, int stretch = 0)
void insertLayout(int index, QLayout * layout, int stretch = 0)

参数里添加的 layout 布局器会作为一个整体,与父布局器里其他控件一块排布,stretch 也是伸展因子。

对于 QBoxLayout、QHBoxLayout 、QVBoxLayout 布局器,通常不需要手动新建空白条对象,因为它们自带相关函数:

void QBoxLayout::​addSpacing(int size) //添加 size 固定尺寸空白条到布局器
void QBoxLayout::​addStretch(int stretch = 0) //添加自动拉伸的空白条到布局器
void QBoxLayout::​insertSpacing(int index, int size) //插入 size 固定尺寸空白条到布局器
void QBoxLayout::​insertStretch(int index, int stretch = 0) //插入自动拉伸的空白条到布局器

对于 add* 添加函数,因为布局器内部通常有多个控件,添加函数是把空白条添加到最后。

而 insert* 插入函数,是把空白条插入到指定序号 index 的位置。

如果要添加自己创建的空白条对象,也是可行的:

void addSpacerItem(QSpacerItem * spacerItem)
void insertSpacerItem(int index, QSpacerItem * spacerItem)

另外,还可以自己从 QLayoutItem 派生新的布局器条目,对布局器条目进行自定义,这些新的布局器条目可以用如下函数添加:

virtual void addItem(QLayoutItem * item)
void insertItem(int index, QLayoutItem * item)

讲了如何添加控件和其他布局器,当然也可以计算布局器里面的条目计数:

virtual int count() const

如果要获得布局器中某个序号的条目:

virtual QLayoutItem * itemAt(int index) const

如果要删除布局器中某个序号的条目:

virtual QLayoutItem * takeAt(int index)

布局器中无论是填充普通控件还是其他布局器,每个条目都是用 QLayoutItem 封装的,对于获得的 QLayoutItem 指针(非空指针),如果要获取里面封装的控件、布局器或空白条,使用如下函数:

QWidget * QLayoutItem::​widget()
QLayout * QLayoutItem::​layout()
QSpacerItem * QLayoutItem::​spacerItem()

注意判断以上函数的返回值是否为 NULL 指针,如果是非空指针才能进行其他操作。关于水平和垂直布局器的内容介绍这么多,下面通过例子展示怎么用这些布局器。

3. 网络参数示例的布局

前一章 5.2.4 节有一个网络参数输入的数据验证器示例 netparas ,我们以这个项目为底板构造本节的布局例子。

主界面的窗体都可以通过如下函数,将一个布局器设置为主布局器(或叫总布局器、顶级布局器):

void QWidget::​setLayout(QLayout * layout)

参数里的 layout 布局器就是主布局器,会自动占满窗体内部所有区域。我们可以设置好主布局器,然后通过该函数把主布局器设置给窗体。或者,如果在 layout 布局器构造参数里用主界面窗体的指针作为该布局器父指针 parent,主界面窗体只有这唯一一个直属的布局器(其他子布局器的 parent 设置为该布局器或其他子布局器),那么这个唯一直属布局器自动成为主布局器。

下面我们把 5.2.4 小节 D:\QtProjects\ch05\ 目录里的 netparas 子文件夹复制一份,

保存到第 6 章例子目录 D:\QtProjects\ch06\ 里面,然后进行下面操作:

① 把新的 netparas 文件夹重命名为 netparasnew,并把 netparasnew 里面的 netparas.pro.user 文件删掉。

② 进入 netparasnew 文件夹,把 netparas.pro 重命名为 netparasnew.pro 。

③ 用记事本打开 netparasnew.pro ,修改里面的 TARGET 一行,变成下面这句:

TARGET = netparasnew

这样我们就得到了这个新例子 netparasnew 项目。

双击打开 netparasnew.pro 文件或者用 QtCreator 打开这个 netparasnew.pro 项目,在配置项目界面选择所有套件并点 击 "Configure Project" ,配置好项目后,打开 widget.ui 界面文件,进入 QtCreator 设计模式:

designer

本节只用到图上标出的两个工具栏按钮,即水平布局和垂直布局。下面教大家两套布局操作,第一套操作,另外需要在 widget.cpp 文件构造函数里加一句​setLayout() 函数设置主布局器。第二套操作,不需要手动添加任何代码,单纯用设计师操作。

◆ 先看第一套布局操作:

按照下图,选中第一行的两个控件,点击设计模式上面的水平布局按钮:

designer

点击水平布局按钮之后,看到如下图中红框框的布局器:

designer

然后类似地,选中第二行控件,点击设计模式上面的水平布局按钮;

再选中第三行控件,点击设计模式上面的水平布局按钮,得到如下图所示的三行布局器:

designer

下面要把三行水平布局器在垂直方向在进行一次布局,得到主布局器。选中三行水平布局器,然后点击设计模式上面的垂直布局按钮:

designer

最后得到包括所有控件的主布局器:

designer

这时候垂直布局器 verticalLayout 就是我们需要的主布局器,在 widget.cpp 的构造函数里面末尾的位置还需要添加一句代码:

//第一种布局操作需要的代码 setLayout ( ui -> verticalLayout ); 这是第一套操作的过程。

◆ 再看第二套布局操作的过程

我们回到窗体没有任何布局的状态,可以按快捷键 Ctrl+A ,选中刚才全部的布局器,点击设计模式的打破布局按钮:

designer

上图打破布局按钮的功能是这样的,如果选中单个布局器,它就打破选中的那一个布局器;

如果选中所有的控件和布局器,它就打破所有的布局器,只剩下普通控件。打破所有布局后,回到没有布局器的状态:

designer

然后我们从头开始布局,选中第一行的两个控件,点击上面水平布局按钮;

选中第二行的两个控件,点击上面的水平布局按钮;

再选中第三行的两个控件,也点击上面的水平布局按钮。得到三行水平布局:

designer

得到三行水平布局之后,我们需要设置最后的主布局器,注意这里第二套操作的过程,与之前第一套的操作不同。

我们现在点击一下主界面窗体的标题或者空白位置,不选中任何布局和控件(其实就是唯一选中主 界面窗口自身),直接点击上面的垂直 布局按钮,主界面会变成下面所示的:

designer

不选中任何布局和控件,直接在上面点击垂直布局器,这时候垂直布局器自动成为主窗体的主布局器,并且在右下角的属性编辑一栏会多出来一个 Layout 属性。

因为现在 verticalLayout 自动成为了主布局器,它填满了整个主窗体,原先的三行水平布局器的高度都变高了,每个水平布局器大约是主窗体的三分之一 高。

一般情况下,主界面的窗体 Widget 没有 Layout 属性,只有像刚才那样操作,不选中布局和控件,直接点击上面的布局器,才会自动为主界面的窗体添加主布局器,Widget 才有 Layout 属性,就是主布局器。

无论是主布局器,还是一般的子布局器,都有几个可以设置的属性条目:

① layoutName,布局器名称,uic 自动为界面生成窗体代码时,这个名字就是布局器对象的指针,比如 ui->verticalLayout 。

② layoutLeftMargin、layoutTopMargin、layoutRightMargin、layoutBottomMargin,四个边距,布局器内部 的控件或子布局器距离该布局器四个边的距离。设置了边距之后,控件距离布局四周会有个空隙,这样界面看起来不会太挤。上图的 verticalLayout 四个边距都是 9 。

③ layoutSpacing,布局器内部的控件或子布局器的固定间距,上图的三个水平布局器之间的间隙就是 6 。

④ layoutStretch,布局器内的控件或子布局器的伸缩因子,默认都是 0,这个留到 6.5 节专门讲。

⑤ layoutSizeConstraint,布局器的尺寸约束,一般用默认的 QLayout::SetDefaultConstraint 即可。

我们按照第二套操作方法,就不需要修改 *.h 和 *.cpp 中的源代码,界面布局的设置与源代码是分离的,只需要从设计师操作 *.ui 文件就够了,这是 非常方便的。

当然,上面的主界面还有一点瑕疵,就是三个标签控件宽度不一样,会看起来比较别扭。第一行的标签宽度是 18,第二行的标签宽度是 12,第三行的标签宽度是 24,我们设置三个标签控件的 minimumSize 属性,把最小宽度都设置成 24,那么三个标签就自动对齐了:

designer

设置好标签最小宽度之后,界面里的控件就全都对齐了,而且窗体无论变大变小,都能自动适应。

一般修改并保存界面文件之后,需要从 QtCreator 的菜单,点击【 构建--> 重新构建项目 "项目名" 】,需要手动重新构建项目,这样 ui_*.h 文件才会根据 *.ui 文件做更新,手动重新构建项目之后,我们运行例子程序:

designer

现在可以随意拉伸或缩小程序窗口,里面的控件布局会自动适应窗口大小。

这样我们就不需要手动编写任何代码,Qt 布局器会自动完成控件的分布和尺寸调整。

4. 简易 HTML 查看器示例的布局

前一章 5.3.4 节有一个简易 HTML 查看器示例 simplebrowser,我们还是用水平和垂直布局器对它进行布局。Qt 的多种布局器都可以作为窗口的主布局器,上个例子使用垂直布局器作为窗口的主布局器,现在我们试一下把水平布局器设置成窗口的主布局器。

以后设置主布局器我们都是用前面介绍的第二套布局操作,自动设置窗口的主布局器,而不需要添加任何布局代码。

我们把 5.3.4 小节 D:\QtProjects\ch05\ 目录里的 simplebrowser 子文件夹复制一份,

保存到第 6 章例子目录 D:\QtProjects\ch06\ 里面,然后进行下面操作:

① 把新的 simplebrowser 文件夹重命名为 simplebrowsernew,并把 simplebrowsernew 文件夹里的 simplebrowser.pro.user 删掉。

② 进入 simplebrowsernew 文件夹,把 simplebrowser.pro 文件重命名为 simplebrowsernew.pro 。

③ 用记事本打开 simplebrowsernew.pro ,修改里面的 TARGET 一行,变成下面这句:

TARGET = simplebrowsernew 这样我们就得到了新的 simplebrowsernew 项目。

双击打开新项目或者用 QtCreator 打开这个 simplebrowsernew.pro 项目文件,在配置项目界面选择所有套件并点击 "Configure Project" ,配置好项目后,打开 widget.ui 界面文件,进入 QtCreator 设计模式:

designer

这个窗体里的控件是比较有规律的,中间一道缝,两边各一半。

下面有三个按钮,我们先处理一下按钮的布局。

选中左边两个按钮,点击设计模式上面的水平布局按钮,得到下图所示的布局器:

designer

然后选中左边的 textBrowser 控件和两个按钮的布局器,点击设计模式上面的垂直布局按钮,得到下图所示的左半部分布局器:

designer

这时候我们就需要注意了,这两个按钮被拉得太宽,如果布局器跟着窗体变宽,那么按钮会被拉伸得非常宽,会比较丑,这不科学。

我们希望避免按钮被拉伸,就可以使用前面 6.2.1 节的空白条,我们拖动一个水平空白条,塞到第二个按钮的右边的细缝里:

designer

这样左半边的布局就设置好了。

对于空白条的拖动,可以先设置布局,再塞入空白条。当然也可以预先拖一个空白条,然后选中空白条和控件一起进行布局。

有了左半边布局的经验,我们也希望右边的按钮不被拉伸,我们预先拖一个水平空白条到 "打开HTML" 按钮的左边:

designer

我们现在选中新的空白条和 "打开HTML" 按钮,点击设计模式上面的水平布局按钮,得到下图的布局器:

designer

然后我们选中右边带空白条的布局器和 plainTextEdit 控件,点击设计模式上面的垂直布局按钮,会生成右半部的布局器:

designer

两个半边的布局器都设置好了,现在我们点击主窗体下方的空白区域,不选中任何布局器和控件(其实就是唯一选中主界面窗口自身),直接点击设计模式上面的水平布局按 钮,这样新的水平布局自动成为主窗体 的 Layout 属性,就是主布局器:

designer

现在布局工作就完成了。我们点击 QtCreator 菜单【 构建--> 重新构建项目 "项目名" 】,重新构建例子,然后运行它:

designer

现在随便拉伸程序的窗口都可以,Qt 布局会自动调整里面的控件分布和尺寸,整个布局过程不需要添加任何一句代码。

通过这个例子,主要是介绍空白条的使用,可以先布局,再塞空白条;或者先拖好空白条,再进行布局。两种操作方式都可以。

5. ui_*.h 文件中布局器的内幕代码

虽然我们不需要手动编写布局器的代码,但是应该学习 uic 功能生成的布局器内幕代码,因为难免遇到需要自己编写布局器代码的时候。Qt 设计师也不是万能的,学 习 ui_*.h 里面的代码是很有必要的。

我们就以刚才的简易 HTML 查看器示例布局为例,学习这个例子的 ui_widget.h 里面的代码,这个文件位于影子构建目录:

D:\QtProjects\ch06\build-simplebrowsernew-Desktop_Qt_5_4_0_MinGW_32bit-Debug

现在把它的内容贴出来:

#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QPlainTextEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
    QHBoxLayout *horizontalLayout_3;
    QVBoxLayout *verticalLayout;
    QTextBrowser *textBrowser;
    QHBoxLayout *horizontalLayout;
    QPushButton *pushButtonBackward;
    QPushButton *pushButtonForeward;
    QSpacerItem *horizontalSpacer;
    QVBoxLayout *verticalLayout_2;
    QPlainTextEdit *plainTextEdit;
    QHBoxLayout *horizontalLayout_2;
    QSpacerItem *horizontalSpacer_2;
    QPushButton *pushButtonOpen;
    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QStringLiteral("Widget"));
        Widget->resize(630, 350);
        horizontalLayout_3 = new QHBoxLayout(Widget);
        horizontalLayout_3->setSpacing(6);
        horizontalLayout_3->setContentsMargins(11, 11, 11, 11);
        horizontalLayout_3->setObjectName(QStringLiteral("horizontalLayout_3"));
        verticalLayout = new QVBoxLayout();
        verticalLayout->setSpacing(6);
        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
        textBrowser = new QTextBrowser(Widget);
        textBrowser->setObjectName(QStringLiteral("textBrowser"));
        verticalLayout->addWidget(textBrowser);
        horizontalLayout = new QHBoxLayout();
        horizontalLayout->setSpacing(6);
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
        pushButtonBackward = new QPushButton(Widget);
        pushButtonBackward->setObjectName(QStringLiteral("pushButtonBackward"));
        horizontalLayout->addWidget(pushButtonBackward);
        pushButtonForeward = new QPushButton(Widget);
        pushButtonForeward->setObjectName(QStringLiteral("pushButtonForeward"));
        horizontalLayout->addWidget(pushButtonForeward);
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        horizontalLayout->addItem(horizontalSpacer);
verticalLayout->addLayout(horizontalLayout);
        horizontalLayout_3->addLayout(verticalLayout);
        verticalLayout_2 = new QVBoxLayout();
        verticalLayout_2->setSpacing(6);
        verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));
        plainTextEdit = new QPlainTextEdit(Widget);
        plainTextEdit->setObjectName(QStringLiteral("plainTextEdit"));
        verticalLayout_2->addWidget(plainTextEdit);
        horizontalLayout_2 = new QHBoxLayout();
        horizontalLayout_2->setSpacing(6);
        horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        horizontalLayout_2->addItem(horizontalSpacer_2);
        pushButtonOpen = new QPushButton(Widget);
        pushButtonOpen->setObjectName(QStringLiteral("pushButtonOpen"));
        horizontalLayout_2->addWidget(pushButtonOpen);
        verticalLayout_2->addLayout(horizontalLayout_2);
        horizontalLayout_3->addLayout(verticalLayout_2);
        retranslateUi(Widget);
        QMetaObject::connectSlotsByName(Widget);
    } // setupUi
    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QApplication::translate("Widget", "Widget", 0));
        pushButtonBackward->setText(QApplication::translate("Widget", "\345\220\216\351\200\200", 0));
        pushButtonForeward->setText(QApplication::translate("Widget", "\345\211\215\350\277\233", 0));
        pushButtonOpen->setText(QApplication::translate("Widget", "\346\211\223\345\274\200HTML", 0));
    } // retranslateUi
};
namespace Ui {
    class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H

它包含的头文件就是例子用到的控件类以及其他预先包含的头文件。

我们现在解释一下 Ui_Widget 类开始处的成员变量,直接用注释形式说明:

class Ui_Widget
{
public:
    QHBoxLayout *horizontalLayout_3;//主布局器
    //左半边
    QVBoxLayout *verticalLayout;    //左半边布局器
    QTextBrowser *textBrowser;      //丰富文本浏览器
    QHBoxLayout *horizontalLayout;  //两个按钮和空白条的水平布局器
    QPushButton *pushButtonBackward;//后退按钮
    QPushButton *pushButtonForeward;//前进按钮
    QSpacerItem *horizontalSpacer;  //左边的空白条
    //右半边
    QVBoxLayout *verticalLayout_2;  //右半边布局器
    QPlainTextEdit *plainTextEdit;  //普通文本编辑器
    QHBoxLayout *horizontalLayout_2;//右边按钮和空白条的水平布局器
    QSpacerItem *horizontalSpacer_2;//右边的空白条
    QPushButton *pushButtonOpen;    //打开按钮

解释了成员变量之后,我们来看看 setupUi() 函数,主窗体的布局器代码都在这里面,我们对代码进行分片讲解,代码内容以注释形式讲解:

    void setupUi(QWidget *Widget)
    {
        //Widget 是要进行界面构造的窗体
        //先设置窗体的对象名称
        if (Widget->objectName().isEmpty())
            Widget->setObjectName(QStringLiteral("Widget"));
            Widget->resize(630, 350);   //设置窗体尺寸
        //主布局器构造
        horizontalLayout_3 = new QHBoxLayout(Widget);   //新建布局器
        horizontalLayout_3->setSpacing(6);              //设置内部控件或子布局器间隙
        //设置内部控件或子布局器距离四个边的边距
        //设计师原本是(9,9,9,9),因为是主布局器,uic 工具额外增加了 2 的边距
        horizontalLayout_3->setContentsMargins(11, 11, 11, 11);
        //设置主布局器对象名称
        horizontalLayout_3->setObjectName(QStringLiteral("horizontalLayout_3"));
}

该函数内部先设置主窗体 Widget 的对象名和尺寸。

然后构造了主布局器,并设置主布局器的属性。我们在 QtCreator 设计模式原本看到的 layoutLeftMargin、layoutTopMargin、layoutRightMargin、layoutBottomMargin,四个边距都是 9,但是这里调用的是 setContentsMargins(11, 11, 11, 11),因为是主布局器,额外增了 2 的边距,与窗体边界多设置了一些 间隙。只有主布局器会额外加边距,内部子布局器不会。

然后是左边控件和布局器的代码:

//左半边
        verticalLayout = new QVBoxLayout(); //左半边大布局器
        verticalLayout->setSpacing(6);      //内部控件和子布局器间隙也是 6
        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));//布局器对象名称
        //左边丰富文本浏览器控件
        textBrowser = new QTextBrowser(Widget); //新建丰富文本浏览器
        textBrowser->setObjectName(QStringLiteral("textBrowser"));  //对象名称
        verticalLayout->addWidget(textBrowser); //添加丰富文本浏览器到左边大布局器
        //左边两个按钮的布局器
        horizontalLayout = new QHBoxLayout();   //新建布局器
        horizontalLayout->setSpacing(6);        //设置内部控件间隙
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));//对象名称
        pushButtonBackward = new QPushButton(Widget);   //新建后退按钮
        pushButtonBackward->setObjectName(QStringLiteral("pushButtonBackward"));//对象名称
        horizontalLayout->addWidget(pushButtonBackward);//添加到按钮布局器
        pushButtonForeward = new QPushButton(Widget);   //新建前进按钮
        pushButtonForeward->setObjectName(QStringLiteral("pushButtonForeward"));//对象名称
        horizontalLayout->addWidget(pushButtonForeward);//添加到按钮布局器
        //新建左边的水平空白条,水平策略是伸展方式 QSizePolicy::Expanding
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        horizontalLayout->addItem(horizontalSpacer);    //添加空白条到按钮布局器
        verticalLayout->addLayout(horizontalLayout);    //添加按钮布局器到左边的大布局器
        horizontalLayout_3->addLayout(verticalLayout);  //添加左半边大布局器到主布局器

左半边的布局器和控件创建过程是,先建立左边大布局器 verticalLayout ,设置该布局器的属性;

新建左上方的丰富文本浏览器,设置对象名称,添加到 verticalLayout ;

新建两个按钮的水平布局器 horizontalLayout,设置布局间隙和对象名称;

新建后退按钮,设置对象名,添加到按钮水平布局器 horizontalLayout;

新建前进按钮,设置对象名,添加到按钮水平布局器 horizontalLayout;

新建空白条,空白条的最优大小是 40*20,水平策略 QSizePolicy::Expanding,是尽可能伸展的意思,垂直策略是 QSizePolicy::Minimum,是尽可能不拉伸,高度达到最优高度 20 以后,就不会再变高;

然后用 addItem() 函数把空白条添加到按钮水平布局器 horizontalLayout;

把 horizontalLayout 布局器添加到左半边大布局器 verticalLayout;

再把左半边的 verticalLayout 添加到主布局器 horizontalLayout_3 。

这个构建和布局的过程,其实就是对布局树的遍历过程,我们在 QtCreator 设计模式右上角能看到这个布局树:

designer

从布局树上看,先构建根节点, 然后 构建子节点,再构建孙子节点,依此类推;

从窗体图形界面上看,同一层级的布局或控件,是按照窗体图形里从上到下、从左到右的方式构建。

构建的基本规律就是上面描述的,现在来看看窗体右半部分的构建和布局代码:

        //右半边
        verticalLayout_2 = new QVBoxLayout();   //右半边大布局器
        verticalLayout_2->setSpacing(6);        //布局间隙
        verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));//对象名称
        plainTextEdit = new QPlainTextEdit(Widget); //普通文本编辑器
        plainTextEdit->setObjectName(QStringLiteral("plainTextEdit"));  //对象名称
        verticalLayout_2->addWidget(plainTextEdit); //添加到右半边大布局器
        //右边按钮的布局器
        horizontalLayout_2 = new QHBoxLayout(); //新建右边按钮的水平布局器
        horizontalLayout_2->setSpacing(6);      //布局间隙
        horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));//对象名称
        //新建右边的空白条,最优尺寸 40*20,水平策略是尽量伸展,垂直策略是高度到 20 之后不变高
        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
        horizontalLayout_2->addItem(horizontalSpacer_2);//添加空白条到按钮的布局器
        pushButtonOpen = new QPushButton(Widget);   //新建打开按钮
        pushButtonOpen->setObjectName(QStringLiteral("pushButtonOpen"));//对象名称
        horizontalLayout_2->addWidget(pushButtonOpen);  //添加到按钮的水平布局器
        verticalLayout_2->addLayout(horizontalLayout_2);//添加到右半边的大布局
        horizontalLayout_3->addLayout(verticalLayout_2);//添加到主布局器
 

右半边的构造和布局器过程与左半边是类似的。

新建右半边的大布局器 verticalLayout_2,设置布局间隙和对象名;

新建普通文本编辑器,设置对象名,添加到右边布局器 verticalLayout_2;

新建右边按钮的布局器,设置布局间隙和对象名;

新建右边的水平空白条,最优尺寸 40*20,水平策略 QSizePolicy::Expanding,垂直策略 QSizePolicy::Minimum;

用 addItem() 函数把空白条添加到按钮的水平布局器 horizontalLayout_2;

新建打开按钮,设置对象名,添加到按钮水平布局器 horizontalLayout_2;

把按钮布局器 horizontalLayout_2 添加到右半边大布局器 verticalLayout_2;

再把右半边大布局器 verticalLayout_2 添加到主布局器 horizontalLayout_3。

关于界面构建和布局的代码就是上面那么多。下面把 Ui_Widget 剩下的代码概略看看:

        retranslateUi(Widget);  //翻译界面
        QMetaObject::connectSlotsByName(Widget);//自动关联槽函数
    } // setupUi 函数
    void retranslateUi(QWidget *Widget)
    {
        //设置窗口标题
        Widget->setWindowTitle(QApplication::translate("Widget", "Widget", 0));
        //后退按钮的文本
        //八进制 "\345\220\216"  UTF-8 编码的 "后" 
        //八进制 "\351\200\200"  UTF-8 编码的 "退" 
        pushButtonBackward->setText(QApplication::translate("Widget", "\345\220\216\351\200\200", 0));
        //设置前进按钮文本,八进制表示的 UTF-8 文本 "前进"
        pushButtonForeward->setText(QApplication::translate("Widget", "\345\211\215\350\277\233", 0));
        //设置打开HTML按钮文本,八进制和英文字母表示的 "打开HTML"
        pushButtonOpen->setText(QApplication::translate("Widget", "\346\211\223\345\274\200HTML", 0));
    } // retranslateUi 函数
};//Ui_Widget 

setupUi 函数最后两句是翻译界面和实现自动关联槽函数。

retranslateUi() 函数是设置窗体的标题文本、按钮的文本。

以上是 ui_widget.h 里面的主要代码,是 uic 工具根据 widget.ui 文件自动生成的代码。以后我们自己如果需要写布局代码,可以效仿上面的代码,当然不需要那么严格,我们可以按照自己的规划从小到大、从少到多地构建界面,不一定要像上面那 样死板,可以自己灵活写代码,比如水平空白条就不需要单独新建,直接调用一句 horizontalLayout->addStretch() 即可。

本节介绍的是水平和垂直布局器,对应大部分的窗体其实这两个布局器都够用了。Qt 还另外提供了网格布局器、表单布局器,无论用哪些布局器,其实都可以实现类似的布局效果,条条大道通罗马。不需要纠结用哪些布局器,也不要拘泥于一两种布局器,只要能实现 效果就够了,实现的过程可以按照界面的需求和自己的喜好来定。


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



10 次浏览
1次