Qt 布局管理器

本文详细介绍了如何在Qt中使用布局管理器(如QVBoxLayout、QHBoxLayout、QGridLayout和QFormLayout)来管理和调整界面控件的位置,包括垂直和水平布局、网格布局以及表单布局,以及如何实现控件的自动适应窗口大小和间距设置。
摘要由CSDN通过智能技术生成

之前使用 Qt 在界面上创建的控件,都是通过“绝对定位”的方式来设定的

也就是每个控件所在的位置,都需要计算坐标,最终通过 setGeometry 或者 move 方式摆放过去

这种设定方式其实并不方便,尤其是界面如果内容比较多,不好计算。而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小。

因此 Qt 引入“布局管理器”(Layout)机制,来解决上述问题

垂直布局

使用 QVBoxLayout 表示垂直的布局管理器. V 是 vertical 的缩写.

核心属性

属性说明
layoutLeftMargin左侧边距
layoutRightMargin右侧边距
layoutTopMargin上方边距
layoutBottomMargin下方边距
layoutSpacing相邻元素之间的间距

Layout 只是用于界面布局,并没有提供信号

代码示例: 使用 QVBoxLayout 管理多个控件

(1) 编写代码,创建布局管理器和三个按钮并且把按钮添加到布局管理器中.

  • 使用 addWidget 把控件添加到布局管理器中.
  • 使用 setLayout 设置该布局管理器到 widget 中.
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);
}

(2) 运行程序, 可以看到此时界面上的按钮就存在于布局管理器中. 随着窗口尺寸变化而发生改变.

在这里插入图片描述

此时三个按钮的尺寸和位置, 都是自动计算出来的.

通过上述代码的方式,只能给这个 widget 设定一个布局管理器。实际上也可以通过 Qt Design 在一个窗口中创建多个布局管理器.

代码示例: 创建两个 QVBoxLayout

(1) 在界面上创建两个 QVBoxLayout ,每个 QVBoxLayout 各放三个按钮

在这里插入图片描述

(2) 运行程序, 可以看到这些按钮已经自动排列好. 只不过当前这些按钮的位置不能随着窗口大小自动变化

在这里插入图片描述

通过 Qt Designer 创建的布局管理器,其实是先创建了一个 widget,设置过 geometry 属性的。再把这个 layout 设置到这个 widget 中.

实际上,一个 widget 只能包含一个layout

打开 ui 文件的原始 xml,可以看到其中的端倪

这种情况下 layout 并非是窗口 widget 的布局管理器,因此不会随着窗口大小改变

<widget class="QWidget" name="verticalLayoutWidget">
    <property name="geometry">
        <rect>
            <x>140</x>
            <y>140</y>
            <width>141</width>
            <height>331</height>
        </rect>
    </property>
    <layout class="QVBoxLayout" name="verticalLayout">
        <item>
            <widget class="QPushButton" name="pushButton_3">
                <property name="text">
                    <string>PushButton</string>
                </property>
            </widget>
        </item>
        <item>
            <widget class="QPushButton" name="pushButton_2">
                <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>

水平布局

使用 QHBoxLayout 表示垂直的布局管理器.H是 horizontal 的缩写!

核心属性 (和 QVBoxLayout 属性是一致的)

代码示例: 使用 QHBoxLayout 管理控件

(1) 编写代码创建布局管理器和三个按钮并且把按钮添加到布局管理器中

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);
}

(2) 运行程序,可以看到此时界面上的按钮就存在于布局管理器中随着窗口尺寸变化而发生改变

在这里插入图片描述

Layout 里面可以再嵌套上其他的 layout, 从而达到更复杂的布局效果

代码示例: 嵌套的 layout

(1) 在代码中创建以下内容

使用 addLayout 给 layout 中添加子 layout.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
	ui->setupUi(this);
	// 创建顶层 layout
	QVBoxLayout* layoutParent = new QVBoxLayout();
	this->setLayout(layoutParent);
	// 添加两个按钮进去
	QPushButton* btn1 = new QPushButton("按钮1");
	QPushButton* btn2 = new QPushButton("按钮2");
	layoutParent->addWidget(btn1);
	layoutParent->addWidget(btn2);
	// 创建⼦ layout
	QHBoxLayout* layoutChild = new QHBoxLayout();
	// 添加两个按钮进去
	QPushButton* btn3 = new QPushButton("按钮3");
	QPushButton* btn4 = new QPushButton("按钮4");
	layoutChild->addWidget(btn3);
	layoutChild->addWidget(btn4);
	// 把这个⼦ layout 添加到 ⽗ layout 中
	layoutParent->addLayout(layoutChild);
}

