第五章 布局管理器:让界面自动适应
布局管理器,说白了就是帮你自动排列控件的工具。我刚开始学Qt时,习惯把所有控件都用setGeometry()硬编码位置,结果窗口一拉伸,界面就乱成一锅粥。后来才明白——布局管理器才是正经解决方案。
5.1 为什么需要布局管理器?
你想想看,如果用户把窗口拉大了,按钮还挤在左上角,是不是很丑?布局管理器能自动计算每个控件的位置和大小。窗口变了,它跟着变。
我个人习惯是:能用布局,绝不用绝对定位。除非你做的不是标准窗口,比如绘图板那种自由画布。
核心思想:布局管理器 = 自动化的位置计算器。你只管把控件丢进去,它帮你排好。
5.2 四大布局管理器
Qt提供了五种常用布局,但最核心的是这四个:
| 布局类 | 排列方式 | 适用场景 |
|---|---|---|
| QHBoxLayout | 水平排列 | 一行按钮、工具栏 |
| QVBoxLayout | 垂直排列 | 表单、设置面板 |
| QGridLayout | 网格排列 | 计算器、表格输入 |
| QFormLayout | 表单排列 | 标签+输入框 |
5.3 QHBoxLayout:水平布局
最简单的布局,控件从左到右排成一行。
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
setLayout(layout);
嗯,这里要注意:addWidget()是按添加顺序排列的。你加的顺序就是显示的顺序。
小技巧:如果想让按钮靠右对齐,可以在前面加一个addStretch()。它会吃掉多余的空间。
5.4 QVBoxLayout:垂直布局
和水平布局对称,只是方向变成从上到下。
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(label);
layout->addWidget(edit);
layout->addWidget(button);
setLayout(layout);
我在项目中遇到过一个问题:三个控件垂直排列,我希望按钮在最下面,标签和输入框在上面平分空间。怎么办?用addStretch()加伸缩因子。
5.5 QGridLayout:网格布局
这个就厉害了。你可以把界面想象成一个Excel表格,控件放在指定的行和列。
QGridLayout *layout = new QGridLayout;
layout->addWidget(labelName, 0, 0); // 第0行第0列
layout->addWidget(editName, 0, 1); // 第0行第1列
layout->addWidget(labelAge, 1, 0); // 第1行第0列
layout->addWidget(editAge, 1, 1); // 第1行第1列
layout->addWidget(buttonOK, 2, 0, 1, 2); // 跨两列
setLayout(layout);
注意最后一个参数:addWidget(widget, row, col, rowSpan, colSpan)。rowSpan和colSpan表示控件跨越几行几列。这个参数很容易忘,我刚开始就经常漏掉。
避坑指南:我曾经在网格布局里放了一个跨多列的控件,结果其他列被挤没了。后来发现是忘了设置列的最小宽度。记得用setColumnMinimumWidth()保底。
5.6 QFormLayout:表单布局
专门为「标签+输入框」这种模式设计的。两列布局,左列是标签,右列是输入控件。
QFormLayout *layout = new QFormLayout;
layout->addRow("姓名:", new QLineEdit);
layout->addRow("年龄:", new QSpinBox);
layout->addRow("邮箱:", new QLineEdit);
setLayout(layout);
你看,代码比QGridLayout简洁多了。而且它自动处理标签对齐,省心。
我个人习惯用QFormLayout做设置对话框。用户看到标签和输入框对齐,视觉上很舒服。
5.7 布局嵌套与伸缩因子
实际项目中,很少只用一种布局。更多时候是布局套布局。
比如一个典型的对话框:左边是垂直排列的输入区,右边是垂直排列的按钮区。怎么做?
// 左边:输入区
QVBoxLayout *leftLayout = new QVBoxLayout;
leftLayout->addWidget(nameEdit);
leftLayout->addWidget(ageEdit);
// 右边:按钮区
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(okButton);
rightLayout->addWidget(cancelButton);
// 外层:水平布局
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
伸缩因子(stretch factor)控制空间分配比例。比如:
mainLayout->addLayout(leftLayout, 3); // 占3份
mainLayout->addLayout(rightLayout, 1); // 占1份
这样左边占75%,右边占25%。窗口拉大,比例不变。
经验之谈:伸缩因子用整数,比例越简单越好。3:1比7:3好记,也更容易调试。
5.8 布局策略
每个控件都有sizePolicy属性,告诉布局管理器「我该怎么伸缩」。
| 策略 | 含义 | 典型控件 |
|---|---|---|
| Fixed | 固定大小,不伸缩 | 标签、按钮 |
| Minimum | 可以缩小到最小尺寸 | 输入框 |
| Expanding | 尽量占满多余空间 | 文本编辑框 |
| Preferred | 首选大小,可伸缩 | 大多数控件 |
QPushButton *btn = new QPushButton("确定");
btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// 水平方向固定,垂直方向固定
嗯,这里有个坑:如果你把按钮设成Expanding,它会变得很大,很难看。按钮一般用Fixed或Preferred就够了。
建议:文本编辑框、列表这类需要展示大量内容的控件,用Expanding。按钮、标签这种,用Fixed或Preferred。
5.9 知识体系总览
下面这张图总结了本章的核心内容:
5.10 避坑总结
最后,分享几个我踩过的坑:
- 布局嵌套太深:我曾经嵌套了五六层布局,结果界面卡顿。后来发现三层以内就能解决大部分问题。
- 忘记设置边距:默认边距是0,控件会贴在一起。记得用
setContentsMargins()加一点间距。 - 伸缩因子乱用:所有控件都设成Expanding,结果界面像被撑爆了。该固定的就固定。
- 布局和setGeometry混用:这两个是冲突的。用了布局,就别再手动设位置了。
重要提醒:布局管理器只能管理直接子控件。如果你把控件放在QGroupBox里,布局只能管到QGroupBox,管不到里面的控件。里面的控件需要单独设置布局。
好了,布局管理器就讲到这里。记住一句话:布局是界面的骨架。骨架搭好了,界面怎么变都稳。