11.3 基本布局实践

基本布局主要包括:水平布局、垂直布局、栅格布局和表单布局这 4 种。在讲解水平 布局和垂直布局之前,还要重点说一下它们的父类 -QBoxLayout,它也是使用比较多的,并 且包含了水平布局和垂直布局的一些共性特点。

11.3.1 QBoxLayout

使用 QBoxLayout 类可以创建一个布局,能够把其中的窗口部件水平的或者垂直的按照直线排列。

之所以取名为“Box”,是由于 QBoxLayout 类把位于其内的空间均匀的划分成若干个 “盒子”,并把各个窗口部件都放入一个盒子里面。

使用 QBoxLayout 创建的布局是有方向性的,主要是有水平和垂直两个方向。当给定其 方向参数为 Qt::Horizontal 时,即表示水平方向,位于其中的窗口部件将水平排列成一 行,并且它们都会找到适合自己的大小。当给定方向参数为 Qt::Vertical 时,窗口部件将按 照垂直直线排列。布局内的其余空间是共享的,它们的尺寸可以由伸缩因子( stretch factors)来确定。

使用 QBoxLayout 类来创建一个布局是很容易的,做法是直接调用其构造函数,其原型 如下:

QBoxLayout::QBoxLayout ( Direction dir, QWidget * parent = 0 )

该构造函数它有两个参数,一个是布局的方向,一个是父窗口指针,默认为 0 即当前窗口。布局的方向枚举值如表 11-4 所示。

表 11-4 QBoxLayout 的方向枚举值

常量 说明
QBoxLayout::LeftToRight 0 从左到右水平排列
QBoxLayout::RightToLeft 1 从右到左水平排列
QBoxLayout::TopToBottom 2 从上到下垂直排列
QBoxLayout::BottomToTop 3 从下到上垂直排列

一个实例代码如下:

//第 1 步
QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton(tr("One"));
QPushButton *button2 = new QPushButton(tr("Two"));
QPushButton *button3 = new QPushButton(tr("Three"));
QPushButton *button4 = new QPushButton(tr("Four"));
QPushButton *button5 = new QPushButton(tr("Five"));
//第 2 步
QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight,0);
//第 3 步
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
//第 4 步
window->setLayout(layout);
//显示窗口
window->show();

创建这种布局的一般步骤是:

第 1 步,创建要使用的窗口部件。

第 2 步,创建布局,并指定其方向和父窗口。

第 3 步,将窗口部件依次加入到布局中。

第 4 步,为应用程序程序窗口设置布局。

如上代码所示,在其中通过注释指出了各步骤对应的代码行。这段代码将创建 1 个名 为 window 的应用程序窗口,1 个名为 layout 的 QBoxLayout 实例,以及 5 个按钮,布局方 向是从左到右的水平方向。图 11-16 显示了这个实例的效果,你会发现它与使用水平布局 的效果是一样的。

图 11-16 使用 QBoxLayout 类创建的布局效果

当然,在实际应用中,我们大多数情况下会直接使用 QBoxLayout 的两个派生类,水平 布局和垂直布局,它们更加方便而且有针对性。

11.3.2 水平布局

水平布局把在其中的窗口部件按照一条直线水平排列。 QHBoxLayout 类用来创建水平 布局的实例。

一个简单的手写代码创建水平布局的实例如下:

QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton(tr("One"));
QPushButton *button2 = new QPushButton(tr("Two"));
QPushButton *button3 = new QPushButton(tr("Three"));
QPushButton *button4 = new QPushButton(tr("Four"));
QPushButton *button5 = new QPushButton(tr("Five"));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();

使用手写代码创建水平布局大体需要下面这些步骤:

第 1 步,创建布局内的窗口部件。

第 2 步,创建一个水平布局的实例,也就是创建一个 QHBoxLayout 的实例,并将第 1 步创建的窗口部件加入到该布局之中。

第 3 步,调用 QWidget::setLayout()函数将该布局安装到窗体上。设置了布局之后, 在该布局内的窗口部件将以 window 作为它们的父窗口。该实例的效果如图 11-17 所示:

图 11-17 水平布局效果

11.3.3 垂直布局

垂直布局把位于其中的窗口部件按照一条直线垂直排列。 QVBoxLayout 类用来创建一个 垂直布局的实例。

一个简单的使用手写代码创建垂直布局的实例如下:

