布局管理器

目录

引言:

1.垂直布局

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

代码示例: 创建两个 QVBoxLayout

 2.水平布局

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

代码示例: 嵌套的 layout

3.网格布局

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

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

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

4.表单布局

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

5.Spacer

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


引言:

  • 之前使用 Qt 在界面上创建的控件, 都是通过 "绝对定位" 的方式来设定的.
  • 也就是每个控件所在的位置, 都需要计算坐标, 最终通过 setGeometry 或者 move 方式摆放过去.
  • 这种设定方式其实并不方便. 尤其是界面如果内容比较多, 不好计算. 而且一个窗口大小往往是可以调整的, 按照绝对定位的方式, 也无法自适应窗口大小.
  • 因此 Qt 引入 "布局管理器" (Layout) 机制, 来解决上述问题.

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

1.垂直布局

  • 使用 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);

    // 创建3个按钮,使用垂直布局管理器管理起来
    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    QPushButton* button3 = new QPushButton("按钮3");

    // 创建布局管理器, 并且把按钮添加进去
    // 如果创建的时候指定⽗元素为 this, 则后⾯不需要 setLayout ⽅法了.
    QVBoxLayout* layout = new QVBoxLayout();
    layout->addWidget(button1);
    layout->addWidget(button2);
    layout->addWidget(button3);

    // 把布局管理器添加到窗口中
    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>180</x>
     <y>110</y>
     <width>161</width>
     <height>261</height>
    </rect>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QPushButton" name="pushButton_2">
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
    <item>
     <widget class="QPushButton" name="pushButton_3">
      <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>

 2.水平布局

  • 使用 QHBoxLayout 表示水平的布局管理器. Hhorizontal 的缩写.

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

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

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

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

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

    // 创建3个按钮
    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    QPushButton* button3 = new QPushButton("按钮3");

    // 创建布局管理器
    QHBoxLayout* hlayout = new QHBoxLayout();
    hlayout->addWidget(button1);
    hlayout->addWidget(button2);
    hlayout->addWidget(button3);

    // 设置 layout 到 widget 上
    this->setLayout(hlayout);
}

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

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

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


代码示例: 嵌套的 layout

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

  • 使用 addLayout 给 layout 中添加子 layout.
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 创建垂直的布局管理器
    QVBoxLayout* vlayout = new QVBoxLayout();
    this->setLayout(vlayout);

    // 添加两个按钮进去
    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    vlayout->addWidget(button1);
    vlayout->addWidget(button2);

    // 创建水平的布局管理器
    QHBoxLayout* hlayout = new QHBoxLayout();
    // 添加两个按钮进去
    QPushButton* button3 = new QPushButton("按钮3");
    QPushButton* button4 = new QPushButton("按钮4");
    hlayout->addWidget(button3);
    hlayout->addWidget(button4);

    // 把水平布局管理器添加到垂直布局管理器内部
    vlayout->addLayout(hlayout);
}

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

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

3.网格布局

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* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    QPushButton* button3 = new QPushButton("按钮3");
    QPushButton* button4 = new QPushButton("按钮4");

    // 创建网格布局管理器,并且添加元素
    QGridLayout* layout = new QGridLayout();
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 0, 1);
    layout->addWidget(button3, 1, 0);
    layout->addWidget(button4, 1, 1);

    // 设置 layout 到窗口中
    this->setLayout(layout);
}

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

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

     // 这个写法相当于水平布局
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 0, 1);
    layout->addWidget(button3, 0, 2);
    layout->addWidget(button4, 0, 3);

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

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

      // 这个写法相当于垂直布局
      layout->addWidget(button1, 0, 0);
      layout->addWidget(button2, 1, 0);
      layout->addWidget(button3, 2, 0);
      layout->addWidget(button4, 3, 0);

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

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

    // 这种写法是每个按钮独占一行和一列
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 1, 1);
    layout->addWidget(button3, 2, 2);
    layout->addWidget(button4, 3, 3);