(2) 执行程序, 观察结果

在这里插入图片描述

结合 QHBoxLayout 和 QVBoxLayout , 就可以做出各种复杂的界面了

网格布局

Qt 中还提供了 QGridLayout 用来实现网格布局的效果可以达到 M*N 的这种网格的效果

核心属性

整体和 QVBoxLayout 以及 QHBoxLayout 相似. 但是设置 spacing 的时候是按照垂直水平两个方向来设置的.

属性说明
layoutLeftMargin左侧边距
layoutRightMargin右侧边距
layoutTopMargin上方边距
layoutBottomMargin下方边距
layoutHorizontalSpacing相邻元素之间水平方向的间距
layoutVerticalSpacing相邻元素之间垂直方向的间距
layoutRowStretch行方向的拉伸系数
layoutColumnStretch列方向的拉伸系数

代码示例: 使用 QGridLayout 管理元素

(1) 代码中创建 QGridLayout 和 4 个按钮.

使用 addWidget 添加控件到布局管理器中. 但是添加的同时会指定两个坐标. 表示放在第几行, 第几列.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
	ui->setupUi(this);
	// 创建 4 个按钮
	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);
}

(2) 执行代码, 观察效果. 可以看到当前的这几个按钮是按照 2 行 2 列的方式排列的.

在这里插入图片描述

(3) 如果调整行列坐标为下列代码

// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 0, 3);

执行代码, 可以看到这几个按钮都在同一行了. 相当于 QHBoxLayout

在这里插入图片描述

(4)) 如果调整行列坐标为下列代码

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

在这里插入图片描述

(5) 任意调整行列, 即可看到不同的效果

// 创建⽹格布局管理器, 并且添加元素
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 1);
layout->addWidget(btn3, 2, 2);
layout->addWidget(btn4, 3, 3);

在这里插入图片描述

(6) 编写代码形如

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 下方看起来和上面的 123 的情况是相同的

在这里插入图片描述

代码示例: 设置 QGridLayout 中元素的大小比例.

(1) 创建 6 个按钮, 按照 2 行 3 列的方式排列

使用 setColumnStretch 设置每⼀列的拉伸系数.

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);
	// 设置拉伸⽐例
	// 第 0 列拉伸⽐例设为 1;
	layout->setColumnStretch(0, 1);
	// 第 1 列拉伸⽐例设为 0, 即为固定⼤⼩, 不参与拉伸
	layout->setColumnStretch(1, 0);
	// 第 2 列拉伸⽐例设为 3, 即为第 2 列的宽度是第 0 列的 3 倍
	layout->setColumnStretch(2, 3);
	// 设置 layout 到窗⼝中.
	this->setLayout(layout);
}

(2) 执行程序, 可以看到每一列的宽度是不同的. 并且随着窗口调整动态变化

在这里插入图片描述

另外, QGridLayout 也提供了 setRowStretch 设置行之间的拉伸系数.

上述案例中, 直接设置 setRowStretch 效果不明显, 因为每个按钮的高度是固定的. 需要把按钮的垂直方向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器, 才能看到效果.

代码示例: 设置垂直方向的拉伸系数(setSizePolicy)

(1) 编写代码, 创建 6 个按钮, 按照 3 行 2 列方式排列.

使用 setSizePolicy 设置按钮的尺寸策略. 可选的值如下:

QSizePolicy::Ignored : 忽略控件的尺寸,不对布局产生影响。

QSizePolicy::Minimum : 控件的最小尺寸为固定值,布局时不会超过该值。

QSizePolicy::Maximum : 控件的最大尺寸为固定值,布局时不会小于该值。

QSizePolicy::Preferred : 控件的理想尺寸为固定值,布局时会尽量接近该值。

QSizePolicy::Expanding : 控件的尺寸可以根据空间调整,尽可能占据更多空间。