QWidget *window = new QWidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two");
QPushButton *button3 = new QPushButton("Three");
QPushButton *button4 = new QPushButton("Four");
QPushButton *button5 = new QPushButton("Five");
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
layout->addWidget(button4);
layout->addWidget(button5);
window->setLayout(layout);
window->show();

使用手写代码创建垂直布局大体需要下面这些步骤:

第 1 步,创建布局内的窗口部件

第 2 步,创建一个垂直布局的实例,也就是创建一个 QVBoxLayout 类的实例,并将第 1 步创建的窗口部件加入到该布局之中

第 3 步,调用 QWidget::setLayout()函数将该布局安装到窗体上。设置了布局之后, 在该布局内的窗口部件将以 window 作为它们的父窗口。该实例的效果如图 11-18 所示:

图 11-18 垂直布局实例效果

水平和垂直布局可以嵌套、组合使用,并且可至任意的深度。但在比较复杂的情况 下,使用栅格布局往往是更理想的做法。

11.3.4 栅格布局

栅格布局将位于其中的窗口部件放入一个网状的栅格之中。栅格布局是这样工作的:

它计算了位于其中的空间,然后将它们合理的划分成若干个行( row)和列(column),并把每个由它管理的窗口部件放置在合适的单元之中,这里所指的单元( cell)即是指由行和列交叉所划分出来的空间。

在使用栅格布局之前,需要在代码中包含如下的头文件声明:

#include <QGridLayout>

创建栅格布局的大致步骤如下:

第 1 步,创建布局内的窗口部件。

第 2 步,创建栅格布局的实例,也就是创建一个 QGridLayout 类的实例,并将第 1 步 创建的窗口部件加入到布局之中。

第 3 步,调用 QWidget::setLayout()函数将布局安装到窗体上。 一个典型的实例代码如下:

nameLabel = new QLabel(tr("&Name:"));
nameLabel->setBuddy(nameLineEdit);
ageLabel = new QLabel(tr("&Age:"));
ageLabel->setBuddy(ageSpinBox);
QGridLayout *gridLayout = new QGridLayout;
gridLayout->addWidget(nameLabel, 0, 0);
gridLayout->addWidget(nameLineEdit, 0, 1);
gridLayout->addWidget(ageLabel, 1, 0);
gridLayout->addWidget(ageSpinBox, 1, 1);
setLayout(gridLayout);

我们来讲解一下这段代码。可以明显的看出, QHBoxLayout 和 QVBoxLayout 的用法相当简单明了,但是 QGridLayout 的用法则稍微有些麻烦。QGridLayout 的工作基于一个二维单 元格。在这个布局中,左上角的 QLabel 即 nameLabel 的位置是(0,0),而与之相应的 QLineEdit 即 nameLineEdit 的位置是(0,1)。以此类推,ageLabel 的位置是(1,0),而 ageSpinBox 的位置是(1,1)。它们都占用一列的值。

对于 QGridLayout::addWidget()的调用遵循如下的语法形式:

layout->addWidget(widget,row,column,rowSpan,columnSpan);

其中,widget 是要插入到布局中的子窗口部件,(row,column)是由该窗口部件所占用的左上角单元格,rowSpan 是该窗口部件要占用的行数,而 column 是该窗口部件要占用的列 数。如果省略了这些参数,则它们将会取默认值 1。

该示例的运行效果如图 11-19 所示。

图 11-19 栅格布局效果

在栅格布局中,行和列本质上是相同的,只是叫法不同而已。下面我们将重点讨论 列,这些内容当然也适用于行。

在栅格布局中,每个列(以及行)都有一个最小宽度( minimum width)以及一个伸缩 因子(stretch factor)。最小宽度指的是位于该列中的窗口部件的最小的宽度,而伸缩因 子决定了该列内的窗口部件能够获得多少空间。它们的值可以通过 setColumnMinimumWidth()和 setColumnStretch()方法来设置。

此外,一般情况下我们都是把某个窗口部件放进栅格布局的一个单元中,但窗口部件 有时也可能会需要占用多个单元。这时就需要用到 addWidget()方法的一个重载版本,它的 原型如下:

void QGridLayout::addWidget ( QWidget * widget, int fromRow, int fromColumn, int rowSpan, int columnSpan,Qt::Alignment alignment = 0 )

这时这个单元将从 fromRow 和 fromColumn 开始,扩展到 rowSpan 和 columnSpan 指定 的倍数的行和列。如果 rowSpan 或 columnSpan 的值为-1,则窗口部件将扩展到布局的底部 或者右边边缘处。

