布局管理系统
Qt 的布局管理系统为控件的排版提供了强大而灵活的支持。它通过自动调整控件的大小和位置来帮助开发者创建响应式的界面。Qt 布局系统的核心是通过继承自 QLayout 类的布局管理器实现的。
1、布局管理器
常用的布局管理器有 QHBoxLayout、QVBoxLayout、QGridLayout 和 QFormLayout 等。

基本布局管理器(QBoxLayout)
基本布局管理器 QBoxLayout 类可以使子部件在水平方向或者垂直方向排成一列,它将所有的空间分成一行盒子,然后将每个部件放入一个盒子中。
QHBoxLayout 水平布局管理器和 QVBoxLayout 垂直布局管理器
代码实现:
#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle(tr("基本布局管理器"));
QVBoxLayout *layout = new QVBoxLayout;
// 上部分使用水平布局
QHBoxLayout *hLayout = new QHBoxLayout; // 新建水平布局管理器
QPushButton *lBtn = new QPushButton("左面控件");
QPushButton *rBtn = new QPushButton("右面控件");
hLayout->addWidget(lBtn);
hLayout->addWidget(rBtn);
hLayout->setSpacing(20); // 设置部件的间隔
hLayout->setContentsMargins(0, 0, 50, 100); // 设置布局管理器到边界的距离 左 上 右 下
// 下部分使用垂直布局
QVBoxLayout *vLayout = new QVBoxLayout; // 新建水平布局管理器
QPushButton *tBtn = new QPushButton("上面控件");
QPushButton *bBtn = new QPushButton("下面控件");
vLayout->addWidget(tBtn);
vLayout->addWidget(bBtn);
vLayout->setSpacing(20); // 设置部件的间隔
vLayout->setContentsMargins(0, 0, 50, 100); // 设置布局管理器到边界的距离 左 上 右 下
layout->addLayout(hLayout);
layout->addLayout(vLayout);
setLayout(layout);
}
};

栅格布局管理器(QGridLayout)
QGridLayout 类使部件在网格中进行布局,它将所有的空间分隔成一些行和列,行和列的交叉形成了单元格,然后将部件放入一个确定的单元格中。
代码实现:
#include <QWidget>
#include <QPushButton>
#include <QGridLayout>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle(tr("栅格布局管理器"));
QGridLayout *layout = new QGridLayout();
QPushButton *btn1_1 = new QPushButton("第一行第一列");
QPushButton *btn1_2 = new QPushButton("第一行第二列");
QPushButton *btn2_1 = new QPushButton("第二行第一列");
QPushButton *btn2_2 = new QPushButton("第二行第二列");
// 添加部件,从第0行0列开始,占据1行1列
layout->addWidget(btn1_1, 0, 0, 1, 1);
// 添加部件,从第0行1列开始,占据1行2列
layout->addWidget(btn1_2, 0, 1, 1, 2);
// 添加部件,从第1行0列开始,占据1行2列
layout->addWidget(btn2_1, 1, 0, 1, 2);
// 添加部件,从第1行2列开始,占据1行1列
layout->addWidget(btn2_2, 1, 2, 1, 1);
setLayout(layout);
}
};
注意:当部件加入到一个布局管理器中,然后将这个布局管理器再放到一个窗口部件上时,这个布局管理器以及它包含的所有部件都会自动重新定义自己的父对象(parent)为这个窗口部件,所以在创建布局管理器和其中的部件时并不用指定父部件。

窗口布局管理器(QFormLayout)
QFormLayout 是一种非常适合创建表单界面的布局管理器。它能帮助开发者快速地将标签和输入控件按行排列,并自动处理控件的对齐、间距和大小调整。它特别适合用于信息录入的界面,比如用户注册、设置界面、填写表单等场景。
举例说明:
我们分别使用 QFormLayout 与 QGridLayout 来创建一个登陆界面,如图所示:

QFormLayout
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QFormLayout>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle("窗口布局管理器");
QFormLayout *layout = new QFormLayout();
layout->addRow(tr("用户名:"), new QLineEdit);
layout->addRow(tr("密码:"), new QLineEdit);
layout->addRow(new QPushButton("确定"));
layout->setSpacing(10);
layout->setMargin(10);
setLayout(layout);
}
};
QGridLayout
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle("栅格布局管理器");
QGridLayout *layout = new QGridLayout();
layout->addWidget(new QLabel("用户名:"), 0, 0, 1, 1);
layout->addWidget(new QLineEdit, 0, 1, 1, 2);
layout->addWidget(new QLabel("密码:"), 1, 0, 1, 1);
layout->addWidget(new QLineEdit, 1, 1, 1, 2);
layout->addWidget(new QPushButton("确定"), 2, 0, 1, 3);
layout->setSpacing(10);
layout->setMargin(10);
setLayout(layout);
}
};
此时你会发现栅格布局的代码会比窗口布局多一点,不过还可以接受,但是当你对界面进行拉伸的时候就会有对比!
窗口布局拉伸效果展示:

栅格布局拉伸效果展示:

此时,你会发现使用栅格布局创建的界面中左侧的标签被拉伸了,显然不符合我们的预期。这样就能看出在创建表单界面时窗口布局的优势了!
综合使用布局管理器
前面讲到的3种布局管理器,真正使用时一般是将它们综合起来应用,大家可以自己动手尝试一下!
2、设置部件大小
在 Qt 中,布局管理器(QLayout)以及控件的大小控制机制是非常重要的概念,它们直接影响控件在界面中的排列和大小调整。为了更好地理解这些概念,我们需要从几个重要的方面来进行探讨:sizeHint、minimumSizeHint、QSizePolicy、伸缩因子(stretch factor)以及布局管理器的相关属性。
sizeHint 和 minimumSizeHint
- sizeHint: 是一个建议的大小,告诉布局管理器部件在理想情况下的推荐尺寸。它基于部件的内容以及其它因素自动计算得出。
- minimumSizeHint:是控件的最小推荐大小。当布局管理器将控件的大小缩小时,它不会小于 minimumSizeHint 的返回值。
QSizePolicy
QSizePolicy是控制控件在布局管理器中如何伸缩的关键属性。通过 QSizePolicy,你可以定义控件在布局中的大小行为(水平和垂直方向)。每个控件都有一个 QSizePolicy,它包括水平(Horizontal)和垂直(Vertical)两个方向的策略。

可以看到,大小策略与 SizeHint() 的值息息相关。对于布局管理器来说,大小策略对于布局效果也起到了很重要的作用。
伸缩因子(stretch factor)
伸缩因子是指在布局中,当空间变动时,不同控件会按照一定的比例来分配可用空间。通常用于 QHBoxLayout 或 QVBoxLayout 中,它定义了布局管理器如何分配控件之间的剩余空间。
例如,如果你有两个控件,想让左侧控件的宽度是右侧控件宽度的两倍,可以通过设置伸缩因子来实现:

布局管理器中的属性
布局间距(Spacing)
- setSpacing():设置控件之间的间距,默认为6像素。通过修改此属性,可以调整布局中控件之间的空间。
QHBoxLayout *layout = new QHBoxLayout;
layout->setSpacing(10); // 设置控件之间的间距为10像素
布局边距(Margins)
- setContentsMargins():设置布局的边距,即布局与父控件之间的空间。通常用于调整布局边界的间距。
QVBoxLayout *layout = new QVBoxLayout;
layout->setContentsMargins(10, 10, 10, 10); // 设置四个方向的边距为10像素
大小约束(Size Contraints)

可扩展窗口
一个窗口可能有很多选项是扩充的,只有在必要的时候才显示出来,这时就可以使用一个按钮来隐藏或者显示多余的内容,就是所谓的可扩展窗口。要实现可扩展窗口,就要得力于布局管理器的特性,那就是当子部件隐藏时,布局管理器自动缩小,当子部件重新显示时,布局管理器再放大。
举例说明:
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QFormLayout>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle(tr("可扩展窗口"));
// 上部分使用水平布局
QHBoxLayout *hLayout = new QHBoxLayout; // 新建水平布局管理器
QPushButton *lBtn = new QPushButton("隐藏可扩展窗口");
QPushButton *rBtn = new QPushButton("可扩展窗口");
hLayout->addWidget(lBtn, 2); // 设置伸缩因子为2
hLayout->addWidget(rBtn, 1); // 设置伸缩因子为1
hLayout->setSpacing(20); // 设置部件的间隔
hLayout->setContentsMargins(0, 0, 50, 100); // 设置布局管理器到边界的距离 左 上 右 下
setLayout(hLayout);
connect(lBtn, &QPushButton::clicked, [=]() {
if (rBtn->isHidden()) {
rBtn->show();
lBtn->setText("隐藏可扩展窗口");
} else {
rBtn->hide();
lBtn->setText("显示可扩展窗口");
}
});
}
};
结果展示:

点击"隐藏可扩展窗口"按钮,实现"可扩展窗口"按钮的显示与隐藏。

分裂器(QSplitter)
分裂器 QSplitter 与 QBoxLayout 类似,可以完成布局管理器的功能,但是包含在它里面的部件,默认是可以随着分裂器的大小变化而变化的。
举例说明:
#include <QSplitter>
#include <QTextEdit>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr)
: QWidget(parent)
{
setWindowTitle("分割器");
// 创建一个垂直的 QSplitter
QSplitter *splitter = new QSplitter(Qt::Horizontal, this);
// 创建控件
QTextEdit *textEdit1 = new QTextEdit("第一个控件", this);
QTextEdit *textEdit2 = new QTextEdit("第二个控件", this);
QLabel *label = new QLabel("第三个控件", this);
// 设置控件到 splitter 中
splitter->addWidget(textEdit1);
splitter->addWidget(textEdit2);
splitter->addWidget(label);
// 设置初始大小比例
splitter->setStretchFactor(0, 2); // 第一个控件占用两倍的伸展因子
splitter->setStretchFactor(1, 1); // 第二个控件占用一倍的伸展因子
splitter->setStretchFactor(2, 1); // 第三个控件占用一倍的伸展因子
// 设置分隔条样式
splitter->setHandleWidth(8); // 设置分隔条的宽度
// 添加按钮到主布局
QPushButton *button = new QPushButton("方向切换", this);
// 创建主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(splitter);
mainLayout->addWidget(button);
setLayout(mainLayout);
// 切换方向的按钮功能
connect(button, &QPushButton::clicked, this, [=]() {
if (splitter->orientation() == Qt::Horizontal) {
splitter->setOrientation(Qt::Vertical); // 切换为垂直方向
} else {
splitter->setOrientation(Qt::Horizontal); // 切换为水平方向
}
});
}
};
结果展示:

方向切换后结果:

Qt布局管理系统详解
3900

被折叠的 条评论
为什么被折叠?



