【QT】常⽤控件详解(七)容器类控件 GroupBox TabWidget 布局管理器 Spacer
文章目录
- 一、容器类控件
- 二、GroupBox
- 2.1 核心属性与信号
- 2.2 给⻨当劳案例加上分组框
- 三、 TabWidget
- 3.1 核心属性
- 3.2 使⽤标签⻚管理多组控件
- 四、 布局管理器
- 4.1 垂直布局
- 4.1.1 使用QVBoxLayout管理多个控件.
- 4.1.2 创建两个QVBoxLayout
- 4.2 ⽔平布局
- 4.2.1 使⽤`QHBoxLayout` 管理控件
- 4.2.2 嵌套的layout
- 4.3 ⽹格布局
- 4.3.1使⽤`QGridLayout` 管理元素
- 4.3.2 设置QGridLayout 中元素的⼤⼩⽐例.
- 4.4 设置垂直⽅向的拉伸系数
- 4.5 表单布局
- 4.5.1 使⽤QFormLayout 创建表单.
- 五、 Spacer
- 5.1 创建⼀组左右排列的按钮.
- 🚩总结
一、容器类控件
容器类控件(Container Widgets) 是一类特殊的控件,其核心功能是容纳、管理其他控件(子控件),并通过布局管理来控制子控件的位置、大小及排列方式。它们本身通常不直接与用户交互,而是作为 “容器” 组织界面结构,使界面更有序、易维护。
二、GroupBox
使用QGroupBox
实现一个带有标题的分组框.可以把其他的控件放到里面作为一组.这样看起来能更好看一点.
注意,不要把 QGroupBox和QButtonGroup混淆.(之前在介绍QRadionButton的时候提到了QButtonGroup ).
2.1 核心属性与信号
属性 | 说明 |
---|---|
title | 分组框的标题 |
alignment | 分组框内部内容的对齐方式 |
flat | 是否是 “扁平” 模式 |
checkable | 是否可选择. 设为 true, 则在 title 前方会多出一个可勾选的部分. |
checked | 描述分组框的选择状态 (前提是 checkable 为 true) |
分组框只是⼀个⽤来"美化界⾯"这样的组件,并不涉及到⽤⼾交互和业务逻辑.属于"锦上添花".
核⼼信号
属性 | 说明 |
---|---|
currentChanged(int) | 在标签页发生切换时触发,参数为被点击的选项卡编号. |
tabBarClicked(int) | 在点击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
tabBarDoubleClicked(int) | 在双击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
tabCloseRequest(int) | 在标签页关闭时触发.参数为被关闭的选项卡编号. |
2.2 给⻨当劳案例加上分组框
- 在界⾯上创建三个分组框,并且在分组框内部创建下拉框和微调框.
2) 编写widget.cpp,添加初始化下拉框的代码
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);ui->comboBox->addItem("巨⽆霸");ui->comboBox->addItem("⻨辣鸡腿堡");ui->comboBox_2->addItem("薯条");ui->comboBox_2->addItem("⻨辣鸡翅");ui->comboBox_3->addItem("可乐");ui->comboBox_3->addItem("雪碧");}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString orderInfo = "你下单的商品是:\n";// 处理主食类if (ui->groupBox->isChecked()) {QString food = ui->comboBox->currentText();int count = ui->spinBox->value();orderInfo += QString("%1 %2份\n").arg(food).arg(count);}// 处理小食类if (ui->groupBox_2->isChecked()) {QString snack = ui->comboBox_2->currentText();int count = ui->spinBox_2->value();orderInfo += QString("%1 %2份\n").arg(snack).arg(count);}// 处理可乐类if (ui->groupBox_3->isChecked()) {QString drink = ui->comboBox_3->currentText();int count = ui->spinBox_3->value();orderInfo += QString("%1 %2份\n").arg(drink).arg(count);}// 将下单信息显示到文本框中,假设用于显示的控件是QTextEdit,objectName为textEditui->label->setText(orderInfo);
}
三、 TabWidget
使用QTabwidget
实现一个带有标签页的控件,可以往里面添加一些widget.进一步的就可以通过标签页来切换.
3.1 核心属性
属性 | 说明 |
---|---|
tabPosition | 标签页所在的位置.
|
currentIndex | 当前选中了第几个标签页 (从 0 开始计算) |
currentTabText | 当前选中的标签页的文本 |
currentTabName | 当前选中的标签页的名字 |
currentTabIcon | 当前选中的标签页的图标 |
currentTabToolTip | 当前选中的标签页的提示信息 |
tabsCloseable | 标签页是否可以关闭 |
movable | 标签页是否可以移动 |
3.2 使⽤标签⻚管理多组控件
1)在界面上创建一个QTabwidget,和两个按钮.
按钮的objectName
为pushButton_add
和pushButton_remove
注意,
QTabwidget
中的每个标签页都是一个 Qwidget
点击标签页,就可以直接切换.
·右键QTabwidget ,可以添加标签页或者删除标签页.
代码:
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QLabel* label = new QLabel(ui->tab);label->setText("标签页1");label->resize(100, 50);QLabel* label2 = new QLabel(ui->tab_2);label2->setText("标签页2");label2->resize(100, 50);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_add_clicked()
{//获取当前有几个标签页int count = ui->tabWidget->count();//创建新的WidgetQWidget* w = new QWidget();ui->tabWidget->addTab(w, QString("Tab: ") + QString::number(count + 1));//给 widget 中添加labelQLabel* label = new QLabel(w);label->setText(QString("标签页") + QString::number(count + 1));label->resize(100, 50);//选中这个新的标签页ui->tabWidget->setCurrentIndex(count);
}void Widget::on_pushButton_delete_clicked()
{//获取当前的标签页的下标int index = ui->tabWidget->currentIndex();//删除这个标签页ui->tabWidget->removeTab(index);
}void Widget::on_tabWidget_currentChanged(int index)
{qDebug() << "当前选中标签页为:"<< index;
}
效果:
四、 布局管理器
之前使用Qt在界面上创建的控件,都是通过"绝对定位"的方式来设定的.
也就是每个控件所在的位置,都需要计算坐标,最终通过setGeometry或者move方式摆放过去.
这种设定方式其实并不方便.尤其是界面如果内容比较多,不好计算.而且一个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小.
因此 Qt引入"布局管理器"(Layout)机制,来解决上述问题.
当然,布局管理器并⾮Qt独有.其他的GUI开发框架,像Android,前端等也有类似的机制
4.1 垂直布局
使用QVBoxLayout
表示垂直的布局管理器.V是vertical的缩写.
核心要点
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutSpacing | 相邻元素之间的间距 |
Layout 只是⽤于界⾯布局,并没有提供信号.
4.1.1 使用QVBoxLayout管理多个控件.
1)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.·使用addwidget把控件添加到布局管理器中.
·使用setLayout设置该布局管理器到widget 中.
代码
#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;
}
- 运⾏程序,可以看到此时界⾯上的按钮就存在于布局管理器中.随着窗⼝尺⼨变化⽽发⽣改变.
此时三个按钮的尺⼨和位置,都是⾃动计算出来的.
4.1.2 创建两个QVBoxLayout
- 在界⾯上创建两个
QVBoxLayout
,每个QVBoxLayout
各放三个按钮.
- 运⾏程序,可以看到这些按钮已经⾃动排列好.只不过当前这些按钮的位置不能随着窗⼝⼤⼩⾃动变化.
通过QtDesigner创建的布局管理器,其实是先创建了⼀个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>
4.2 ⽔平布局
使⽤QHBoxLayout
表⽰垂直的布局管理器.H
是horizontal
的缩写
核⼼属性(和QVBoxLayout
属性是⼀致的)
4.2.1 使⽤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);//设置 layout 到widget上this->setLayout(layout);
}
- 运⾏程序,可以看到此时界⾯上的按钮就存在于布局管理器中.随着窗⼝尺⼨变化⽽发⽣改变.此时三个按钮的尺⼨和位置,都是⾃动计算出来的
Layout
⾥⾯可以再嵌套上其他的layout,从⽽达到更复杂的布局效果.
4.2.2 嵌套的layout
- 在代码中创建以下内容
使⽤addLayout
给layout
中添加⼦layout
#include "widget.h"
#include "ui_widget.h"
#include <QVBoxLayout>
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建顶层 layoutQVBoxLayout* layoutParent = new QVBoxLayout();this->setLayout(layoutParent);//添加两个按钮进去QPushButton* btn1 = new QPushButton("按钮1");QPushButton* btn2 = new QPushButton("按钮2");layoutParent->addWidget(btn1);layoutParent->addWidget(btn2);//创建子 layoutQHBoxLayout* layoutChild = new QHBoxLayout();//添加两个按钮进去QPushButton* btn3 = new QPushButton("按钮3");QPushButton* btn4 = new QPushButton("按钮4");layoutChild->addWidget(btn3);layoutChild->addWidget(btn4);//把这个子layout 添加到父 layout 中layoutParent->addLayout(layoutChild);}
- 执⾏程序,观察结果
结合QHBoxLayout
和QVBoxLayout
,就可以做出各种复杂的界⾯了
4.3 ⽹格布局
Qt中还提供了QGridLayout
用来实现网格布局的效果.可以达到M*N的这种网格的效果.
核心属性
整体和
QVBoxLayout
以及QHBoxLayout
相似.但是设置spacing 的时候是按照垂直水平两个方向来设置的.
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上方边距 |
layoutBottomMargin | 下方边距 |
layoutHorizontalSpacing | 相邻元素之间水平方向的间距 |
layoutVerticalSpacing | 相邻元素之间垂直方向的间距 |
layoutRowStretch | 行方向的拉伸系数 |
layoutColumnStretch | 列方向的拉伸系数 |
4.3.1使⽤QGridLayout
管理元素
1)代码中创建QGridLayout
和4个按钮.
使用addwidget添加控件到布局管理器中.但是添加的同时会指定两个坐标.表示放在第几行,第几列.
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QGridLayout>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列的⽅式排列的.
-
如果调整⾏列坐标为下列代码
执行代码,可以看到这几个按钮都在同一行了.相当于QHBoxLayout
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 0, 1);
layout->addWidget(btn3, 0, 2);
layout->addWidget(btn4, 0, 3);
4)如果调整行列坐标为下列代码,执行代码,可以看到这几个按钮都在同一行了.相当于QVBoxLayout
QGridLayout* layout = new QGridLayout();
layout->addWidget(btn1, 1, 0);
layout->addWidget(btn2, 2, 0);
layout->addWidget(btn3, 3, 0);
layout->addWidget(btn4, 4, 0);
- 编写代码形如
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下方.看起来和上面的0 1 2 3的情况是相同的.
4.3.2 设置QGridLayout 中元素的⼤⼩⽐例.
- 创建6个按钮,按照2⾏3列的⽅式排列
使⽤setColumnStretch
设置每⼀列的拉伸系数.
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QGridLayout>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);}
- 执⾏程序,可以看到每⼀列的宽度是不同的.并且随着窗⼝调整动态变化
注:这有时上传不了图片,后面好了会补上
4.4 设置垂直⽅向的拉伸系数
- 编写代码,创建6个按钮,按照3⾏2列⽅式排列.
使⽤setSizePolicy
设置按钮的尺⼨策略.可选的值如下:
| | |
| ------------------------ | ------------------------------------------------------------ |
|QSizePolicy::Ignored
| 忽略控件的尺寸,不对布局产生影响。 |
|QSizePolicy::Minimum
| 控件的最小尺寸为固定值,布局时不会超过该值。 |
|QSizePolicy::Maximum
| 控件的最大尺寸为固定值,布局时不会小于该值。 |
|QSizePolicy::Preferred
| 控件的理想尺寸为固定值,布局时会尽量接近该值。 |
|QSizePolicy::Expanding
| 控件的尺寸可以根据空间调整,尽可能占据更多空间。 |
|QSizePolicy::Shrinking
| 控件的尺寸可以根据空间调整,尽可能缩小以适应空间。 |
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QGridLayout>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);//创建 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);// 第0行拉伸比例设为 1;layout->setRowStretch(1 ,0);// 第0行拉伸比例设为 1;layout->setRowStretch(2 ,3);//设置 layout 到窗口中this->setLayout(layout);
}
- 执⾏代码,观察效果.
此时的按钮垂直⽅向都舒展开了.并且调整窗⼝尺⼨,也会按照设定的⽐例同步变化(图片还是上传失败)
总的来说,使用QGridLayout能够代替很多QHBoxLayout和QVBoxLayout嵌套的场景.毕竟嵌套的代码写起来是比较麻烦的.
另外不要忘了,QGridLayout里面也能嵌套QHBoxLayout和QVBoxLayout,QHBoxLayout和QVBoxLayout里面也能嵌套QGridLayout .
灵活使用上述布局管理器,就可以实现出任意的复杂界面.
4.5 表单布局
除了上述的布局管理器之外,Qt还提供了QFormLayout ,属于是 QGridLayout的特殊情况,专门用于实现两列表单的布局.
这种表单布局多用于让用户填写信息的场景.左侧列为提示,右侧列为输入框.
4.5.1 使⽤QFormLayout 创建表单.
1)编写代码,创建QFormLayout ,以及三个label和三个lineEdit
- 使用addRow方法来添加一行.每行包含两个控件.第一个控件固定是QLabel/文本,第二个控件则可以是任意控件
- 如果把第一个参数填写为NULL,则什么都不显示.
#include "widget.h"
#include "ui_widget.h"
#include <QFormLayout>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建 layoutQFormLayout* layout = new QFormLayout();this->setLayout(layout);// 创建3 个labelQLabel* label1 = new QLabel("姓名");QLabel* label2 = new QLabel("年龄");QLabel* label3 = new QLabel("电话");//创建 3 个lineEditQLineEdit* 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);
}
五、 Spacer
使⽤布局管理器的时候,可能需要在控件之间,添加⼀段空⽩.就可以使⽤
QSpacerItem
来表⽰.
核⼼属性
属性 | 说明 |
---|---|
width | 宽度 |
height | 高度 |
hData | 水平方向的 sizePolicy
|
vData | 垂直方向的 sizePolicy 选项同上. |
上述属性在构造函数设置即可.
5.1 创建⼀组左右排列的按钮.
1)在界面上创建一个QVBoxLayout
,并添加两个按钮.
#include "widget.h"
#include "ui_widget.h"
#include <QHBoxLayout>
#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");//创建SpacerQSpacerItem* spacer = new QSpacerItem(200, 20);layout->addWidget(btn1);//在两个widget 中间添加空白layout->addSpacerItem(spacer);layout->addWidget(btn2);
}
在QtDesigner
中,也可以直接给界⾯上添加spacer