小贴士:栅格布局中的某个单元(cell)的长和宽,也可以说是栅格布局的行和列的尺寸 并不是一样大小的。如果你想使它们相等,你必须通过调用 setColumnMinimumWidth()和 setColumnStretch()方法来使得它们的最小宽度以及伸缩因子都彼此相等。

如果 QGridLayout 不是窗体的顶层布局(就是说它不能管理所有的区域和子窗口部 件),那么当你创建它的同时,就必须为它指定一个父布局,也就是把它加入到父布局中 去,并且在此之前,不要对它做任何的操作。使用 addLayout()方法可以完成这一动作。

在创建栅格布局完成后,就可以使用 addWidget(),addItem(),以及 addLayout()方 法向其中加入窗口部件,以及其它的布局。

当界面元素较为复杂时,应该毫不犹豫的尽量使用栅格布局,而不是使用水平和垂直布局的组合或者嵌套的形式,因为在多数情况下,后者往往会使 “局势”更加复杂而难以控 制。栅格布局赋予了界面设计器更大的自由度来排列组合界面元素,而仅仅带来了微小的复杂度开销。

当要设计的界面是一种类似于两列和若干行组成的形式时,使用表单布局要比栅格布 局更为方便些。

11.3.5 表单布局

表单布局是从 Qt 4.4 开始被引入的。它把布局内的界面元素分成两列,左边的列通常 放置标签(label)而右边的列放置对应值的窗口部件,如 line edits,spin boxes 等等, 如图 11-20 所示。表单布局在不同的平台上与本地原生外观相同,并且支持长的行形式,

如表 11-5 所示。

图 11-20 使用表单布局

QFormLayout 这个类是从 Qt 4.4 以后引入的。在使用该类之前,需要包含如下的头文 件声明。

#include <QFormLayout>

在 QFormLayout 类出现以前,这种类似于两列布局的情形我们通常使用 QGridLayout 来创建。而现在,使用 QFormLayout 则有更多的优势:

能够适应不同的平台,并且提供与本地原生平台一致的观感

举例来说,在 Mac OS X Aqua 和 KDE 平台上通常要求标签中的文本是右对齐的,而在 Windows 和 GNOME 平台上则要求文本是左对齐的,QFormLayout 能够很好的自动适应。

支持可折叠的长行(当其中的文本较多时) Support for wrapping long rows. 在便携式设备上,文本太长以至于需要折叠的情况比较常见,但既要折叠而又要不影响显示的效果则是比较困难的事情。现在的 QFormLayout 类可以很好的解决这个问题,它会判断当前的情形是否需要折叠以及怎样折叠才好。表 11-5 显示了它的折叠策略。

表 11-5 QFormLayout 行折叠策略枚举值

常量 说明
QFormLayout::DontWrapRows 0 值域(即可输入的文本行)总是在其对应的标签(label)的旁边,也就是不 折叠。这是默认的行折叠策略(采用了 Qt 扩展风格的情况除外)。
QFormLayout::WrapLongRows 1 标签将获得最大允许的自由空间,与它对应的值域则使用剩余的空间,如 果一行放不下,它将另起一行,也就是把自己折叠起来。这是采用 Qt 扩展 风格情况的默认行折叠策略。
QFormLayout::WrapAllRows 2 值域将总是在与它对应的标签的下边一行。

API

为创建标签--(对应的)值域这种伙伴(buddy)类型的界面提供了丰富、方便的通过使用 addRow()函数(共有 6 种重载形式)或者 insertRow()函数(也有 6 种重载形式),我们可以简便的创建一个 QLabel 窗口部件以及它的伙伴窗口部件。一个实例代码 如下:

QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("&Name:"), nameLineEdit);
formLayout->addRow(tr("&Age:"), ageSpinBox);
setLayout(formLayout);

如果同样的情形使用 QGridLayout 来布局,则代码如下:

nameLabel = new QLabel(tr("&Name:"));
nameLabel->setBuddy(nameLineEdit);
ageLabel = new QLabel(tr("&Age:"));
ageLabel->setBuddy(ageSpinBox);
QGridLayout *gridLayout = new QGridLayout;
gridLayout->addWidget(nameLabel, 0, 0);
gridLayout->addWidget(nameLineEdit, 0, 1);
gridLayout->addWidget(ageLabel, 1, 0);
gridLayout->addWidget(ageSpinBox, 1, 1);
setLayout(gridLayout);

对比这两段代码就可以发现,它们实现了相同的界面,而使用 QFormLayout 类则更简单高效,并且不易出错。表 11-6 显示了该界面在不同的平台下的默认观感。

