4. 布局管理器:QHBoxLayout、QVBoxLayout、QGridLayout、QFormLayout、嵌套布局实战
布局管理器,说白了就是帮我们自动排列子控件的工具。你想想看,如果每个按钮、每个输入框都要手动算坐标,那窗口一拉伸就全乱套了。Qt 提供了几种非常实用的布局器,这一章我们一个一个来过一遍。
4.1 QHBoxLayout:水平排列
QHBoxLayout 会把子控件从左到右排成一行。我在做文件管理器工具栏的时候,就经常用它来放“新建文件夹”、“删除”、“刷新”这几个按钮。
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addWidget(btnNew);
hLayout->addWidget(btnDelete);
hLayout->addWidget(btnRefresh);
hLayout->addStretch(); // 添加弹性空间,把按钮挤到左边
setLayout(hLayout);
小技巧:addStretch() 可以加在任意位置,用来控制控件之间的间距。加在末尾,按钮就靠左;加在开头,按钮就靠右。
4.2 QVBoxLayout:垂直排列
QVBoxLayout 和水平布局正好相反,控件从上往下排。我习惯用它来做属性面板,比如文件路径、文件大小、修改日期这些信息,一列排下来,清晰又整齐。
QVBoxLayout *vLayout = new QVBoxLayout;
vLayout->addWidget(labelPath);
vLayout->addWidget(labelSize);
vLayout->addWidget(labelDate);
vLayout->addStretch();
setLayout(vLayout);
4.3 QGridLayout:网格排列
QGridLayout 把窗口分成行和列,你可以指定控件放在第几行第几列。它比 QHBoxLayout 和 QVBoxLayout 灵活得多,适合做表单、计算器、设置界面这类需要对齐的场景。
QGridLayout *grid = new QGridLayout;
grid->addWidget(labelName, 0, 0); // 第0行第0列
grid->addWidget(editName, 0, 1); // 第0行第1列
grid->addWidget(labelPass, 1, 0);
grid->addWidget(editPass, 1, 1);
grid->addWidget(btnLogin, 2, 0, 1, 2); // 跨两列
setLayout(grid);
关键点:addWidget 的第三个和第四个参数是行和列的起始位置,第五个和第六个参数是跨几行几列。比如 (2, 0, 1, 2) 表示从第2行第0列开始,占1行2列。
4.4 QFormLayout:表单布局
QFormLayout 是专门为“标签+输入框”这种表单场景设计的。它自动把标签放在左边,输入框放在右边,对齐得整整齐齐。我曾经用它做过用户注册界面,省了不少对齐的功夫。
QFormLayout *form = new QFormLayout;
form->addRow("用户名:", editUser);
form->addRow("密 码:", editPass);
form->addRow("邮 箱:", editEmail);
setLayout(form);
注意:QFormLayout 的标签默认是右对齐的,如果你想要左对齐,可以调用 form->setLabelAlignment(Qt::AlignLeft)。
4.5 嵌套布局实战
实际项目中,很少只用一种布局。大多数情况下,我们需要把多种布局嵌套起来用。比如一个文件管理器的界面,左边是树形目录(垂直布局),右边是文件列表(网格布局),顶部是工具栏(水平布局)。
嵌套布局的核心思路是:先拆分成大块,再细化每一块。我一般会先画一个草图,把界面分成几个大区域,然后每个区域用不同的布局器实现。
// 主布局:水平分割
QHBoxLayout *mainLayout = new QHBoxLayout;
// 左侧:垂直布局
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addWidget(treeWidget);
leftLayout->addStretch();
// 右侧:垂直布局
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(toolBar); // 工具栏
rightLayout->addWidget(tableWidget); // 文件列表
// 组装
mainLayout->addLayout(leftLayout, 1); // 左侧占1份
mainLayout->addLayout(rightLayout, 3); // 右侧占3份
setLayout(mainLayout);
经验之谈:addLayout 的第二个参数是拉伸因子(stretch factor)。数值越大,该区域在窗口拉伸时占的空间就越多。我通常把主工作区设为 3,侧边栏设为 1,这样看起来比较舒服。
4.6 布局管理器对比
| 布局类型 | 排列方向 | 适用场景 | 常用方法 |
|---|---|---|---|
| QHBoxLayout | 水平 | 工具栏、按钮组 | addWidget, addStretch, addSpacing |
| QVBoxLayout | 垂直 | 属性面板、列表 | addWidget, addStretch, addSpacing |
| QGridLayout | 网格 | 计算器、设置界面 | addWidget(row, col), addLayout |
| QFormLayout | 表单 | 登录、注册、配置 | addRow(label, widget) |
4.7 布局管理器核心逻辑图
下面这张图展示了四种布局管理器的核心逻辑和嵌套关系,帮你快速理解它们各自的特点。
4.8 避坑指南
- 布局嵌套过深:我曾经在一个项目里嵌套了五六层布局,结果窗口拉伸时卡得不行。后来发现,三层以内基本够用,超过三层就该考虑用 QSplitter 或者 QStackedWidget 了。
- 忘记设置父对象:布局管理器本身不需要设置父对象,但里面的控件一定要有父对象,否则会内存泄漏。我习惯在 new 控件时直接传 this。
- 布局和固定大小冲突:如果你给控件设置了固定大小(setFixedSize),布局管理器就没办法自动调整了。嗯,这里要注意,要么用布局,要么用固定大小,别混着来。
总结:布局管理器是 Qt 界面开发的基石。QHBoxLayout 和 QVBoxLayout 是最基础的,QGridLayout 适合网格场景,QFormLayout 是表单神器。嵌套布局时,先画草图、再拆大块、最后细化,这样写出来的代码既清晰又容易维护。