Group Box
使⽤ QGroupBox 实现⼀个带有标题的分组框.可以把其他的控件放到⾥⾯作为⼀组.这样看起来能更好看⼀点.
注意,不要把 QGroupBox 和 QButtonGroup 混淆.(之前在介绍 QRadionButton的时候提到了 QButtonGroup ).
核⼼属性
属性 | 说明 |
---|---|
title | 分组框的标题 |
alignment | 分组框内部内容的对⻬⽅式 |
flat | 是否是"扁平"模式 |
checkable | 是否可选择. 设为true,则在title前⽅会多出⼀个可勾选的部分. |
checked | 描述分组框的选择状态(前提是checkable为true) |
分组框只是⼀个⽤来"美化界⾯"这样的组件,并不涉及到⽤⼾交互和业务逻辑.属于"锦上添花"
代码⽰例:给⻨当劳案例加上分组框
1)在界⾯上创建三个分组框,并且在分组框内部创建下拉框和微调框.
在复制粘贴控件的时候,⼀定要先选中对应的⽗控件,再粘贴.
2)编写widget.cpp,添加初始化下拉框的代码
Tab Widget
使⽤ QTabWidget 实现⼀个带有标签⻚的控件,可以往⾥⾯添加⼀些widget.进⼀步的就可以通过标签⻚来切换
核⼼属性
属性 | 说明 |
---|---|
tabPosition | 标签⻚所在的位置. • North 上⽅ • South 下⽅ • West 左侧 • East 右侧 |
currentIndex | 当前选中了第⼏个标签⻚(从0开始计算) |
currentTabText | 当前选中的标签⻚的⽂本 |
currentTabName | 当前选中的标签⻚的名字 |
currentTabIcon | 当前选中的标签⻚的图标 |
currentTabToolTip | 当前选中的标签⻚的提⽰信息 |
tabsCloseable | 标签⻚是否可以关闭 |
movable | 标签⻚是否可以移动 |
核⼼信号
属性 | 说明 |
---|---|
currentChanged(int) | 在标签⻚发⽣切换时触发,参数为被点击的选项卡编号. |
tabBarClicked(int) | 在点击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
tabBarDoubleClicked(int) | 在双击选项卡的标签条的时候触发.参数为被点击的选项卡编号. |
tabCloseRequest(int) | 在标签⻚关闭时触发.参数为被关闭的选项卡编号. |
代码⽰例:使⽤标签⻚管理多组控件
1)在界⾯上创建⼀个 QTabWidget ,和两个按钮.
按钮的 objectName 为 pushButton_add 和 pushButton_remove
- QTabWidget 中的每个标签⻚都是⼀个 QWidget
- 点击标签⻚,就可以直接切换.
- 右键 QTabWidget ,可以添加标签⻚或者删除标签⻚.
2)编写widget.cpp,进⾏初始化,给标签⻚中放个简单的label - 注意新创建的label的⽗元素,是 ui->tab 和 ui->tab_2 .Qt中使⽤⽗⼦关系决定该控件"在哪⾥".
#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//在每个标签页中添加一个label
QLabel* label1 = new QLabel(ui->tab);
label1->setText("标签页1");
label1->resize(100, 50);
QLabel* label2 = new QLabel(ui->tab_2);
label2->setText("标签页2");
label2->resize(100, 50);
}
3)编写按钮的slot函数
- 使⽤ count() 获取到标签⻚的个数.
- 使⽤ addTab 新增标签⻚.
- 使⽤ removeTab 删除标签⻚.
- 使⽤ currentIndex 获取到当前标签⻚的下标.
- 使⽤ setCurrentIndex 切换当前标签⻚.
void Widget::on_pushButton_add_clicked()
{
//使用addTab方法创建新的标签页
//参数1指定一个QWidget,
//参数2指定一个标签页的text(标题),此处标题是Tab+数字
int count = ui->tabWidget->count(); //获取到标签页的数量
QWidget* w = new QWidget();
ui->tabWidget->addTab(w, QString("Tab ") + QString::number(count + 1));
//添加Qlabel显示内容
QLabel* label = new QLabel(w);
label->setText(QString("标签页 ") + QString::number(count + 1));
label->resize(100, 50);
//设置新标签页被选中
ui->tabWidget->setCurrentIndex(count);
}
void Widget::on_pushButton_remove_clicked()
{
//拿到选中标签页的下标
int index = ui->tabWidget->currentIndex();
//删除标签页
ui->tabWidget->removeTab(index);
}
void Widget::on_tabWidget_currentChanged(int index)
{
qDebug() << "当前选中的标签页是: " << index;
}
5)运⾏程序,观察效果
- 点击新建标签⻚,可以创建出新的标签.
- 点击删除当前标签⻚,可以删除标签.
- 切换标签⻚时,可以看到qDebug打印出的标签⻚编号.
布局管理器
之前使⽤Qt在界⾯上创建的控件,都是通过"绝对定位"的⽅式来设定的.
也就是每个控件所在的位置,都需要计算坐标,最终通过 setGeometry 或者 move ⽅式摆放过去.
这种设定⽅式其实并不⽅便.尤其是界⾯如果内容⽐较多,不好计算.⽽且⼀个窗⼝⼤⼩往往是可以调整的,按照绝对定位的⽅式,也⽆法⾃适应窗⼝⼤⼩.
因此Qt引⼊"布局管理器"(Layout)机制,来解决上述问题.
当然,布局管理器并⾮Qt独有.其他的GUI开发框架,像Android,前端等也有类似的机制.
垂直布局
使⽤ 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* button1 = new QPushButton("按钮1");
QPushButton* button2 = new QPushButton("按钮2");
QPushButton* button3 = new QPushButton("按钮3");
//创建布局管理器
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)运⾏程序,可以看到这些按钮已经⾃动排列好.只不过当前这些按钮的位置不能随着窗⼝⼤⼩⾃动变化.
如果在代码中创建layout,其实是只创建了一个 layout.
通过Qt Designer创建的布局管理器,其实是先创建了⼀个widget,设置过 geometry 属性的.再把这个layout设置到这个widget中.
实际上,⼀个widget只能包含⼀个layout.
打开ui⽂件的原始xml,可以看到其中的端倪.
这种情况下layout并⾮是窗⼝widget的布局管理器,因此不会随着窗⼝⼤⼩改变.
layout标签表示的是布局管理器的本体外面自动创建出了一个Widget
每个layout里面又可以包含若干个item(也就是一个按钮)
刚才是先拖了layout过去,然后再往layout中拖其他控件也可以先拖其他控件,后给这些控件套上layout~~
⽔平布局
使⽤ QHBoxLayout 表⽰垂直的布局管理器.H是 horizontal 的缩写.
核⼼属性(和 QVBoxLayout 属性是⼀致的)
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上⽅边距 |
layoutBottomMargin | 下⽅边距 |
layoutSpacing | 相邻元素之间的间距 |
代码⽰例:使⽤ QHBoxLayout 管理控件
1)编写代码,创建布局管理器和三个按钮.并且把按钮添加到布局管理器中.
#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* button1 = new QPushButton("按钮1");
QPushButton* button2 = new QPushButton("按钮2");
QPushButton* button3 = new QPushButton("按钮3");
QHBoxLayout* layout = new QHBoxLayout();
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
this->setLayout(layout);
}
2)运⾏程序,可以看到此时界⾯上的按钮就存在于布局管理器中.随着窗⼝尺⼨变化⽽发⽣改变.
此时三个按钮的尺⼨和位置,都是⾃动计算出来的
Layout⾥⾯可以再嵌套上其他的layout,从⽽达到更复杂的布局效果.
代码⽰例:嵌套的layout
1)在代码中创建以下内容
- 使⽤ addLayout 给layout中添加⼦layout.
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QHBoxLayout>
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 ,就可以做出各种复杂的界⾯了
⽹格布局
Qt中还提供了 QGridLayout ⽤来实现⽹格布局的效果.可以达到M*
N的这种⽹格的效果.
核⼼属性
整体和 QVBoxLayout 以及 QHBoxLayout 相似.但是设置spacing的时候是按照垂直⽔平两个⽅向来设置的.
属性 | 说明 |
---|---|
layoutLeftMargin | 左侧边距 |
layoutRightMargin | 右侧边距 |
layoutTopMargin | 上⽅边距 |
layoutBottomMargin | 下⽅边距 |
layoutHorizontalSpacing | 相邻元素之间⽔平⽅向的间距 |
layoutVerticalSpacing | 相邻元素之间垂直⽅向的间距 |
layoutRowStretch | ⾏⽅向的拉伸系数 |
layoutColumnStretch | 列⽅向的拉伸系数 |
代码⽰例:使⽤ 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);
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);
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
行数只要一样即可,不一定非得是0
4)如果调整⾏列坐标为下列代码
layout->addWidget(button1, 0, 0);
layout->addWidget(button2, 1, 0);
layout->addWidget(button3, 2, 0);
layout->addWidget(button4, 3, 0);
执⾏代码,可以看到这⼏个按钮都在同⼀列了.相当于 QVBoxLayout
layout->addWidget(button1, 0, 0);
layout->addWidget(button2, 1, 1);
layout->addWidget(button3, 2, 2);
layout->addWidget(button4, 3, 3);
此处也要注意,设置⾏和列的时候,如果设置的是⼀个很⼤的值,但是这个值和上⼀个值之间并没有其他的元素,那么并不会在中间腾出额外的空间.
layout->addWidget(button1, 0, 0);
layout->addWidget(button2, 1, 1);
layout->addWidget(button3, 2, 2);
layout->addWidget(button4, 100, 100);
代码⽰例:设置 QGridLayout 中元素的⼤⼩⽐例.
1)创建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个按钮,使用网格布局按照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);
}
layout->setColumnStretch(0, 1);
layout->setColumnStretch(1, 1);
layout->setColumnStretch(2, 2);
此处代码的含义,就是这三个列按照1:1:2的方式来设置宽度
layout->setColumnStretch(0, 0);
layout->setColumnStretch(1, 1);
layout->setColumnStretch(2, 2);
如果拉伸系数设为0,意思是不参与拉伸~此时按钮的宽度是固定值
代码⽰例:设置垂直⽅向的拉伸系数
1)编写代码,创建6个按钮,按照3⾏2列⽅式排列.
#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* 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");
//创建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);
this->setLayout(layout);
//设置拉伸系数
layout->setRowStretch(0, 1);
layout->setRowStretch(1, 1);
layout->setRowStretch(2, 2);
}
另外, QGridLayout 也提供了 setRowStretch 设置⾏之间的拉伸系数.
上述案例中,直接设置 setRowStretch 效果不明显,因为每个按钮的⾼度是固定的.需要把按钮的垂直⽅向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填充满布局管理器,才能看到效果.
由于按钮垂直方向默认没有拉伸开(水平方向默认是拉伸的)因此垂直方向不会受到拉伸系数的影响了
要想让垂直方向的拉伸系数生效,就需要让按钮先能够拉伸展开
使⽤ setSizePolicy 设置按钮的尺⼨策略.可选的值如下:
- QSizePolicy::Ignored :忽略控件的尺⼨,不对布局产⽣影响。
- QSizePolicy::Minimum :控件的最⼩尺⼨为固定值,布局时不会超过该值。
- QSizePolicy::Maximum :控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
- QSizePolicy::Preferred :控件的理想尺⼨为固定值,布局时会尽量接近该值。
- QSizePolicy::Expanding :控件的尺⼨可以根据空间调整,尽可能占据更多空间。
- QSizePolicy::Shrinking :控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。
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);
2)执⾏代码,观察效果.
此时的按钮垂直⽅向都舒展开了.并且调整窗⼝尺⼨,也会按照设定的⽐例同步变化.
总的来说,使⽤ QGridLayout 能够代替很多 QHBoxLayout 和 QVBoxLayout 嵌套的场景.毕竟嵌套的代码写起来是⽐较⿇烦的.
另外不要忘了, QGridLayout ⾥⾯也能嵌套 QHBoxLayout 和 QVBoxLayout ,
QHBoxLayout 和 QVBoxLayout ⾥⾯也能嵌套 QGridLayout .
灵活使⽤上述布局管理器,就可以实现出任意的复杂界⾯.
表单布局
除了上述的布局管理器之外,Qt还提供了 QFormLayout ,属于是 QGridLayout 的特殊情况,专⻔⽤于实现两列表单的布局.
这种表单布局多⽤于让⽤⼾填写信息的场景.左侧列为提⽰,右侧列为输⼊框.
代码⽰例:使⽤ QFormLayout 创建表单.
1)编写代码,创建 QFormLayout ,以及三个label和三个lineEdit
- 使⽤ addRow ⽅法来添加⼀⾏.每⾏包含两个控件.第⼀个控件固定是QLabel/⽂本,第⼆个控件则可以是任意控件.
- 如果把第⼀个参数填写为NULL,则什么都不显⽰.
#include "widget.h"
#include "ui_widget.h"
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
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("电话");
//创建3个输入框
QLineEdit* edit1 = new QLineEdit();
QLineEdit* edit2 = new QLineEdit();
QLineEdit* edit3 = new QLineEdit();
//把上述控件添加到表单布局中
layout->addRow(label1, edit1);
layout->addRow(label2, edit2);
layout->addRow(label3, edit3);
}
//创建按钮用来提交
QPushButton* button = new QPushButton("提交: ");
layout->addRow(nullptr, button);
Spacer
使⽤布局管理器的时候,可能需要在控件之间,添加⼀段空⽩.就可以使⽤ QSpacerItem 来表⽰
核⼼属性
属性 | 说明 |
---|---|
width | 宽度 |
height | ⾼度 |
hData | ⽔平⽅向的sizePolicy • QSizePolicy::Ignored :忽略控件的尺⼨,不对布局产⽣影响。 • QSizePolicy::Minimum :控件的最⼩尺⼨为固定值,布局时不会超过该值。 • QSizePolicy::Maximum :控件的最⼤尺⼨为固定值,布局时不会⼩于该值。 • QSizePolicy::Preferred :控件的理想尺⼨为固定值,布局时会尽量接近该值。 • QSizePolicy::Expanding :控件的尺⼨可以根据空间调整,尽可能占据更多空间。 • QSizePolicy::Shrinking :控件的尺⼨可以根据空间调整,尽可能缩⼩以适应空间。 |
vData | 垂直⽅向的sizePolicy选项同上. |
上述属性在构造函数设置即可. |
代码⽰例:创建⼀组左右排列的按钮.
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* button1 = new QPushButton("按钮1");
QPushButton* button2 = new QPushButton("按钮2");
layout->addWidget(button1);
layout->addWidget(button2);
}
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);
}
在Qt Designer中,也可以直接给界⾯上添加spacer.