📑 目录
- [📚 1. 容器类控件](#📚 1. 容器类控件)
- [1.1 Group Box](#1.1 Group Box)
- [1.2 Tab Widget](#1.2 Tab Widget)
- [📚 2. 布局管理器](#📚 2. 布局管理器)
- [2.1 垂直布局](#2.1 垂直布局)
- [2.2 水平布局](#2.2 水平布局)
- [2.3 网格布局](#2.3 网格布局)
- [2.4 表单布局](#2.4 表单布局)
- [2.5 Spacer](#2.5 Spacer)
- [📝 3. 小结](#📝 3. 小结)
- [🎯 4. 经典面试题](#🎯 4. 经典面试题)
📚 1.容器类控件
1.1 Group Box
使⽤ QGroupBox 实现⼀个带有标题的分组框. 可以把其他的控件放到⾥⾯作为⼀组. 这样看起来能更好看⼀点.
📌 注意, 不要把
QGroupBox和QButtonGroup混淆. (之前在介绍QRadionButton的时候提到了QButtonGroup).可以把其他的各种控件放到
GroupBox中,这些内部控件的父元素就不是this了,而是这个GroupBox.

核⼼属性
| 属性 | 说明 |
|---|---|
title |
分组框的标题 |
alignment |
分组框内部内容的对齐方式 |
flat |
是否是"扁平"模式 |
checkable |
是否可选择。设为 true,则在 title 前方会多出一个可勾选的部分 |
checked |
描述分组框的选择状态(前提是 checkable 为 true) |
🌰 分组框只是⼀个⽤来 "美化界⾯" 的组件, 并不涉及到⽤⼾交互和业务逻辑. 属于 "锦上添花" .
代码⽰例: 给⻨当劳案例加上分组框
- 在界⾯上创建三个分组框, 并且在分组框内部创建下拉框和微调框.
🏆 注意:在复制粘贴控件的时候, ⼀定要先选中对应的⽗控件, 再粘贴.

- 编写
widget.cpp, 添加初始化下拉框的代码
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->comboBox->addItem("巨无霸");
ui->comboBox->addItem("麦辣鸡腿堡");
ui->comboBox_2->addItem("薯条");
ui->comboBox_2->addItem("麦辣鸡翅");
ui->comboBox_3->addItem("可乐");
ui->comboBox_3->addItem("雪碧");
ui->spinBox->setRange(1,10);
ui->spinBox->setValue(1);//默认为一份
ui->spinBox_2->setRange(1,10);
ui->spinBox_2->setValue(1);//默认为一份
ui->spinBox_3->setRange(1,10);
ui->spinBox_3->setValue(1);//默认为一份
}
Widget::~Widget()
{
delete ui;
}
- 运⾏程序, 观察效果

1.2 Tab Widget
📌**说明:**使⽤
QTabWidget实现⼀个带有标签⻚的控件, 可以往⾥⾯添加⼀些widget. 进⼀步的就可以通标签⻚来切换.核⼼属性
| 属性 | 说明 |
|---|---|
tabPosition |
标签页所在的位置: - North 上方 - South 下方 - West 左侧 - East 右侧 |
currentIndex |
当前选中了第几个标签页(从 0 开始计算) |
currentTabText |
当前选中的标签页的文本 |
currentTabName |
当前选中的标签页的名字 |
currentTabIcon |
当前选中的标签页的图标 |
currentTabToolTip |
当前选中的标签页的提示信息 |
tabsCloseable |
标签页是否可以关闭 |
movable |
标签页是否可以移动 |
核⼼信号
| 信号 | 说明 |
|---|---|
currentChanged(int) |
在标签页发生切换时触发,参数为被点击的选项卡编号 |
tabBarClicked(int) |
在点击选项卡的标签条的时候触发,参数为被点击的选项卡编号 |
tabBarDoubleClicked(int) |
在双击选项卡的标签条的时候触发,参数为被点击的选项卡编号 |
tabCloseRequest(int) |
在标签页关闭时触发,参数为被关闭的选项卡编号 |
代码⽰例: 使⽤标签⻚管理多组控件
- 在界⾯上创建⼀个
QTabWidget, 和两个按钮.
按钮的objectName为pushButton_add和pushButton_remove

🏠 注意,
•
QTabWidget中的每个标签⻚都是⼀个QWidget• 点击标签⻚, 就可以直接切换.
• 右键
QTabWidget, 可以添加标签⻚或者删除标签⻚.
- 编写
widget.cpp, 进⾏初始化, 给标签⻚中放个简单的label
📌注意新创建的
label的⽗元素, 是ui->tab和ui->tab_2.Qt中使⽤⽗⼦关系决定该控件 "在哪⾥".
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label = new QLabel(ui->tab);//QLabel的初始化参数是其父类
label->setText("标签页1");
label->resize(100,50);
QLabel* label2 = new QLabel(ui->tab);//QLabel的初始化参数是其父类
label->setText("标签页2");
label->resize(100,50);
}
Widget::~Widget()
{
delete ui;
}
- 编写按钮的
slot 函数并编写QTabWidget的currentChanged函数
• 使⽤count()获取到标签⻚的个数.
• 使⽤addTab新增标签⻚.
• 使⽤removeTab删除标签⻚.
• 使⽤currentIndex获取到当前标签⻚的下标.
• 使⽤setCurrentIndex切换当前标签⻚.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QLabel>
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLabel* label = new QLabel(ui->tab);//QLabel的初始化参数是其父类
label->setText("标签页1");
label->resize(100,50);
QLabel* label2 = new QLabel(ui->tab);//QLabel的初始化参数是其父类
label->setText("标签页2");
label->resize(100,50);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//获取当前有几个标签页
int count = ui->tabWidget->count();
//创建新的widget
QWidget* w = new QWidget();
//设置标签页名
ui->tabWidget->addTab(w,QString("Tab ") + QString::number(count+1));
//给widget中添加label,当前label的父类是w
QLabel* label = new QLabel(w);
label->setText(QString("标签页") + QString::number(count+1));
label->resize(100,50);
//选中这个新的标签页
ui->tabWidget->setCurrentIndex(count);
}
void Widget::on_pushButton_2_clicked()
{
//获取当前标签页的下标
int index = ui->tabWidget->currentIndex();
//删除这个标签页
ui->tabWidget->removeTab(index);
}
void Widget::on_tabWidget_currentChanged(int index)
{
qDebug()<<"当前选中标签页为:"<<index;
}
- 运⾏程序, 观察效果
• 点击新建标签⻚, 可以创建出新的标签.
• 点击删除当前标签⻚, 可以删除标签.
• 切换标签⻚时, 可以看到qDebug打印出的标签⻚编号.

📚 2. 布局管理器
之前使⽤ Qt 在界⾯上创建的控件, 都是通过 "绝对定位" 的⽅式来设定的.
也就是每个控件所在的位置, 都需要计算坐标, 最终通过 setGeometry 或者 move ⽅式摆放过去.
这种设定⽅式其实并不⽅便. 尤其是界⾯如果内容⽐较多, 不好计算. ⽽且⼀个窗⼝⼤⼩往往是可以调整的, 按照绝对定位的⽅式, 也⽆法⾃适应窗⼝⼤⼩.
🦄 当然, 布局管理器并⾮ Qt 独有. 其他的 GUI 开发框架, 像 Android, 前端等也### 2.1 垂直布局al `的缩写.
核⼼属性
| 属性 | 说明 |
|---|---|
layoutLeftMargin |
左侧边距 |
layoutRightMargin |
右侧边距 |
layoutTopMargin |
上方边距 |
layoutBottomMargin |
下方边距 |
layoutSpacing |
相邻元素之间的间距 |
🏠注意:
Layout只是⽤于界⾯布局, 并没有提供信号.如果在代码中创建
layout,其实是只创建了一个layout如果在
Qt Designer中创建一个layout,先创了一个Widget然后再在这个新的
Widget中添加了一个layout
代码⽰例: 使⽤ QVBoxLayout 管理多个控件.
- 编写代码, 创建布局管理器和三个按钮. 并且把按钮添加到布局管理器中.
• 使⽤addWidget把控件添加到布局管理器中.
• 使⽤setLayout设置该布局管理器到widget中.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QVBoxLayout>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
//创建布局管理器,并且把按钮添加进去
//如果创建的时候指定父元素为this,则后面不需要setLayout方法了
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
//将布局管理器添加到widget中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
- 运⾏程序, 可以看到此时界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变.
此时三个按钮的尺⼨和位置, 都是⾃动计算出来的.

通过上述代码的⽅式, 只能给这个 widget 设定⼀个布局管理器. 实际上也可以通过 Qt Design 在⼀个窗⼝中创建多个布局管理器.
代码⽰例: 创建两个 QVBoxLayout
- 在界⾯上创建两个
QVBoxLayout, 每个QVBoxLayout各放三个按钮.

- 运⾏程序, 可以看到这些按钮已经⾃动排列好. 只不过当前这些按钮的位置不能随着窗⼝⼤⼩⾃动变化.

🏝️ 通过
Qt Designer创建的布局管理器, 其实是先创建了⼀个widget, 设置过geometry属性的. 再把这个layout设置到这个widget中.实际上, ⼀个
widget只能包含⼀个layout.打开
ui⽂件的原始xml, 可以看到其中的端倪.这种情况下
layout并⾮是窗⼝widget的布局管理器, 因此不会随着窗⼝⼤⼩改变.
xml
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>130</x>
<y>80</y>
<width>160</width>
<height>411</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
2.2 ⽔平布局
使⽤ QHBoxLayout表⽰垂直的布局管理器. H 是 horizontal 的缩写.
核⼼属性 (和 QVBoxLayout 属性是⼀致的)
| 属性 | 说明 |
|---|---|
layoutLeftMargin |
左侧边距 |
layoutRightMargin |
右侧边距 |
layoutTopMargin |
上方边距 |
layoutBottomMargin |
下方边距 |
layoutSpacing |
相邻元素之间的间距 |
代码⽰例: 使⽤ ·QHBoxLayout· 管理控件
- 编写代码, 创建布局管理器和三个按钮. 并且把按钮添加到布局管理器中.
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建三个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
// 创建布局管理器
QHBoxLayout* layout = new QHBoxLayout();
layout->addWidget(btn1);
layout->addWidget(btn2);
layout->addWidget(btn3);
// 设置 layout 到 widget 上
this->setLayout(layout);
}
- 运⾏程序, 可以看到此时界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变.
此时三个按钮的尺⼨和位置, 都是⾃动计算出来的.

Layout ⾥⾯可以再嵌套上其他的 layout, 从⽽达到更复杂的布局效果.
代码⽰例: 嵌套的 layout
- 在代码中创建以下内容
• 使⽤addLayout给layout中添加⼦layout.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QVBoxLayout>
#include<QHBoxLayout>
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个垂直布局器
QVBoxLayout* parent_layout = new QVBoxLayout();
this->setLayout(parent_layout);
//新建2个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
parent_layout->addWidget(btn1);
parent_layout->addWidget(btn2);
//创建子layout
QHBoxLayout* child_layout = new QHBoxLayout();
//添加两个按钮进去
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
child_layout->addWidget(btn3);
child_layout->addWidget(btn4);
//把这个子layout添加到父layout中
parent_layout->addLayout(child_layout);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏程序, 观察结果

✏️笔记:刚刚创建的布局管理器,这里的控件尺寸都是均等的。当需要创建出尺寸不同的控件的时候,就可以通过拉伸系数来设置拉伸系数就相当于设置控件之间尺寸的"比例"
2.3 ⽹格布局
Qt 中还提供了 QGridLayout ⽤来实现⽹格布局的效果. 可以达到M * N的这种⽹格的效果.
核⼼属性
整体和 QVBoxLayout 以及 QHBoxLayout 相似. 但是设置 spacing 的时候是按照垂直⽔平两个⽅向来设置的.
| 属性 | 说明 |
|---|---|
layoutLeftMargin |
左侧边距 |
layoutRightMargin |
右侧边距 |
layoutTopMargin |
上方边距 |
layoutBottomMargin |
下方边距 |
layoutHorizontalSpacing |
相邻元素之间水平方向的间距 |
layoutVerticalSpacing |
相邻元素之间垂直方向的间距 |
layoutRowStretch |
行方向的拉伸系数 |
layoutColumnStretch |
列方向的拉伸系数 |
代码⽰例: 使⽤ QGridLayout 管理元素
- 代码中创建
QGridLayout和 4 个按钮.
• 使⽤addWidget添加控件到布局管理器中. 但是添加的同时会指定两个坐标. 表⽰放在第⼏⾏, 第⼏列.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QGridLayout>
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建4个按钮,并添加到GridLayout中
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,1,0);
layout->addWidget(btn4,1,1);
//设置layout到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏代码, 观察效果. 可以看到当前的这⼏个按钮是按照 2 ⾏ 2 列的⽅式排列的.

- 如果调整⾏列坐标为下列代码
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QGridLayout>
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建4个按钮,并添加到GridLayout中
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,0,2);
layout->addWidget(btn4,0,3);
//设置layout到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏代码, 可以看到这⼏个按钮都在同⼀⾏了. 相当于
QHBoxLayout

- 如果调整⾏列坐标为下列代码
cpp
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 1, 0);
layout->addWidget(btn2, 2, 0);
layout->addWidget(btn3, 3, 0);
layout->addWidget(btn4, 4, 0);
- 执⾏代码, 可以看到这⼏个按钮都在同⼀列了. 相当于
QVBoxLayout

- 任意调整⾏列, 即可看到不同的效果.
cpp
// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 1);
layout->addWidget(btn3, 2, 2);
layout->addWidget(btn4, 3, 3);

- 编写代码形如
cpp
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 0);
layout->addWidget(btn3, 2, 0);
layout->addWidget(btn4, 10, 0);
🥇 此处也要注意, 设置⾏和列的时候, 如果设置的是⼀个很⼤的值, 但是这个值和上⼀个值之间并没有其他的元素, 那么并不会在中间腾出额外的空间.
虽然把btn4设置在第10 ⾏, 但是由于3-9 ⾏没有元素. 因此 btn4 仍然会紧挨在btn3下⽅. 看起来和上⾯的 0 1 2 3 的情况是相同的.

代码⽰例: 设置 QGridLayout 中元素的⼤⼩⽐例.
- 创建
6个按钮, 按照2⾏3列的⽅式排列
• 使⽤setColumnStretch设置每⼀列的拉伸系数
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建6个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,0,2);
layout->addWidget(btn4,1,0);
layout->addWidget(btn5,1,1);
layout->addWidget(btn6,1,2);
//设置拉伸比例 - 三列的比例是1 : 0 : 3
//第0列拉伸比例设为1;
layout->setColumnStretch(0,1);
//第一列拉伸比例设为0,即为固定大小,不参与拉伸
layout->setColumnStretch(1,0);
//第二列拉伸比例设为3,即为第2列的宽度是第0列的3倍
layout->setColumnStretch(2,3);
//设置layout到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏程序, 可以看到每⼀列的宽度是不同的. 并且随着窗⼝调整动态变化.

🏝️另外,
QGridLayout也提供了setRowStretch设置⾏之间的拉伸系数.上述案例中, 直接设置
setRowStretch效果不明显, 因为每个按钮的⾼度是固定的. 需要把按钮的垂直⽅向的sizePolicy属性设置为QSizePolicy::Expanding尽可能填充满布局管理器, 才能看到效果.
代码⽰例: 设置垂直⽅向的拉伸系数
- 编写代码, 创建 6 个按钮, 按照 3 ⾏ 2 列⽅式排列.
使⽤setSizePolicy设置按钮的尺⼨策略. 可选的值如下:
• QSizePolicy::Ignored : 忽略控件的尺⼨,不对布局产⽣影响。
• QSizePolicy::Minimum : 控件的最⼩尺⼨为固定值,布局时不会超过该值。
• QSizePolicy::Maximum : 控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
• QSizePolicy::Preferred : 控件的理想尺⼨为固定值,布局时会尽量接近该值。
• QSizePolicy::Expanding : 控件的尺⼨可以根据空间调整,尽可能占据更多空间。
• QSizePolicy::Shrinking : 控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建6个按钮
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
QPushButton* btn3 = new QPushButton("按钮3");
QPushButton* btn4 = new QPushButton("按钮4");
QPushButton* btn5 = new QPushButton("按钮5");
QPushButton* btn6 = new QPushButton("按钮6");
//设置按钮的sizePolicy,此时按钮的水平方向和垂直方向都会尽量舒展开
btn1->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn2->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn3->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn4->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn5->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
btn6->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
//创建网格布局管理器,并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1,0,0);
layout->addWidget(btn2,0,1);
layout->addWidget(btn3,1,0);
layout->addWidget(btn4,1,1);
layout->addWidget(btn5,2,0);
layout->addWidget(btn6,2,1);
//设置拉伸比例
//第0行拉伸比例设为1
layout->setRowStretch(0,1);
//第1行拉伸比例设为1
layout->setRowStretch(1,1);
//第2行拉伸比例设为3,即为第2行的宽度(垂直方向)是第0行的3倍
layout->setRowStretch(2,3);
//设置layout到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏代码, 观察效果.
此时的按钮垂直⽅向都舒展开了. 并且调整窗⼝尺⼨, 也会按照设定的⽐例同步变化.

总的来说, 使⽤QGridLayout能够代替很多QHBoxLayout和QVBoxLayout嵌套的场景. 毕竟嵌套的代码写起来是⽐较⿇烦的.
另外不要忘了,
QGridLayout⾥⾯也能嵌套QHBoxLayout和QVBoxLayout,
QHBoxLayout和QVBoxLayout⾥⾯也能嵌套QGridLayout.灵活使⽤上述布局管理器, 就可以实现出任意的复杂界⾯.
2.4 表单布局
除了上述的布局管理器之外, Qt 还提供了 QFormLayout , 属于是QGridLayout的特殊情况, 专
⻔⽤于实现两列表单的布局.
这种表单布局多⽤于让⽤⼾填写信息的场景. 左侧列为提⽰, 右侧列为输⼊框.
代码⽰例: 使⽤ QFormLayout 创建表单.
- 编写代码, 创建
QFormLayout, 以及三个label和三个lineEdit
• 使⽤addRow⽅法来添加⼀⾏. 每⾏包含两个控件. 第⼀个控件固定是QLabel / ⽂本, 第⼆个控件则可以是任意控件.
• 如果把第⼀个参数填写为NULL, 则什么都不显⽰.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QFormLayout>
#include<QLineEdit>
#include<QLabel>
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//1.创建一个FormLayout
QFormLayout* layout = new QFormLayout(this);
//创建三个label
QLabel* label1 = new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");
//创建三个lineEdit
QLineEdit* lineEdit1 = new QLineEdit();
QLineEdit* lineEdit2 = new QLineEdit();
QLineEdit* lineEdit3 = new QLineEdit();
//创建一个提交按钮
QPushButton* commit = new QPushButton("提交");
//把上述元素添加到layout中
layout->addRow(label1,lineEdit1);
layout->addRow(label2,lineEdit2);
layout->addRow(label3,lineEdit3);
layout->addRow(NULL,commit);
}
Widget::~Widget()
{
delete ui;
}
- 执⾏程序, 可以看到以下结果.

2.5 Spacer
使⽤布局管理器的时候, 可能需要在控件之间, 添加⼀段空⽩. 就可以使⽤ QSpacerItem 来表⽰.
核⼼属性

代码⽰例: 创建⼀组左右排列的按钮.
- 在界⾯上创建⼀个
QVBoxLayout, 并添加两个按钮.
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QHBoxLayout>
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个水平布局器并添加两个按钮
QHBoxLayout* layout = new QHBoxLayout(this);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
layout->addWidget(btn1);
layout->addWidget(btn2);
}
Widget::~Widget()
{
delete ui;
}
-
直接运⾏程序, 可以看到两个按钮是紧挨着的.