表 11-6 表单布局在不同平台上的默认外观

小贴士:标签(QLabel)和它的伙伴(buddy)窗口部件

一个标签窗口部件和一个窗口部件具有伙伴关系,即指当用户按下激活标签的快捷键 时,鼠标/键盘的焦点将会移到它的伙伴窗口部件上。只有 QLabel 的实例才可以有伙伴窗 口部件,也只有该 QLabel 实例具有快捷键(在显示文本的某个字符前面添加一个前缀 &, 就可以定义快捷键)时,伙伴关系才有效。例如:

QLineEdit* nameLineEdit = new QLineEdit(this);
QLabel* nameLabel = new QLabel(tr("&Name"),this);
nameLabel->setBuddy(nameLineEdit);

该代码定义了 nameLabel 标签的快捷键为"Alt+P",并将行编辑框 nameLineEdit 设置为它的伙伴窗口部件。所以当用户按下快捷键 "Alt+P"时,焦点将会跳至行编辑框 nameLineEdit 中。

在 Qt Designer 中,可以通过鼠标拖放操作快捷的建立 QLabel 和它的窗口部件的伙伴 关系,这需要切换到“伙伴编辑模式”。

由于 Qt 提供了许多的方法,所以使用表单布局时可以比较灵活的变换它的风格,经常 使用的方法有 setLabelAlignment()、setFormAlignment()、setFieldGrowthPolicy()、 setRowWrapPolicy()等。举个例子,如果要在所有的平台上都模拟出 Mac OS X 上的观感, 但却是用 Windows 上常见的标签文本左对齐的规则,可以这样写代码:

formLayout->setRowWrapPolicy(QFormLayout::DontWrapRows);
formLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
formLayout->setFormAlignment(Qt::AlignHCenter | Qt::AlignTop);
formLayout->setLabelAlignment(Qt::AlignLeft);

总结一下,创建表单布局的大致步骤如下:

第 1 步,创建布局内的窗口部件。

第 2 步,创建表单布局的实例,也就是创建一个 QFormLayout 类的实例,并将第 1 步 创建的窗口部件加入到布局之中。

第 3 步,调用 QWidget::setLayout()函数将布局安装到窗体上。

读者朋友可以通过前面的代码验证这些步骤。

11.3.6 删除布局内窗口部件的方法

要从一个布局内删除一个窗口部件,只需调用 QLayout::removeWidget()方法。其原型 如下:

void QLayout::removeWidget ( QWidget * widget )

这将删除该布局内的 widget 窗口部件,但是并没有把它从窗体界面上删除。调用完该函数后,你需要为该窗口部件指定一个合适的几何大小,或者干脆把它从界面上删除。一个 实例代码如下:

gridLayout->removeWidget(nameLabel);
nameLabel->setGeometry(9,9,50,25);

如果只是想使布局内的窗口部件隐藏起来,就可以调用 QWidget::hide()方法。然后调用 QWidget::show()方法可以使它再次显示。使用方法比较简单,读者可以自行验证。

如果往布局中添加一个窗口部件或者从布局中移除一个窗口部件,布局都会自动适应 所产生的这些新情况。如果对一个子窗口部件调用了 hide()或者 show(),也同样能够做到 自动适应。如果一个子窗口部件的大小提示发生了变化,布局将会自动进行调整,从而把新 的大小提示考虑进去。还有,布局管理器也会自动根据窗体中子窗口部件的最小大小提示和 大小提示,从总体上考虑,为这个窗体设置一个最小尺寸。

11.3.7 基本布局的综合运用

本实例利用基本布局管理(QHBoxLayout、QVBoxLayout、QGridLayout、QFormLayout)实现一个基于对话框的综合页面。实现效果如图 11-21 所示。实例代码见 basiclayouts 实例。

图 11-21 基本布局综合实例效果图

常用到的布局类有 QHBoxLayout、QVBoxLayout、QGridLayout、QFormLayout 这 4 种, 分别是水平布局、垂直布局、栅格布局和表单布局。布局中最常用的方法是 addWidget()和 addLayout(),addWidget()方法用于在布局中插入窗口部件,addLayout()用于在布局中插 入子布局。对于表单布局而言,向其中加入窗口部件最常用的方法是 addRow()。

下面通过实例的实现过程了解布局管理的使用方法。首先通过一个示意图了解此对话 框的布局结构,如图 11-22 所示。

图 11-22 实例布局结构图