QSizePolicy::Shrinking : 控件的尺寸可以根据空间调整,尽可能缩小以适应空间。

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 ⾏拉伸⽐例设为 0, 即为固定⼤⼩, 不参与拉伸
	layout->setRowStretch(1, 0);
	// 第 2 ⾏拉伸⽐例设为 3, 即为第 2 ⾏的宽度是第 0 ⾏的 3 倍
	layout->setRowStretch(2, 3);
	// 设置 layout 到窗⼝中.
	this->setLayout(layout);
}

(2) 执行代码, 观察效果

此时的按钮垂直方向都舒展开了. 并且调整窗口尺寸, 也会按照设定的比例同步变化.

在这里插入图片描述

总的来说, 使用 QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景. 毕竟嵌套的代码写起来是比较麻烦的.

另外不要忘了, QGridLayout 里面也能嵌套 QHBoxLayout 和 QVBoxLayout , QHBoxLayout 和 QVBoxLayout 里面也能嵌套 QGridLayout . 灵活使用上述布局管理器, 就可以实现出任意的复杂界面

表单布局

除了上述的布局管理器之外, Qt 还提供了 QFormLayout , 属于是 QGridLayout 的特殊情况, 专门用于实现两列表单的布局.

这种表单布局多用于让用户填写信息的场景. 左侧列为提示, 右侧列为输入框.

代码示例: 使用 QFormLayout 创建表单.

(1) 编写代码, 创建 QFormLayout , 以及三个 label 和三个 lineEdit

  • 使用 addRow 方法来添加一行. 每行包含两个控件. 第一个控件固定是 QLabel / 文本, 第二个控件则可以是任意控件.
  • 如果把第一个参数填写为 NULL, 则什么都不显示.
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
	ui->setupUi(this);
	// 创建 layout
	QFormLayout* layout = new QFormLayout();
	this->setLayout(layout);
	// 创建三个 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* btn = new QPushButton("提交");
	// 把上述元素添加到 layout 中
	layout->addRow(label1, lineEdit1);
	layout->addRow(label2, lineEdit2);
	layout->addRow(label3, lineEdit3);
	layout->addRow(NULL, btn);
}

(2) 执行程序, 可以看到以下结果.

在这里插入图片描述

Spacer

使用布局管理器的时候, 可能需要在控件之间, 添加一段空白. 就可以使用 QSpacerItem 来表示

核心属性

属性说明
width宽度
height高度
hData水平方向的 sizePolicy
QSizePolicy::Ignored : 忽略控件的尺寸,不对布局产⽣影响。
QSizePolicy::Minimum : 控件的最小尺寸为固定值,布局时不会超过该值。
QSizePolicy::Maximum : 控件的最大尺寸为固定值,布局时不会小于该值。
QSizePolicy::Preferred : 控件的理想尺寸为固定值,布局时会尽量接近该值。
QSizePolicy::Expanding : 控件的尺寸可以根据空间调整,尽可能占据更多空间。
QSizePolicy::Shrinking : 控件的尺寸可以根据空间调整,尽可能缩小以适应控件
vData垂直方向的 sizePolicy
选项同上.

代码示例:创建一组左右排列的按钮

(1) 在界面上创建⼀个 QHBoxLayout , 并添加两个按钮.

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
	ui->setupUi(this);
	QHBoxLayout* layout = new QHBoxLayout();
	this->setLayout(layout);
	QPushButton* btn1 = new QPushButton("按钮1");
	QPushButton* btn2 = new QPushButton("按钮2");
	layout->addWidget(btn1);
	layout->addWidget(btn2);
}

(2) 直接运行程序, 可以看到两个按钮是紧挨着的.

在这里插入图片描述

(3) 在两个按钮中间添加⼀个 spacer

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
	ui->setupUi(this);
	QHBoxLayout* layout = new QHBoxLayout();
	this->setLayout(layout);
	QPushButton* btn1 = new QPushButton("按钮1");
	QPushButton* btn2 = new QPushButton("按钮2");
	// 创建 Spacer
	QSpacerItem* spacer = new QSpacerItem(200, 20);
	layout->addWidget(btn1);
	// 在两个 widget 中间添加空⽩
	layout->addSpacerItem(spacer);
	layout->addWidget(btn2);
}

(4) 运行程序, 观察代码效果. 可以看到两个按钮之间已经存在了间隔了.

调整 QSpacerItem 不同的尺寸, 即可看到不同的间距

在这里插入图片描述

在 Qt Designer 中, 也可以直接给界面上添加 spacer

在这里插入图片描述

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值