容器类控件和布局管理器

发布日期:2025-09-25 00:30:53 分类:365bet是什么公司 浏览:1623

目录

1. 容器类控件1.1 Group Box1.2 Tab Widget

2. 布局管理器2.1 垂直布局2.2 水平布局2.3 网格布局2.4 表单布局2.5 Spacer

3. 容器类控件和布局管理器总结

1. 容器类控件

1.1 Group Box

(1)使用 QGroupBox 实现一个带有标题的分组框。可以把其他的控件放到里面作为一组。这样看起来能更好看一点。

注意:不要把 QGroupBox 和 QButtonGroup 混淆。(之前在介绍 QRadionButton 的时候提到了 QButtonGroup )。

(2)核心属性:

属性说明title分组框的标题。alignment分组框内部内容的对齐方式。flat是否是 “扁平” 模式。checkable是否可选择。设为 true, 则在 title 前方会多出⼀个可勾选的部分。checked描述分组框的选择状态 (前提是 checkable 为 true) 。

分组框只是⼀个用来 “美化界面” 这样的组件,并不涉及到用户交互和业务逻辑。属于 “锦上添花”。

(3)代码示例:给麦当劳案例加上分组框。

在界面上创建三个分组框,并且在分组框内部创建下拉框和微调框。

注意:在复制粘贴控件的时候,⼀定要先选中对应的父控件再粘贴。

编写 widget.cpp,添加初始化下拉框的代码。

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("雪碧");

}

运行程序观察效果:

1.2 Tab Widget

(1)使用 QTabWidget 实现⼀个带有标签页的控件,可以往里面添加一些 widget。进一步的就可以通过标签页来切换。 核心属性如下:

属性说明tabPosition标签页所在的位置。North 上方。 South 下方。West 左侧。East 右侧。currentIndex当前选中了第几个标签页 (从 0 开始计算) 。currentTabText当前选中的标签页的文本。currentTabName当前选中的标签页的名字。currentTabIcon当前选中的标签页的图标。currentTabToolTip当前选中的标签页的提示信息。tabsCloseable标签页是否可以关闭。movable标签页是否可以移动。

(2)核心信号:

属性说明currentChanged(int)在标签页发生切换时触发,参数为被点击的选项卡编号。tabBarClicked(int)在点击选项卡的标签条的时候触发。参数为被点击的选项卡编号。tabBarDoubleClicked(int)在双击选项卡的标签条的时候触发。参数为被点击的选项卡编号。tabCloseRequest(int)在标签页关闭时触发。参数为被关闭的选项卡编号。

(3)代码示例:使用标签页管理多组控件。

在界面上创建⼀个 QTabWidget 和两个按钮。按钮的 objectName 为 pushButton_add 和 pushButton_remove。

注意:

QTabWidget 中的每个标签页都是⼀个 QWidget 。点击标签页,就可以直接切换。右键 QTabWidget,可以添加标签页或者删除标签页。 编写 widget.cpp,进行初始化,给标签页中放个简单的 label。

注意新创建的 label 的父元素是 ui->tab 和 ui->tab_2 。Qt 中使用父子关系决定该控件 “在哪里”。

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

编写按钮的 slot 函数:

使用 count() 获取到标签页的个数.使用 addTab 新增标签页。使用 removeTab 删除标签页。使用 currentIndex 获取到当前标签页的下标.使用 setCurrentIndex 切换当前标签页。

void Widget::on_pushButton_add_clicked()

{

// 获取当前有⼏个标签⻚了

int count = ui->tabWidget->count();

// 创建新的 widget

QWidget* w = new QWidget();

ui->tabWidget->addTab(w, QString("Tab ") + QString::number(count + 1));

// 给 widget 中添加 label

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

}

编写 QTabWidget 的 currentChanged 函数。

void Widget::on_tabWidget_currentChanged(int index)

{

qDebug() << "当前选中标签⻚为: " << index;

}

运行程序观察效果:

点击新建标签页,可以创建出新的标签。点击删除当前标签页,可以删除标签。切换标签页时,可以看到 qDebug 打印出的标签页编号。

2. 布局管理器

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

也就是每个控件所在的位置,都需要计算坐标,最终通过 setGeometry 或者 move 方式摆放过去。这种设定方式其实并不方便。尤其是界面如果内容比较多,不好计算。而且⼀个窗口大小往往是可以调整的,按照绝对定位的方式,也无法自适应窗口大小。因此 Qt 引入 “布局管理器” (Layout) 机制来解决上述问题。当然布局管理器并非 Qt 独有。其他的 GUI 开发框架,像 Android,前端等也有类似的机制。