从图中可知,本实例共用到 4 个布局管理器,最外层也即是顶级布局是一个垂直布 局,它是 QVBoxLayout 的实例,在顶级布局里面的最上层是一个水平布局,它是 QHBoxLayout 的实例,在它的下面是一个栅格布局,它是 QGridLayout 的实例,然后下面是 一个表单布局,它是 QFormLayout 的实例。

下面是具体的实现,首先声明一个对话框类 Dialog,它单继承自 QDialog。在该类的 头文件 dialog.h 中声明对话框中的各个窗口部件。

class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog();
private:
    void createHorizontalGroupBox();
    void createGridGroupBox();
    void createFormGroupBox();
    enum { NumGridRows = 3, NumButtons = 4 };
    QGroupBox *horizontalGroupBox;
    QGroupBox *gridGroupBox;
    QGroupBox *formGroupBox;
    QTextEdit *smallEditor;
    QTextEdit *bigEditor;
    QLabel *labels[NumGridRows];
    QLineEdit *lineEdits[NumGridRows];
    QPushButton *buttons[NumButtons];
    QDialogButtonBox *buttonBox;
};

然后定义对话框中包含的窗口部件。我们看看类的构造函数。

Dialog::Dialog()
{
    createHorizontalGroupBox();
    createGridGroupBox();
    createFormGroupBox();
    bigEditor = new QTextEdit;
    bigEditor->setPlainText(tr("This widget takes up all the remaining space "
        "in the top-level layout."));
    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
        | QDialogButtonBox::Cancel);
    connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(horizontalGroupBox);
    mainLayout->addWidget(gridGroupBox);
    mainLayout->addWidget(formGroupBox);
    mainLayout->addWidget(bigEditor);
    mainLayout->addWidget(buttonBox);
    setLayout(mainLayout);
    setWindowTitle(tr("Basic Layouts"));
}

第 3-5 行分别创建了 3 个组框-GroupBox,在它们的内部是 3 个布局。

第 6-7 行创建了创建了一个 QTextEdit 类的实例,并设置了它的显示文本。

第 8 行创建了一个 QDialogButtonBox 的实例,其中包含【OK】和【Cancel】两个按钮。

第 9-10 行连接按钮的信号和窗体的响应的槽。

第 11 行定义了窗体的顶级布局,它是一个 QVBoxLayout 的实例,是垂直布局。

第 12-16 行依次向顶级布局中加入窗口部件,注意使用 addWidget()方法的话,这些窗 口部件在布局中的排列顺序与它们的加入顺序是关联的。

小贴士:如果你想对布局内的窗口部件排列顺序和位置大小等属性做出更为详尽的控 制,可以考虑使用 insertWidget()方法来向布局中加入窗口部件。insertWidget()的原型 如下:

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

它为窗口部件 widget 指定了伸缩因子 stretch 和排列方向 alignment,以及在布局内 的顺序 index,然后把它加入到布局中。如果 index 的值为负整数,则表示把它加入到顺序 的最后面。

在布局中,哪个窗口部件的 stretch 值更大,那么它将有可能获得更大的空间。 如果在布局里面,所有窗口部件的 stretch 值都为 0,或者都不超过 0,那么它们的大小将由各自的大小策略以及大小策略互相影响的关系决定。

alignment 指定了窗口部件的排列方向,默认值为 0,表示该窗口部件将占用整个布局 内部的区域。alignment 通常有 3 个方向的取值,依次如如表 11-7、表 11-8 和表 11-9 所 示。

表 11-7 水平方向的取值

常量 说明
Qt::AlignLeft 0x0001 在布局内水平左对齐
Qt::AlignRight 0x0002 在布局内水平右对齐
Qt::AlignHCenter 0x0004 在可用的空间内部水平居中排列
Qt::AlignJustify 0x0008 Justifies the text in the available space.

注意,水平方向同时只能取一个值。

表 11-8 垂直方向的取值

常量 说明
Qt::AlignTop 0x0020 在布局内垂直向上对齐
Qt::AlignBottom 0x0040 在布局内垂直向下对齐
Qt::AlignVCenter 0x0080 在可用的空间内部垂直居中排列

垂直方向同时也只能取一个值。

表 11-9 居中取值

常量 说明
Qt::AlignCenter AlignVCenter | AlignHCenter 在水平方向和垂直方向都居中排列

如果已近使用了居中取值的话,就不必再设置水平和垂直方向的取值了。

第 17 行将主布局安装到窗体上。