🌴6) 编写代码形如

    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 1, 0);
    layout->addWidget(button3, 2, 0);
    layout->addWidget(button4, 10, 0);
  • 此处也要注意, 设置行和列的时候, 如果设置的是一个很大的值, 但是这个值和上一个值之间并没有其他的元素, 那么并不会在中间腾出额外的空间.
  • 虽然把 button4 设置在第 10 行, 但是由于 3-9 行没有元素. 因此 button4 仍然会紧挨在 button3 下方. 看起来和上面的 0 1 2 3 的情况是相同的.  此处设置的行数和列数,只是用来决定控件之间的相对位置


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

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

  • 使用 setColumnStretch 设置每一列的拉伸系数.
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 创建 6 个按钮, 使用网格布局按照 2 * 3 的方式排列
    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    QPushButton* button3 = new QPushButton("按钮3");
    QPushButton* button4 = new QPushButton("按钮4");
    QPushButton* button5 = new QPushButton("按钮5");
    QPushButton* button6 = new QPushButton("按钮6");

    // 创建网格布局管理器,把这些按钮添加进去
    QGridLayout* layout = new QGridLayout();
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 0, 1);
    layout->addWidget(button3, 0, 2);
    layout->addWidget(button4, 1, 0);
    layout->addWidget(button5, 1, 1);
    layout->addWidget(button6, 1, 2);

    this->setLayout(layout);

    // 设置水平拉伸系数
    // 第 0 列拉伸⽐例设为 1;
    layout->setColumnStretch(0, 1);
    // 第 1 列拉伸⽐例设为 2,即为第 1 列的宽度是第 0 列的 2 倍;
    layout->setColumnStretch(1, 2);
    // 第 2 列拉伸⽐例设为 3,即为第 2 列的宽度是第 0 列的 3 倍;
    layout->setColumnStretch(2, 3);
}

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

  • 另外, QGridLayout 也提供了 setRowStretch 设置行之间的拉伸系数.
  • 上述案例中, 直接设置 setRowStretch 效果不明显, 因为每个按钮的高度是固定的. 需要把按钮的垂直方向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器, 才能看到效果.

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

🍍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 个按钮,按照 3行2列 的方式进行排列
    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");
    QPushButton* button3 = new QPushButton("按钮3");
    QPushButton* button4 = new QPushButton("按钮4");
    QPushButton* button5 = new QPushButton("按钮5");
    QPushButton* button6 = new QPushButton("按钮6");

    // 设置按钮的 sizePolicy, 此时按钮的⽔平⽅向和垂直⽅向都会尽量舒展开
    button1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    button2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    button3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    button4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    button5->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    button6->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    // 创建 layout 并把按钮添加进去
    QGridLayout* layout = new QGridLayout();
    layout->addWidget(button1, 0, 0);
    layout->addWidget(button2, 0, 1);
    layout->addWidget(button3, 1, 0);
    layout->addWidget(button4, 1, 1);
    layout->addWidget(button5, 2, 0);
    layout->addWidget(button6, 2, 1);

    // 设置拉伸比例
    layout->setRowStretch(0, 1);
    layout->setRowStretch(1, 2);
    layout->setRowStretch(2, 3);

    // 把 layout 设置到窗口中
    this->setLayout(layout);
}

🍍 2) 执行代码, 观察效果.

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

  • 总的来说, 使用 QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景. 毕竟嵌套的代码写起来是比较麻烦的.
  • 另外不要忘了, QGridLayout 里面也能嵌套 QHBoxLayout 和 QVBoxLayout ,
  • QHBoxLayout 和 QVBoxLayout 里面也能嵌套 QGridLayout .
  • 灵活使用上述布局管理器, 就可以实现出任意的复杂界面.

4.表单布局

        除了上述的布局管理器之外, 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,设置成 3 行 2 列
    QFormLayout* layout = new QFormLayout();
    this->setLayout(layout);

    // 创建 3 个 label 作为第一列
    QLabel* label1 = new QLabel("姓名");
    QLabel* label2 = new QLabel("电话");
    QLabel* label3 = new QLabel("年龄");

    // 创建 3 个 lineEdit 作为第二列
    QLineEdit* edit1 = new QLineEdit();
    QLineEdit* edit2 = new QLineEdit();
    QLineEdit* edit3 = new QLineEdit();

    // 创建一个提交按钮
    QPushButton* button = new QPushButton("提交");

    // 把上述控件添加到表单布局中
    layout->addRow(label1, edit1);
    layout->addRow(label2, edit2);
    layout->addRow(label3, edit3);
    layout->addRow(nullptr, button);
}

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

5.Spacer

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

核心属性:

属性说明
width宽度
height高度
hData

水平方向的 sizePolicy

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

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

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

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

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

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

vData垂直方向的 sizePolicy 选项同上.
  • 上述属性在构造函数设置即可.

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

🍉1) 在界面上创建一个 QVBoxLayout , 并添加两个按钮.

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

    QHBoxLayout* layout = new QHBoxLayout();
    this->setLayout(layout);

    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");


    layout->addWidget(button1);
    layout->addWidget(button2);
}

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

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

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

    QHBoxLayout* layout = new QHBoxLayout();
    this->setLayout(layout);

    QPushButton* button1 = new QPushButton("按钮1");
    QPushButton* button2 = new QPushButton("按钮2");

    // 创建 spacer
    QSpacerItem* spacer = new QSpacerItem(200, 20);

    layout->addWidget(button1);
    // 在两个按钮之间添加空白
    layout->addSpacerItem(spacer);
    layout->addWidget(button2);
}

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

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南风与鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值