Bootstrap

C++客户端Qt开发——常用控件(布局管理器)

7.布局管理器

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

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

move方式摆放过去,

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

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

布局管理器并非Qt独有.其他的GUI开发框架,像Android,前端等也有类似的机制

①垂直布局

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

属性

说明

layoutLeftMargin

左侧边距

layoutRightMargin

右侧边距

layoutTopMargin

上方边距

layoutBottomMargin

下方边距

layoutSpacing

相邻元素之间的间距

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

①使用QVBoxLayout管理多个控件

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

②创建两个QVBoxLayout

按钮已经自动排列好,只不过当前这些按钮的位置不能随着窗口大小自动变化

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

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

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

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

②水平布局

使用QHBox Layout,表示垂直的布局管理器.H是horizontal的缩写

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

属性

说明

layoutLeftMargin

左侧边距

layoutRightMargin

右侧边距

layoutTopMargin

上方边距

layoutBottomMargin

下方边距

layoutSpacing

相邻元素之间的间距

①使用QHBoxLayout管理控件

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QHBoxLayout>

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

    this->setLayout(layout);

}

Widget::~Widget()
{
    delete ui;
}

②嵌套下的layout

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

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

    //创建顶层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);
}

Widget::~Widget()
{
    delete ui;
}

③网格布局

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

核心属性

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

属性

说明

layoutLeftMargin

左侧边距

layoutRightMargin

右侧边距

layoutTopMargin

上方边距

layoutBottomMargin

下方边距

layoutHorizontalSpacing

相邻元素之间水平方向的间距

layoutVerticalSpacing

相邻元素之间垂直方向的间距

layoutRowStretch

行方向的拉伸系数

layoutColumnStretch

列方向的拉伸系数

①使用QGridLayout管理元素

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>

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

    this->setLayout(layout);
}

Widget::~Widget()
{
    delete ui;
}

//假如是这种布局
//相当于QHBoxLayout
layout->addWidget(btn1,0,1);
layout->addWidget(btn2,0,2);
layout->addWidget(btn3,0,3);
layout->addWidget(btn4,0,4);
//假如是这种布局
//相当于QVBoxLayout
layout->addWidget(btn1, 1, 0);
layout->addWidget(btn2, 2, 0);
layout->addWidget(btn3, 3, 0);
layout->addWidget(btn4, 4, 0);

此处也要注意,设置行和列的时候,如果设置的是一个很大的值,但是这个值和上一个值之间并没有其他的元素,那么并不会在中间腾出额外的空间

假如把btn4设置在第10行,但是由于3-9行没有元素.因此btn4仍然会紧挨在btn3下方,看起来和上面的0 1 2 3的情况是相同的

②设置QGridLayout中元素的大小比例

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>

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

    this->setLayout(layout);
}

Widget::~Widget()
{
    delete ui;
}

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

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

③设置垂直方向的拉伸系数

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QGridLayout>

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");
    QPushButton* btn4 = new QPushButton("按钮4");
    QPushButton* btn5 = new QPushButton("按钮5");
    QPushButton* btn6 = new QPushButton("按钮6");

    //设置按钮的sizePolice,此时按钮的水平和垂直方向都会尽量舒展开
    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);

    this->setLayout(layout);

}

Widget::~Widget()
{
    delete ui;
}

④表单布局

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

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

#include "widget.h"
#include "ui_widget.h"
#include<QFormLayout>
#include<QLabel>
#include<QLineEdit>
#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

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

}

Widget::~Widget()
{
    delete ui;
}

⑤Spacer 在控件之间添加一段空白

属性

说明

width

宽度

height

高度

hData

水平方向的sizePolicy

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

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

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

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

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

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

vData

垂直方向的sizePolicy

选项同上

①创建一组左右排列的按钮

#include "widget.h"
#include "ui_widget.h"
#include<QVBoxLayout>
#include<QPushButton>

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

Widget::~Widget()
{
    delete ui;
}

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

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

;