最近在折腾项目的时候碰到了这个知识点,查了不少资料,索性整理出来分享给大家。
🔥个人主页:Cx330🌸
❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》
《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔
《Git深度解析》:版本管理实战全解 《Qt 极境架构》MySQL 核心技术与实战
🌟心向往之行必能
🎥Cx330🌸的简介:
目录
一、容器类控件:Group Box & Tab Widget(控件分组与切换)
二、布局管理器:界面自适应的核心(垂直 / 水平 / 网格 / 表单布局)
前言:
在桌面 GUI 开发中,界面控件的排版与自适应缩放是极为核心的一环。如果在 Qt 中通过硬编码绝对坐标(如 setGeometry)来放置控件,当窗口尺寸改变或在不同分辨率的屏幕上运行时,界面极易出现错位、遮挡或空白大面积留白等尴尬情况。
为了解决这一问题,Qt 给出了强大的 Layout Manager(布局管理器) 系统。这篇文章结合课堂板书重点,系统梳理 Qt 中的四大核心布局、弹簧机制(Spacer)以及 Qt 控件的面向对象扩展能力。
一、容器类控件:Group Box & Tab Widget(控件分组与切换)
容器类控件的核心作用是对界面上的控件进行分组管理,让界面结构更清晰,同时给出分组交互(如标签页切换),包含 QGroupBox(分组框)和 QTabWidget(标签页控件)两个核心组件。
1.1 Group Box(控件分组框)
QGroupBox 是基础分组控件,通过边框和标题将相关控件归类(如 “用户信息”“订单信息” 分组),支持 “勾选启用” 功能(checkable 属性),仅美化界面、优化结构,不影响控件功能,是界面模块化 的首选。
核心属性与 API:
setTitle(const QString& title):设置分组框标题;setCheckable(bool flag):设置是否支持勾选(勾选后分组内控件才可用);setChecked(bool checked):设置默认勾选状态;setFlat(bool flat):设置是否为扁平模式(无边框,仅显示标题)。
实战示例:麦当劳点餐分组(汉堡 + 小食 + 饮料分组)
将下拉框和微调框按 “汉堡”“小食”“饮料” 分组,界面结构更清晰:这里直接用ui的模式来实现
1.2 Tab Widget(标签页切换控件)
QTabWidget 是多页面切换的核心控件,通过标签页将不同功能模块的控件分离(如 “基本信息”“高级设置” 标签页),节省界面空间,提升交互效率,支持标签页新增、删除、移动,是繁琐界面的必备组件。
核心属性与 API:
标签页管理:
addTab(QWidget* page, const QString& label):新增标签页(page 为标签页对应的控件容器);removeTab(int index):删除指定下标(从 0 开始)的标签页;currentIndex():获取当前选中的标签页下标;setCurrentIndex(int index):切换到指定下标标签页;
setTabsCloseable(bool flag):设置标签页是否支持关闭(显示关闭按钮);setMovable(bool flag):设置标签页是否可拖动调整顺序;
currentChanged(int index):标签页切换时触发(返回新标签页下标);tabCloseRequested(int index):点击标签页关闭按钮时触发。
#include "widget.h"
#include "ui_widget.h"
#include
#include
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);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_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_2_clicked()
{
// 先获取到当前选中的标签页的下标
int index = ui->tabWidget->currentIndex();
// 删除标签页
ui->tabWidget->removeTab(index);
}
void Widget::on_tabWidget_currentChanged(int index)
{
qDebug()addWidget(button3);
// 自定义布局样式
layout->setSpacing(20); // 控件间距20px
layout->setContentsMargins(30, 30, 30, 30); // 布局内边距30px
// 把布局管理器添加到窗口中
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
补充:直接使用ui也是可以做到的,有两种做法、
2.2 QHBoxLayout(水平布局)
控件按水平方向从左到右排列,适用于 “单行控件” 场景(如工具栏按钮、搜索框 + 按钮),API 与垂直布局完全一致,仅排列方向不同。
实战示例1:垂直布局管理三个按钮
#include "widget.h"
#include "ui_widget.h"
#include
#include
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;
}
2.3 QGridLayout(网格布局)
控件按M 行 N 列的网格排列,支持控件跨行列,适用于繁琐界面(如计算器键盘、数据表格表单)
核心 API:
addWidget(QWidget* widget, int row, int column, int rowSpan, int columnSpan):
row/column:控件所在的行 / 列(从 0 开始);
rowSpan/columnSpan:控件跨的行 / 列数(默认 1 行 1 列);
setRowStretch(int row, int stretch):设置行的拉伸系数(系数越大,窗口缩放时占比越大);setColumnStretch(int column, int stretch):设置列的拉伸系数。
#include "widget.h"
#include "ui_widget.h"
#include
#include
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);
// 这个写法就相当于水平布局了
// 此时大家的行数一样就可以,不一定非得是0
// layout->addWidget(btn1, 0, 0);
// layout->addWidget(btn2, 0, 1);
// layout->addWidget(btn3, 0, 2);
// layout->addWidget(btn4, 0, 3);
// 这个写法就相当于是垂直布局了
// layout->addWidget(btn1, 0, 0);
// layout->addWidget(btn2, 1, 0);
// layout->addWidget(btn3, 2, 0);
// layout->addWidget(btn4, 3, 0);
// 这个写法是每个按钮独占一行和一列
// 即使这是成 100,100,也不搞出很大的空白空间
// 此时设置的行数和列数, 只是用来决定控件之前的相对位置
layout->addWidget(btn1, 0, 0);
layout->addWidget(btn2, 1, 1);
layout->addWidget(btn3, 2, 2);
layout->addWidget(btn4, 3, 3);
// layout->addWidget(btn4, 100, 100);
this->setLayout(layout);
}
Widget::~Widget()
{
delete ui;
}
实战示例2:水平拉伸系数
#include "widget.h"
#include "ui_widget.h"
#include
#include
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,2列比例大小为 1:1:2
layout->setColumnStretch(0,1);
layout->setColumnStretch(1,1);
layout->setColumnStretch(2,2);
}
Widget::~Widget()
{
delete ui;
}
实战示例3:垂直拉伸系数(要注意关键步骤)
#include "widget.h"
#include "ui_widget.h"
#include
#include
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");
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);
this->setLayout(layout);
// 设置拉伸系数
layout->setRowStretch(0,1);
layout->setRowStretch(1,1);
layout->setRowStretch(2,2);
}
Widget::~Widget()
{
delete ui;
}
2.4 QFormLayout(表单布局)
专门用于两列表单(左侧标签 + 右侧输入控件),适用于用户信息填写、参数配置等场景,自动对齐标签和输入框,界面整洁规范
核心 API :
addRow(const QString& labelText, QWidget* fieldWidget):添加一行(标签文本 + 输入控件);addRow(QWidget* labelWidget, QWidget* fieldWidget):添加一行(标签控件 + 输入控件);setLabelAlignment(Qt::Alignment alignment):设置标签的对齐方式。
#include "widget.h"
#include "ui_widget.h"
#include
#include
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置成3行2列
QFormLayout* layout = new QFormLayout();
this->setLayout(layout);
// 创建3个label
QLabel* label1 = new QLabel("姓名");
QLabel* label2 = new QLabel("年龄");
QLabel* label3 = new QLabel("电话");
// 创建3个输入框作为第2列
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);
}
Widget::~Widget()
{
delete ui;
}
2.5 QSpacerItem(空白控件)
用于在布局中添加空白区域,实现控件的灵活排版(如按钮居左 / 居右 / 两端对齐),核心属性是 “宽度 / 高度” 和 “拉伸策略”,支持水平和垂直两种空白。
实战示例:按钮之间加上空白部分
#include "widget.h"
#include "ui_widget.h"
#include
#include
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);
}
Widget::~Widget()
{
delete ui;
}
三、 本章模块避坑指南(覆盖所有收尾知识点)
- Tree Widget:
- 多列节点需先设置
setColumnCount(int count),再给节点设置对应列文本。
- 容器控件:
checkable属性为 true 时,需通过isChecked()判断分组是否启用,再控制子控件可用性;
- Tab Widget 新增标签页时,需创建独立的 QWidget 作为容器,否则控件会重叠。
- 一个 Widget 只能设置一个顶层布局,嵌套布局需通过
addLayout添加到父布局; - 网格布局的
**rowSpan**/**columnSpan**参数是 “跨行列数”,不是 “结束行列号”,避免混淆; - 拉伸系数(setRowStretch/setColumnStretch)仅对 “未固定尺寸的控件” 生效,固定尺寸控件不会拉伸。
- Spacer 控件:
QSizePolicy::Expanding作用于水平方向,垂直 Spacer 作用于垂直方向,需按需选择;
- Spacer 的宽度 / 高度是 “最小空白尺寸”,拉伸时会超过该值。
四、 总结
掌握 Qt 布局系统是编写高质量桌面软件的关键一步:
1. 选择合适布局:简单的单向排版选 QHBoxLayout/QVBoxLayout;网格式卡片选 QGridLayout;表单填写选 QFormLayout。
2. 巧妙利用 Spacer:结合 QSpacerItem 与 QSizePolicy 掌控界面的空白拉伸。
3. 面向对象封装:将经常复用的 UI 结构封装成继承自 QWidget 的自定义子类,打造高内聚、低耦合的优雅代码架构。
就写这么多吧,内容比较基础,适合入门回顾。有补充的地方欢迎留言一起完善。
评论 (0)
暂无评论