-
在两个按钮中间添加⼀个
spacer
cpp
#include "widget.h"
#include "ui_widget.h"
#include<QHBoxLayout>
#include<QPushButton>
#include<QSpacerItem>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个水平布局器
QHBoxLayout* layout = new QHBoxLayout(this);
QPushButton* btn1 = new QPushButton("按钮1");
QPushButton* btn2 = new QPushButton("按钮2");
//创建一个SpacerItem
QSpacerItem* spacer = new QSpacerItem(200,20);
layout->addWidget(btn1);
//在两个widget中间添加空白
layout->addSpacerItem(spacer);
layout->addWidget(btn2);
}
Widget::~Widget()
{
delete ui;
}
- 运⾏程序, 观察代码效果. 可以看到两个按钮之间已经存在了间隔了.
调整QSpacerItem不同的尺⼨, 即可看到不同的间距.

在
Qt Designer中, 也可以直接给界⾯上添加spacer.
📝 3.小结
本文主要介绍了 Qt 中两类重要的界面组织工具:容器类控件 和 布局管理器。
容器类控件 用于把多个控件组织在一起,形成逻辑上的分组:
QGroupBox:带标题的分组框,用于美化界面、把相关控件归为一组,不涉及业务逻辑。QTabWidget:带标签页的控件,每个标签页都是一个QWidget,通过切换标签页来展示不同的内容,并提供了丰富的信号(如currentChanged、tabBarClicked等)。
布局管理器 用于自动计算控件的位置和尺寸,让界面能够自适应窗口大小变化:
QVBoxLayout:垂直布局,控件从上到下排列。QHBoxLayout:水平布局,控件从左到右排列。QGridLayout:网格布局,按M × N的行列方式排列,可设置行列拉伸系数。QFormLayout:表单布局,专用于两列表单(左侧提示、右侧输入)。QSpacerItem:在控件之间添加空白间隔。
核心要点:
- 一个
widget只能包含一个layout。 - 布局管理器可以互相嵌套(
addLayout),实现复杂界面。 - 通过
setColumnStretch/setRowStretch设置拉伸比例,配合sizePolicy实现控件尺寸的动态调整。 - 使用布局管理器后,控件的位置和尺寸由系统自动计算,无需手动设置坐标。
🎯 4.经典面试题
1. QGroupBox 和 QButtonGroup 有什么区别?
答案: QGroupBox 是一个带标题的分组框,用于在界面上把相关控件组织在一起,起到美化界面的作用,本身不涉及业务逻辑。而 QButtonGroup 是一个逻辑分组工具,用于管理一组互斥的按钮(如单选按钮),它不负责界面显示,只负责按钮之间的互斥逻辑。
2. QTabWidget 中如何动态添加和删除标签页?
答案: 使用 addTab(widget, title) 方法可以新增标签页,其中第一个参数是要添加的 QWidget,第二个参数是标签页的标题;使用 removeTab(index) 方法可以删除指定下标的标签页。可以通过 count() 获取当前标签页的总数,通过 currentIndex() 获取当前选中的标签页下标。
3. QTabWidget 的 currentChanged(int) 信号在什么时候触发?
答案: 当用户切换标签页时触发,参数 int 表示被切换到的标签页的编号(从 0 开始)。可以通过连接该信号来响应标签页的切换事件,例如更新界面状态或加载对应页面的数据。
4. 什么是布局管理器?为什么需要它?
答案: 布局管理器是 Qt 中用于自动管理控件位置和尺寸的机制。在没有布局管理器时,控件需要通过绝对定位(setGeometry 或 move)手动设置坐标,这种方式在窗口大小变化时无法自适应。布局管理器可以根据窗口大小自动调整控件的位置和尺寸,使界面更加灵活和美观。
5. QVBoxLayout 和 QHBoxLayout 有什么区别?
答案: QVBoxLayout 是垂直布局管理器,控件从上到下依次排列;QHBoxLayout 是水平布局管理器,控件从左到右依次排列。两者的核心属性(如边距、间距)是一致的,只是排列方向不同。
6. 一个 widget 可以设置多个布局管理器吗?
答案: 不可以。一个 widget 只能包含一个布局管理器。如果需要在一个窗口中实现多个布局区域,可以通过嵌套布局的方式实现,即在一个布局管理器中再添加子布局管理器(addLayout),或者使用 QGridLayout 来划分多个区域。
7. QGridLayout 中如何设置不同列或行的宽度/高度比例?
答案: 使用 setColumnStretch(column, stretch) 方法设置某一列的拉伸系数,使用 setRowStretch(row, stretch) 方法设置某一行的拉伸系数。拉伸系数表示该列/行在窗口缩放时占据空间的比例。例如,设置第 0 列拉伸系数为 1、第 2 列拉伸系数为 3,则第 2 列的宽度是第 0 列的 3 倍。
8. QSizePolicy::Expanding 的作用是什么?
答案: QSizePolicy::Expanding 表示控件的尺寸可以根据可用空间进行调整,尽可能占据更多的空间。当配合布局管理器的拉伸系数使用时,可以让控件在窗口缩放时动态地填充剩余空间,实现自适应布局。
9. QFormLayout 适用于什么场景?如何使用?
答案: QFormLayout 适用于两列表单布局的场景,例如用户填写信息的界面。左侧列通常放置提示文本(QLabel),右侧列放置输入控件(如 QLineEdit)。使用 addRow(label, field) 方法添加一行,其中第一个参数是提示文本,第二个参数是输入控件;如果第一个参数传 NULL,则该行只显示右侧控件。
10. QSpacerItem 的作用是什么?如何创建?
答案: QSpacerItem 用于在布局管理器的控件之间添加一段空白间隔,起到分隔控件的作用。可以通过 new QSpacerItem(width, height) 创建,然后使用 addSpacerItem(spacer) 方法将其添加到布局管理器中。调整其宽高参数即可控制间隔的大小。