第 18 行设置窗口标题。

再来看看创建水平布局的 createHorizontalGroupBox()函数。

void Dialog::createHorizontalGroupBox()
{
    horizontalGroupBox = new QGroupBox(tr("Horizontal layout"));
    QHBoxLayout *layout = new QHBoxLayout;
    for (int i = 0; i < NumButtons; ++i)
    {
        buttons[i] = new QPushButton(tr("Button %1").arg(i + 1));
        layout->addWidget(buttons[i]);
    }
    horizontalGroupBox->setLayout(layout);
}

第 4 行定义了一个水平布局的实例。

第 5-9 行定义了几个按钮窗口部件,然后依次向该布局中加入它们。

第 10 行把该布局安装到它的父窗体上。 然后看看创建栅格布局的函数 createGridGroupBox()里面的内容。

void Dialog::createGridGroupBox()
{
    gridGroupBox = new QGroupBox(tr("Grid layout"));
    QGridLayout *layout = new QGridLayout;
    for (int i = 0; i < NumGridRows; ++i)
    {
        labels[i] = new QLabel(tr("Line %1:").arg(i + 1));
        lineEdits[i] = new QLineEdit;
        layout->addWidget(labels[i], i + 1, 0);
        layout->addWidget(lineEdits[i], i + 1, 1);
    }
    smallEditor = new QTextEdit;
    smallEditor->setPlainText(tr("This widget takes up about two thirds of the "
        "grid layout."));
    layout->addWidget(smallEditor, 0, 2, 4, 1);
    layout->setColumnStretch(1, 10);
    layout->setColumnStretch(2, 20);
    gridGroupBox->setLayout(layout);
}

第 1 行创建了一个组框的实例,它将作为栅格布局的父窗口。

第 3-9 行创建了若干 QLineEdit 的实例,并使用 addWidget()方法把它们加入到布局 中。addWidget()有若干变型,这里使用的这个需要依次指定窗口部件、行和列以及对齐方 式,对齐方式默认为 0。注意可以看到这里的第 1 个 QLineEdit 实例位于(1,0)这个单元 (cell)。

第 10-11 行创建一个 QTextEdit 实例,并为它设置显示文本。

第 12 行将 QTextEdit 的实例 smallEditor 加入到布局中,这里使用的是 addWidget() 的另一种常用变型,其实参分别需要指定窗口部件、起始行、起始列、行跨度数和列跨度 数。

在本实例中实际上创建了一个 3x3 的栅格布局。左上角的文本为 Line 1 的标签窗口部 件位于(1,0),这样就可知道使用本行创建的 smallEditor 窗口部件位于(0,2),行跨度是 4 行,列跨度是 1 列,也就是它的单元占用了 4 行 1 列的空间。

第 13-14 行分别设置了第 1 列和第 2 列的伸缩因子为 10 和 20,这就保证了这两列无论 何时均保持宽度为 1:2。

第 15 行把这个栅格布局安装到其父窗口 gridGroupBox 上。 再看一下创建表单布局的 createFormGroupBox()函数内容。

void Dialog::createFormGroupBox()
{
    formGroupBox = new QGroupBox(tr("Form layout"));
    QFormLayout *layout = new QFormLayout;
    layout->addRow(new QLabel(tr("Line 1:")), new QLineEdit);
    layout->addRow(new QLabel(tr("Line 2, long text:")), new QComboBox);
    layout->addRow(new QLabel(tr("Line 3:")), new QSpinBox);
    formGroupBox->setLayout(layout);
}

第 1 行创建了一个组框,用于管理表单布局。

第 2 行创建了一个表单布局的实例。

第 3-5 行使用 addRow()方法,创建了几个 QLabel 的实例和 QLineEdit、QComboBox 和 QSpinBox 的实例,并把它们加入到表单布局中。在前面讲解表单布局的时候,曾经说到过 addRow()方法有很多种变型,此处采用的是其中的一种。

第 6 行把表单布局安装到它的父窗口上,它的父窗口就是第 1 行创建的组框实例。 最后书写主函数 main.cpp,其内容是创建应用程序全局实例,将刚才创建的窗口显示出来。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Dialog dialog;
    return dialog.exec();
}

这个实例完全采用手写代码实现。可以自己书写 .pro 文件,也可以在命令行下依次键 入 qmake 来生成。

本实例分析了 Qt4 中布局管理常用到的类及其方法,如果读者觉得这种手动布局的方 法比较麻烦,也可采用 Qt Designer 来布局。