目录
前言
一、多元素与容器类控件的核心价值
二、多元素控件:批量数据的高效展示与交互
2.1 ListWidget:简洁高效的列表控件
2.1.1 核心属性与 API
2.1.2 基础用法:简单列表的增删改查
2.1.3 进阶用法:带图标的列表与多选功能
2.1.4 ListWidget 避坑指南
2.2 TableWidget:功能强大的表格控件
2.2.1 核心属性与 API
2.2.2 基础用法:增删改查
2.2.3 TableWidget 避坑指南
2.3 TreeWidget:层级分明的树形控件
2.3.1 核心属性与 API
2.3.1 基础树形结构
2.3.2 TreeWidget 避坑指南
三、容器类控件:界面的有序组织与分组
3.1 GroupBox:控件分组的 “收纳盒”
3.1.1 核心属性与 API
3.1.2 基础用法:表单分组
3.1.3 进阶用法:扁平模式与对齐方式
3.1.4 GroupBox 避坑指南
3.2 TabWidget:多页面切换的 “导航栏”
3.2.1 核心属性与 API
3.2.2 基础用法:功能模块切换
3.2.3 进阶用法:标签页的增加与删除
3.2.4 TabWidget 避坑指南
总结
前言
在 Qt GUI 开发中,当需要展示批量数据或对控件进行分组管理时,单纯的基础控件已无法满足需求。多元素控件(ListWidget/TableWidget/TreeWidget)专为批量数据展示而生,支持列表、表格、树形等多样化数据呈现;容器类控件(GroupBox/TabWidget)则擅长控件分组与界面分区,让复杂界面更具逻辑性和可读性。本文基于 Qt 5.14 版本,以 “属性解析 + 实战案例 + 进阶技巧” 的结构,全面拆解这两类控件的核心用法,带你轻松搞定复杂界面开发!下面就让我们正式开始吧!
一、多元素与容器类控件的核心价值
在实际项目中,我们常面临这些场景:展示一组文件列表、呈现多行多列的表格数据、构建层级化的分类目录、将相关控件归类展示、用标签页切换不同功能模块 —— 这正是多元素与容器类控件的用武之地。
- 多元素控件:聚焦 “数据展示与交互”,支持批量数据的添加、删除、选中、排序等操作,是处理集合数据的核心工具;
- 容器类控件:聚焦 “界面组织与分组”,通过分组、分页等方式优化界面布局,提升用户体验和界面整洁度。
本文将详细讲解 5 个核心控件:
- 多元素控件:ListWidget(列表)、TableWidget(表格)、TreeWidget(树形)
- 容器类控件:GroupBox(分组框)、TabWidget(标签页)
二、多元素控件:批量数据的高效展示与交互
多元素控件的核心优势是“批量管理数据”,Qt 提供了基于 Item 的简化版控件(ListWidget/TableWidget/TreeWidget),无需手动创建 Model,直接操作 Item 即可实现数据展示,开发效率极高。
2.1 ListWidget:简洁高效的列表控件
QListWidget 是垂直列表控件,适用于展示单列批量数据(如文件列表、选项列表等),支持单选、多选、添加、删除等基础操作,用法简洁直观。
2.1.1 核心属性与 API
| 属性 / 方法 | 功能说明 | 实用场景 |
|---|---|---|
| currentRow() | 获取当前选中行的下标(未选中返回 - 1) | 选中数据后获取位置 |
| currentItem() | 获取当前选中的 Item 对象 | 获取选中数据的详细信息 |
| addItem(const QString& text) | 添加单个列表项 | 动态添加数据 |
| addItems(const QStringList& items) | 批量添加列表项 | 初始化批量数据 |
| insertItem(int row, const QString& text) | 在指定行插入列表项 | 插入中间数据 |
| takeItem(int row) | 删除指定行并返回该 Item(需手动释放) | 删除数据并回收资源 |
| setSortingEnabled(bool enable) | 启用 / 禁用排序 | 数据排序展示 |
| itemClicked(QListWidgetItem* item) | 点击列表项时触发的信号 | 点击事件响应 |
核心数据载体:QListWidgetItem,每个 Item 可设置文本、图标、字体、选中状态等,是列表数据的最小单元。
2.1.2 基础用法:简单列表的增删改查
#include "widget.h" #include <QListWidget> #include <QLineEdit> #include <QPushButton> #include <QVBoxLayout> #include <QHBoxLayout> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); this->setWindowTitle("ListWidget基础用法:编程语言列表"); // 布局管理:输入框+按钮(水平布局)+列表(垂直布局) QVBoxLayout *mainLayout = new QVBoxLayout(this); QHBoxLayout *inputLayout = new QHBoxLayout(); // 输入框:用于添加新列表项 QLineEdit *inputEdit = new QLineEdit(this); inputEdit->setPlaceholderText("请输入编程语言名称"); // 按钮:添加、删除 QPushButton *addBtn = new QPushButton("添加", this); QPushButton *delBtn = new QPushButton("删除选中", this); // 列表控件 QListWidget *langList = new QListWidget(this); // 初始化列表数据 QStringList initLangs = {"C++", "Java", "Python", "Qt", "JavaScript", "Golang"}; langList->addItems(initLangs); // 启用排序 langList->setSortingEnabled(true); // 设置选中模式:单选(默认) langList->setSelectionMode(QAbstractItemView::SingleSelection); // 组装布局 inputLayout->addWidget(inputEdit); inputLayout->addWidget(addBtn); inputLayout->addWidget(delBtn); mainLayout->addLayout(inputLayout); mainLayout->addWidget(langList); // 添加按钮点击事件 connect(addBtn, &QPushButton::clicked, this, [=]() { QString text = inputEdit->text().trimmed(); if (!text.isEmpty()) { // 添加新项并自动排序 new QListWidgetItem(text, langList); inputEdit->clear(); } }); // 删除按钮点击事件 connect(delBtn, &QPushButton::clicked, this, [=]() { // 获取当前选中行 int currentRow = langList->currentRow(); if (currentRow != -1) { // 删除选中项(takeItem返回Item,需手动删除避免内存泄漏) QListWidgetItem *item = langList->takeItem(currentRow); delete item; } }); // 列表项点击事件 connect(langList, &QListWidget::itemClicked, this, [=](QListWidgetItem *item) { qDebug() << "选中语言:" << item->text(); }); }2.1.3 进阶用法:带图标的列表与多选功能
ListWidget 支持为 Item 添加图标,且支持多选模式,适用于文件列表、应用列表等场景:
先在.ui文件中创建控件:
编写代码:
#include "widget.h" #include "ui_widget.h" #include <QDebug> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //往这里添加一些元素 ui->listWidget->addItem("C++"); ui->listWidget->addItem("Java"); ui->listWidget->addItem("Python"); //在QListWidgetItem中,可以设置字体属性,设置图标,设置文字大小,设置是否被选中等状态 // ui->listWidget->addItem(new QListWidgetItem("C++")); // ui->listWidget->addItem(new QListWidgetItem("Java")); // ui->listWidget->addItem(new QListWidgetItem("Python")); //也可以在.ui文件中通过图形化界面编辑 } Widget::~Widget() { delete ui; } void Widget::on_pushButton_insert_clicked() { //1.先获取到输入框中的内容 const QString& text =ui->lineEdit->text(); //2.添加到QListWidget中 ui->listWidget->addItem(text); //添加到末尾,如果想添加到中间位置,需要使用insertItem } void Widget::on_pushButton_delete_clicked() { //1.先获取到被选中的元素 int row = ui->listWidget->currentRow(); if(row < 0) { return; } //2.按照行号来删除元素 ui->listWidget->takeItem(row); } void Widget::on_listWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { //通过这个槽函数来感知到变化 if(current != nullptr) { qDebug() << "当前选中的元素:" << current->text(); } if(previous != nullptr) { qDebug() << "上次选中的元素:" << previous->text(); } }运行结果如下:
2.1.4 ListWidget 避坑指南
- 内存泄漏:
takeItem方法会从列表中移除 Item 但不删除,需手动delete回收资源;- 排序不生效:需先调用
setSortingEnabled(true),新增 Item 才会自动排序;- 多选获取:通过
selectedItems()获取所有选中项,返回QList<QListWidgetItem*>;- Item 编辑:需设置
item->setFlags(item->flags() | Qt::ItemIsEditable),默认不可编辑。
2.2 TableWidget:功能强大的表格控件
QTableWidget 是多行多列的表格控件,适用于展示结构化数据(如学生信息表、商品列表、数据统计等),支持单元格编辑、表头自定义、行高列宽调整等功能,是 Qt 中最常用的多元素控件之一。
2.2.1 核心属性与 API
| 属性 / 方法 | 功能说明 | 实用场景 |
|---|---|---|
| setColumnCount(int count) | 设置列数 | 初始化表格结构 |
| setRowCount(int count) | 设置行数 | 初始化表格结构 |
| setHorizontalHeaderItem(int col, QTableWidgetItem* item) | 设置列标题 | 自定义表头文本 / 样式 |
| setItem(int row, int col, QTableWidgetItem* item) | 设置单元格内容 | 填充表格数据 |
| currentRow()/currentColumn() | 获取当前选中单元格的行 / 列下标 | 定位选中位置 |
| insertRow(int row)/insertColumn(int col) | 插入行 / 列 | 动态添加数据 |
| removeRow(int row)/removeColumn(int col) | 删除行 / 列 | 动态删除数据 |
| setEditTriggers(QAbstractItemView::EditTriggers triggers) | 设置编辑触发方式 | 控制单元格是否可编辑 |
| horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode mode) | 设置列宽调整模式 | 自适应列宽 / 固定列宽 |
核心数据载体:QTableWidgetItem,每个单元格对应一个 Item,支持设置文本、图标、对齐方式、字体等。
2.2.2 基础用法:增删改查
在.ui文件中创建控件:
编写代码:
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //创建三行 ui->tableWidget->insertRow(0); ui->tableWidget->insertRow(1); ui->tableWidget->insertRow(2); //创建三列 ui->tableWidget->insertColumn(0); ui->tableWidget->insertColumn(1); ui->tableWidget->insertColumn(2); //给三列设置列名(设置水平方向的表头) ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("学号")); ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("姓名")); ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("年龄")); //给表格中添加数据 ui->tableWidget->setItem(0, 0, new QTableWidgetItem("1001")); ui->tableWidget->setItem(0, 1, new QTableWidgetItem("张三")); ui->tableWidget->setItem(0, 2, new QTableWidgetItem("20")); ui->tableWidget->setItem(1, 0, new QTableWidgetItem("1002")); ui->tableWidget->setItem(1, 1, new QTableWidgetItem("李四")); ui->tableWidget->setItem(1, 2, new QTableWidgetItem("19")); ui->tableWidget->setItem(2, 0, new QTableWidgetItem("1003")); ui->tableWidget->setItem(2, 1, new QTableWidgetItem("王五")); ui->tableWidget->setItem(2, 2, new QTableWidgetItem("18")); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_insertRow_clicked() { //需要知道当前一共有多少行 int rowCount = ui->tableWidget->rowCount(); //在最后一行之后新增新行 //注意此处的参数是“下标”,表示你新增之后的一行是第几行 ui->tableWidget->insertRow(rowCount); } void Widget::on_pushButton_deleteRow_clicked() { //获取到选中的行号 int curRow = ui->tableWidget->currentRow(); //删除这一行 ui->tableWidget->removeRow(curRow); } void Widget::on_pushButton_insertColumn_clicked() { //需要知道当前一共有多少列 int colCount = ui->tableWidget->columnCount(); //在对应位置新增新列 ui->tableWidget->insertColumn(colCount); //设置列名(从输入框中获取到) const QString& text = ui->lineEdit->text(); ui->tableWidget->setHorizontalHeaderItem(colCount, new QTableWidgetItem(text)); } void Widget::on_pushButton_4_clicked() { //获取到选中的列号 int curCol = ui->tableWidget->currentColumn(); //删除这一列 ui->tableWidget->removeColumn(curCol); }运行结果:
2.2.3 TableWidget 避坑指南
- 表头设置:
setHorizontalHeaderLabels需在setColumnCount之后调用,否则表头不生效;- 单元格居中:默认单元格文本左对齐,需手动设置
item->setTextAlignment(Qt::AlignCenter);- 行高列宽:
QHeaderView::Stretch表示自适应拉伸,QHeaderView::Fixed表示固定尺寸;- 编辑控制:
setEditTriggers(QAbstractItemView::NoEditTriggers)可禁用单元格编辑,避免误操作;- 数据获取:通过
item(row, col)获取单元格数据,需判断 Item 是否为nullptr(避免空指针崩溃)。
2.3 TreeWidget:层级分明的树形控件
QTreeWidget 是树形结构控件,适用于展示层级化数据(如文件目录、分类菜单、组织架构等),支持父节点、子节点的嵌套展示,支持展开 / 折叠操作。
2.3.1 核心属性与 API
| 属性 / 方法 | 功能说明 | 实用场景 |
|---|---|---|
| setHeaderLabel(const QString& text) | 设置表头文本(单列) | 单列树形结构 |
| setColumnCount(int count) | 设置列数(多列树形) | 多列树形结构(如文件名称 + 大小) |
| addTopLevelItem(QTreeWidgetItem* item) | 添加顶层节点 | 初始化根节点 |
| QTreeWidgetItem* topLevelItem(int index) | 获取指定顶层节点 | 操作顶层节点 |
| topLevelItemCount() | 获取顶层节点个数 | 遍历顶层节点 |
| item->addChild(QTreeWidgetItem* child) | 为节点添加子节点 | 构建层级结构 |
| item->setExpanded(bool expanded) | 设置节点是否展开 | 默认展开 / 折叠节点 |
| currentItem() | 获取当前选中的节点 | 操作选中节点 |
| itemExpanded(QTreeWidgetItem* item) | 节点展开时触发的信号 | 展开节点时加载子数据 |
核心数据载体:QTreeWidgetItem,每个节点对应一个 Item,支持多列数据,可通过setText(col, text)设置每列内容。
2.3.1 基础树形结构
先创建控件:
编写代码:
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //设置根节点的名字 ui->treeWidget->setHeaderLabel("动物"); //新增顶层结点 QTreeWidgetItem* item1 = new QTreeWidgetItem(); //每个结点都可以设置多个列,此处为了简单就只设置一列 item1->setText(0, "猫"); //添加到顶层结点中 ui->treeWidget->addTopLevelItem(item1); //新增顶层结点 QTreeWidgetItem* item2 = new QTreeWidgetItem(); //每个结点都可以设置多个列,此处为了简单就只设置一列 item2->setText(0, "狗"); //添加到顶层结点中 ui->treeWidget->addTopLevelItem(item2); //新增顶层结点 QTreeWidgetItem* item3 = new QTreeWidgetItem(); //每个结点都可以设置多个列,此处为了简单就只设置一列 item3->setText(0, "鸟"); //添加到顶层结点中 ui->treeWidget->addTopLevelItem(item3); //新增一些子结点 QTreeWidgetItem* item4 = new QTreeWidgetItem(); item4->setText(0, "中华田园猫"); item1->addChild(item4); QTreeWidgetItem* item5 = new QTreeWidgetItem(); item5->setText(0, "布偶猫"); item1->addChild(item5); QTreeWidgetItem* item6 = new QTreeWidgetItem(); item6->setText(0, "暹罗猫"); item1->addChild(item6); } Widget::~Widget() { delete ui; } void Widget::on_pushButton_insertTopLevelItem_clicked() { //获取到输入框中的内容 const QString& text = ui->lineEdit->text(); //构造一个QTreeWidgetItem QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0, text); //添加到顶层结点中 ui->treeWidget->addTopLevelItem(item); } void Widget::on_pushButton_insertItem_clicked() { //获取到当前选中的结点 QTreeWidgetItem* currentItem = ui->treeWidget->currentItem(); if(currentItem == nullptr) { return; } //获取到输入框中的内容 const QString& text = ui->lineEdit->text(); //构造一个QTreeWidgetItem QTreeWidgetItem* item = new QTreeWidgetItem(); item->setText(0, text); //插入到选中结点的子结点中 currentItem->addChild(item); } void Widget::on_pushButton_deleteItem_clicked() { //获取到当前选中的结点 QTreeWidgetItem* currentItem = ui->treeWidget->currentItem(); if(currentItem == nullptr) { return; } //删除选中的元素,需要先获取到父元素,通过父元素进行删除 QTreeWidgetItem* parent = currentItem->parent(); if(parent == nullptr) { //顶层元素 int index = ui->treeWidget->indexOfTopLevelItem(currentItem); ui->treeWidget->takeTopLevelItem(index); } else { //普通元素 parent->removeChild(currentItem); } }运行结果:
2.3.2 TreeWidget 避坑指南
- 节点层级:顶层节点通过
addTopLevelItem添加,子节点通过parentItem->addChild添加;- 路径获取:通过递归遍历
parent()可获取节点的完整路径;- 节点删除:子节点需通过父节点
removeChild删除,顶层节点需通过树形控件takeTopLevelItem删除;- 多列设置:
setColumnCount需在设置表头前调用,setText(col, text)设置对应列内容;- 图标设置:
setIcon(col, icon)设置对应列的图标,默认列索引为 0。
三、容器类控件:界面的有序组织与分组
容器类控件本身不直接展示数据,而是作为 “容器” 管理其他控件,通过分组、分页等方式优化界面结构,让复杂界面更易理解和操作。
3.1 GroupBox:控件分组的 “收纳盒”
QGroupBox 是带标题的分组框,适用于将相关控件归类展示(如表单中的同一模块、选项组等),支持边框显示、标题自定义,还可设置为可勾选模式(勾选后才启用组内控件)。
3.1.1 核心属性与 API
| 属性 / 方法 | 功能说明 | 实用场景 |
|---|---|---|
| setTitle(const QString& title) | 设置分组框标题 | 标识分组用途 |
| setFlat(bool flat) | 设置是否为扁平模式(无边框) | 简洁界面风格 |
| setCheckable(bool checkable) | 设置是否可勾选 | 控制组内控件启用 / 禁用 |
| setChecked(bool checked) | 设置勾选状态 | 默认启用 / 禁用组内控件 |
| setAlignment(Qt::Alignment alignment) | 设置组内控件对齐方式 | 优化组内布局 |
3.1.2 基础用法:表单分组
先创建控件:
运行结果如下:
3.1.3 进阶用法:扁平模式与对齐方式
#include "widget.h" #include <QGroupBox> #include <QPushButton> #include <QHBoxLayout> #include <QVBoxLayout> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); this->setWindowTitle("GroupBox进阶:扁平模式与对齐"); QVBoxLayout *mainLayout = new QVBoxLayout(this); // 扁平模式分组框(无边框) QGroupBox *flatGroup = new QGroupBox("操作按钮", this); flatGroup->setFlat(true); // 扁平模式 QHBoxLayout *flatLayout = new QHBoxLayout(flatGroup); flatLayout->addWidget(new QPushButton("新建", this)); flatLayout->addWidget(new QPushButton("打开", this)); flatLayout->addWidget(new QPushButton("保存", this)); flatLayout->addWidget(new QPushButton("删除", this)); // 右对齐分组框 QGroupBox *alignGroup = new QGroupBox("选项设置", this); alignGroup->setAlignment(Qt::AlignRight); // 组内控件右对齐 QVBoxLayout *alignLayout = new QVBoxLayout(alignGroup); alignLayout->addWidget(new QCheckBox("启用自动保存", this)); alignLayout->addWidget(new QCheckBox("显示状态栏", this)); alignLayout->addWidget(new QCheckBox("允许拖拽", this)); mainLayout->addWidget(flatGroup); mainLayout->addWidget(alignGroup); }3.1.4 GroupBox 避坑指南
- 组内布局:GroupBox 必须设置布局管理器(如 QVBoxLayout),否则内部控件无法正常显示;
- 勾选功能:
setCheckable(true)后,组内控件默认随勾选状态启用 / 禁用,无需手动控制;- 扁平模式:
setFlat(true)会隐藏边框,仅显示标题,适用于简洁界面;- 对齐方式:
setAlignment控制组内控件的整体对齐,而非标题对齐。
3.2 TabWidget:多页面切换的 “导航栏”
QTabWidget 是标签页控件,适用于将不同功能模块放在不同标签页中(如设置界面、编辑界面等),通过切换标签页实现功能切换,节省界面空间,提升界面整洁度。
3.2.1 核心属性与 API
| 属性 / 方法 | 功能说明 | 实用场景 |
|---|---|---|
| addTab(QWidget* page, const QString& label) | 添加标签页(页面 + 标题) | 新增功能模块 |
| insertTab(int index, QWidget* page, const QString& label) | 在指定位置插入标签页 | 调整标签页顺序 |
| removeTab(int index) | 删除指定标签页 | 动态移除模块 |
| currentIndex() | 获取当前选中标签页的下标 | 判断当前激活模块 |
| setCurrentIndex(int index) | 设置当前选中标签页 | 手动切换模块 |
| setTabText(int index, const QString& text) | 修改标签页标题 | 动态更新标题 |
| setTabIcon(int index, const QIcon& icon) | 设置标签页图标 | 美化标签页 |
| setTabsCloseable(bool closeable) | 设置标签页是否可关闭 | 支持动态关闭标签页 |
| currentChanged(int index) | 标签页切换时触发的信号 | 切换模块时执行初始化 |
3.2.2 基础用法:功能模块切换
先创建控件:
运行结果如下:
3.2.3 进阶用法:标签页的增加与删除
编辑.ui文件:
编写代码:
#include "widget.h" #include "ui_widget.h" #include <QLabel> #include <QDebug> 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() << "当前选中的标签页是:" << index; }运行结果如下:
3.2.4 TabWidget 避坑指南
- 页面创建:每个标签页必须是独立的 QWidget,且需设置布局管理器,否则控件无法正常排列;
- 关闭标签页:
setTabsCloseable(true)后,通过tabCloseRequested信号处理关闭逻辑,需手动调用removeTab;- 动态标题:通过
setTabText可动态更新标签页标题,适用于显示实时状态;- 嵌套布局:标签页内部可嵌套任意布局,支持复杂界面设计;
- 切换初始化:
currentChanged信号在标签页切换时触发,可用于初始化当前页面数据。
总结
多元素与容器类控件是 Qt 界面开发的核心工具,掌握它们的使用后,可轻松应对复杂数据展示和界面布局需求。如果本文对你有帮助,欢迎点赞、收藏、转发,如有疑问或建议,欢迎在评论区留言交流~