2.1 垂直布局

(1)使用 QVBoxLayout 表示垂直的布局管理器,V 是 vertical 的缩写。核心属性如下:

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

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

(2)代码示例:使用 QVBoxLayout 管理多个控件。

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

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

}

运行程序,可以看到此时界面上的按钮就存在于布局管理器中。随着窗口尺寸变化而发生改变。此时三个按钮的尺寸和位置都是自动计算出来的。

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

(3)代码示例:创建两个 QVBoxLayout。

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

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

通过 Qt Designer 创建的布局管理器,其实是先创建了⼀个 widget,设置过 geometry 属性的。再把这个 layout 设置到这个 widget 中。实际上⼀个 widget 只能包含⼀个 layout。打开 ui 文件的原始 xml,可以看到其中的端倪。这种情况下 layout 并非是窗口 widget 的布局管理器,因此不会随着窗口大小改变。

140

140

141

331

PushButton

PushButton

PushButton

2.2 水平布局

(1)使用 QHBoxLayout 表示垂直的布局管理器。H 是 horizontal 的缩写。核心属性和 QVBoxLayout 属性是一致的。

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

(2)代码示例:使用 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,而达到更复杂的布局效果。

(3)代码示例:嵌套的 layout。

在代码中创建以下内容。使用 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);

}

执行程序观察结果:

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

2.3 网格布局

(1)Qt 中还提供了 QGridLayout 用来实现网格布局的效果。可以达到 M * N 的这种网格的效果。核心属性如下:

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

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

(2)代码示例:使用 QGridLayout 管理元素。

代码中创建 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 列的方式排列的。

如果调整行列坐标为下列代码:

// 创建⽹格布局管理器, 并且添加元素

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。

如果调整行列坐标为下列代码:

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。

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

// 创建⽹格布局管理器, 并且添加元素

QGridLayout* layout = new QGridLayout();

layout->addWidget(btn1, 0, 0);

layout->addWidget(btn2, 1, 1);

layout->addWidget(btn3, 2, 2);

layout->addWidget(btn4, 3, 3);

运行结果:

编写代码形如:

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 的情况是相同的。

(3)代码示例:设置 QGridLayout 中元素的大小比例。

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

}

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

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

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

编写代码,创建 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);

}

执行代码观察效果。 此时的按钮垂直方向都舒展开了。并且调整窗口尺寸,也会按照设定的比例同步变化。

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

2.4 表单布局

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

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

(2)代码示例:使用 QFormLayout 创建表单。

编写代码,创建 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.5 Spacer

(1)使用布局管理器的时候可能需要在控件之间添加⼀段空白。就可以使用QSpacerItem 来表示。 核心属性如下:

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

上述属性在构造函数设置即可。

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

在界面上创建⼀个 QVBoxLayout,并添加两个按钮。

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

}

直接运行程序可以看到两个按钮是紧挨着的:

在两个按钮中间添加⼀个 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);

}

运行程序观察代码效果。可以看到两个按钮之间已经存在了间隔了。调整 QSpacerItem 不同的尺寸即可看到不同的间距。

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

3. 容器类控件和布局管理器总结

(1)容器类控件:容器类控件用于组织和容纳其他控件,帮助构建结构化的界面布局。

QGroupBox(分组框):

功能:将相关控件分组,并显示标题边框。 QTabWidget(标签页容器):

功能:通过标签页切换多个子界面,节省空间。信号:

currentChanged(int index):页签切换时触发。

(2)布局管理器总结:布局管理器用于自动排列控件位置,适配不同屏幕尺寸和窗口大小。

QHBoxLayout(水平布局):

功能:水平排列控件(从左到右)。 QVBoxLayout(垂直布局):

功能:垂直排列控件(从上到下)。 QGridLayout(网格布局):

功能:按行列网格排列控件,支持跨行/列。 QFormLayout(表单布局):

功能:两列布局(标签 + 输入控件),适合表单设计。

(3)容器与布局选择建议:

容器控件:

分组显示 → QGroupBox分页切换 → QTabWidget长内容滚动 → QScrollArea动态页面 → QStackedWidget可停靠面板 → QDockWidget 布局管理器:

简单横向排列 → QHBoxLayout简单纵向排列 → QVBoxLayout复杂网格 → QGridLayout表单设计 → QFormLayout