首页
学习
活动
专区
圈层
工具
发布
清单首页Qt文章详情

【从零开始的Qt开发指南】(十一)Qt常用控件之多元素控件与容器类控件深度解析


前言

在 Qt GUI 开发中,当需要展示批量数据或对控件进行分组管理时,单纯的基础控件已无法满足需求。多元素控件(ListWidget/TableWidget/TreeWidget)专为批量数据展示而生,支持列表、表格、树形等多样化数据呈现;容器类控件(GroupBox/TabWidget)则擅长控件分组与界面分区,让复杂界面更具逻辑性和可读性。本文基于 Qt 5.14 版本,以 “属性解析 + 实战案例 + 进阶技巧” 的结构,全面拆解这两类控件的核心用法,带你轻松搞定复杂界面开发!下面就让我们正式开始吧!


一、多元素与容器类控件的核心价值

在实际项目中,我们常面临这些场景:展示一组文件列表、呈现多行多列的表格数据、构建层级化的分类目录、将相关控件归类展示、用标签页切换不同功能模块 —— 这正是多元素与容器类控件的用武之地。

  • 多元素控件:聚焦 “数据展示与交互”,支持批量数据的添加、删除、选中、排序等操作,是处理集合数据的核心工具;
  • 容器类控件:聚焦 “界面组织与分组”,通过分组、分页等方式优化界面布局,提升用户体验和界面整洁度。

本文将详细讲解 5 个核心控件:

  1. 多元素控件:ListWidget(列表)、TableWidget(表格)、TreeWidget(树形)
  2. 容器类控件: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 基础用法:简单列表的增删改查

代码语言:javascript
代码运行次数:0
复制
#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文件中创建控件:

编写代码:

代码语言:javascript
代码运行次数:0
复制
#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 避坑指南

  1. 内存泄漏takeItem方法会从列表中移除 Item 但不删除,需手动delete回收资源;
  2. 排序不生效:需先调用setSortingEnabled(true),新增 Item 才会自动排序;
  3. 多选获取:通过selectedItems()获取所有选中项,返回QList<QListWidgetItem*>
  4. 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文件中创建控件:

编写代码:

代码语言:javascript
代码运行次数:0
复制
#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 避坑指南

  1. 表头设置setHorizontalHeaderLabels需在setColumnCount之后调用,否则表头不生效;
  2. 单元格居中:默认单元格文本左对齐,需手动设置item->setTextAlignment(Qt::AlignCenter)
  3. 行高列宽QHeaderView::Stretch表示自适应拉伸,QHeaderView::Fixed表示固定尺寸;
  4. 编辑控制setEditTriggers(QAbstractItemView::NoEditTriggers)可禁用单元格编辑,避免误操作;
  5. 数据获取:通过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 基础树形结构

先创建控件:

编写代码:

代码语言:javascript
代码运行次数:0
复制
#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 避坑指南

  1. 节点层级:顶层节点通过addTopLevelItem添加,子节点通过parentItem->addChild添加;
  2. 路径获取:通过递归遍历parent()可获取节点的完整路径;
  3. 节点删除:子节点需通过父节点removeChild删除,顶层节点需通过树形控件takeTopLevelItem删除;
  4. 多列设置setColumnCount需在设置表头前调用,setText(col, text)设置对应列内容;
  5. 图标设置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 进阶用法:扁平模式与对齐方式

代码语言:javascript
代码运行次数:0
复制
#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 避坑指南

  1. 组内布局:GroupBox 必须设置布局管理器(如 QVBoxLayout),否则内部控件无法正常显示;
  2. 勾选功能setCheckable(true)后,组内控件默认随勾选状态启用 / 禁用,无需手动控制;
  3. 扁平模式setFlat(true)会隐藏边框,仅显示标题,适用于简洁界面;
  4. 对齐方式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文件:

编写代码:

代码语言:javascript
代码运行次数:0
复制
#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 避坑指南

  1. 页面创建:每个标签页必须是独立的 QWidget,且需设置布局管理器,否则控件无法正常排列;
  2. 关闭标签页setTabsCloseable(true)后,通过tabCloseRequested信号处理关闭逻辑,需手动调用removeTab
  3. 动态标题:通过setTabText可动态更新标签页标题,适用于显示实时状态;
  4. 嵌套布局:标签页内部可嵌套任意布局,支持复杂界面设计;
  5. 切换初始化currentChanged信号在标签页切换时触发,可用于初始化当前页面数据。

总结

多元素与容器类控件是 Qt 界面开发的核心工具,掌握它们的使用后,可轻松应对复杂数据展示和界面布局需求。如果本文对你有帮助,欢迎点赞、收藏、转发,如有疑问或建议,欢迎在评论区留言交流~

下一篇
举报
领券