当前位置: 首页 > news >正文

Qt模型控件:QTreeView应用

QTreeView复选框

  • 一、复选框
    • 1.1 解决方案
      • 1. `mainwindow.h`
      • 2. `mainwindow.cpp`
    • 1.2 进阶功能:实现父项与子项的联动
  • 二、插入多列
    • 2.1 解决方案
    • 2.2 总结
  • 三、插入多列(隐藏)
    • 3.1 实现思路
    • 3.2 完整代码示例

这个功能的核心是利用 Qt Model/View 架构中的**“角色(Role)”**概念。
将通过 QStandardItemModelQStandardItem 来实现,复选框的状态(选中、未选中、半选中)将存储在每个项的 Qt::CheckStateRole 中。

一、复选框

1.1 解决方案

  1. 使用 QStandardItemModel: 这是一个灵活的模型,可以存储 QStandardItem 对象。
  2. 设置 Qt::ItemIsUserCheckable 标志: 对于每个需要复选框的 QStandardItem,必须设置 Qt::ItemIsUserCheckable 标志,告诉 QTreeView 这个项是可以被用户勾选的。
  3. 设置初始复选状态: 使用 QStandardItem::setData() 方法,并指定 Qt::CheckStateRole 角色,来设置项的初始状态(Qt::CheckedQt::Unchecked)。
  4. 连接信号: QTreeView 本身不直接提供复选框状态改变的信号。我们需要连接模型的 dataChanged 信号。当任何项的数据(包括复选状态)发生改变时,这个信号就会被发射。
  5. 处理信号: 在槽函数中,判断数据改变的角色是否为 Qt::CheckStateRole。如果是,就执行相应的操作。

完整代码示例

下面是一个完整的示例,演示了如何创建一个带有复选框的树,并响应复选框状态的变化。

1. mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTreeView>
#include <QStandardItemModel>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:// 槽函数:用于处理复选框状态变化void onItemChanged(QStandardItem *item);private:Ui::MainWindow *ui;QTreeView *m_treeView;QStandardItemModel *m_model;
};
#endif // MAINWINDOW_H

2. mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStandardItem>
#include <QDebug>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("QTreeView 复选框示例");// 1. 创建视图和模型m_treeView = new QTreeView(this);m_model = new QStandardItemModel(this);m_model->setHeaderData(0, Qt::Horizontal, "可选项");m_treeView->setModel(m_model);// --- 核心步骤:创建带复选框的项 ---// 2. 创建顶层项 "水果"QStandardItem *fruitItem = new QStandardItem("水果");// 设置为可被用户勾选fruitItem->setFlags(fruitItem->flags() | Qt::ItemIsUserCheckable);// 设置初始状态为未选中fruitItem->setData(Qt::Unchecked, Qt::CheckStateRole);m_model->appendRow(fruitItem);// 3. 创建子项 "苹果"QStandardItem *appleItem = new QStandardItem("苹果");appleItem->setFlags(appleItem->flags() | Qt::ItemIsUserCheckable);appleItem->setData(Qt::Unchecked, Qt::CheckStateRole);fruitItem->appendRow(appleItem);// 4. 创建子项 "香蕉"QStandardItem *bananaItem = new QStandardItem("香蕉");bananaItem->setFlags(bananaItem->flags() | Qt::ItemIsUserCheckable);bananaItem->setData(Qt::Checked, Qt::CheckStateRole); // 初始状态为选中fruitItem->appendRow(bananaItem);// 5. 创建顶层项 "蔬菜"QStandardItem *vegItem = new QStandardItem("蔬菜");vegItem->setFlags(vegItem->flags() | Qt::ItemIsUserCheckable);vegItem->setData(Qt::Unchecked, Qt::CheckStateRole);m_model->appendRow(vegItem);// 自动展开所有节点m_treeView->expandAll();setCentralWidget(m_treeView);// --- 核心步骤:连接信号和槽 ---// 监听模型的数据变化信号// 注意:QStandardItemModel 的 itemChanged 信号比 QAbstractItemModel 的 dataChanged 信号更方便// 因为它直接传递了发生变化的 QStandardItem 指针connect(m_model, &QStandardItemModel::itemChanged, this, &MainWindow::onItemChanged);
}MainWindow::~MainWindow()
{delete ui;
}// --- 核心步骤:实现槽函数 ---
void MainWindow::onItemChanged(QStandardItem *item)
{// 确保我们只处理复选框状态的变化if (item->isCheckable()) {// 获取当前项的文本QString itemText = item->text();// 获取当前的复选状态Qt::CheckState state = item->checkState();if (state == Qt::Checked) {qDebug() << "项 '" << itemText << "' 被选中了。";// 在这里执行选中项后的操作...} else if (state == Qt::Unchecked) {qDebug() << "项 '" << itemText << "' 被取消选中了。";// 在这里执行取消选中项后的操作...}}
}

1.2 进阶功能:实现父项与子项的联动

一个非常常见的需求是:当父项被勾选时,所有子项都自动被勾选;当所有子项都被勾选时,父项自动被勾选;当子项部分被勾选时,父项显示为半选中状态(Qt::PartiallyChecked)。

要实现这个功能,你需要修改 onItemChanged 槽函数,添加相应的逻辑。

修改后的 onItemChanged 槽函数:

void MainWindow::onItemChanged(QStandardItem *item)
{// 防止在批量修改子项状态时,重复触发 itemChanged 信号,导致无限递归// 断开信号连接disconnect(m_model, &QStandardItemModel::itemChanged, this, &MainWindow::onItemChanged);if (item->isCheckable()) {// --- 1. 如果当前项是父项,更新所有子项 ---if (item->hasChildren()) {Qt::CheckState parentState = item->checkState();for (int i = 0; i < item->rowCount(); ++i) {QStandardItem *child = item->child(i);child->setCheckState(parentState);}}// --- 2. 如果当前项是子项,更新父项 ---else if (item->parent()) {QStandardItem *parent = item->parent();int checkedCount = 0;int uncheckedCount = 0;// 统计所有子项的状态for (int i = 0; i < parent->rowCount(); ++i) {QStandardItem *sibling = parent->child(i);if (sibling->checkState() == Qt::Checked) {checkedCount++;} else if (sibling->checkState() == Qt::Unchecked) {uncheckedCount++;}}// 根据统计结果更新父项状态if (checkedCount == parent->rowCount()) {// 所有子项都被选中parent->setCheckState(Qt::Checked);} else if (uncheckedCount == parent->rowCount()) {// 所有子项都未被选中parent->setCheckState(Qt::Unchecked);} else {// 子项部分被选中parent->setCheckState(Qt::PartiallyChecked);}}// 打印调试信息qDebug() << "项 '" << item->text() << "' 的状态变为: " << (item->checkState() == Qt::Checked ? "Checked" : (item->checkState() == Qt::Unchecked ? "Unchecked" : "PartiallyChecked"));}// 重新连接信号connect(m_model, &QStandardItemModel::itemChanged, this, &MainWindow::onItemChanged);
}
  1. 启用复选框: 对 QStandardItem 设置 Qt::ItemIsUserCheckable 标志。
  2. 设置状态: 使用 item->setData(Qt::CheckState, Qt::CheckStateRole)
  3. 响应变化: 连接 QStandardItemModel::itemChanged 信号到自定义槽函数。
  4. 联动逻辑: 在槽函数中,根据当前项是父项还是子项,编写相应的逻辑来更新其他项的状态。记得在批量更新前断开信号,完成后再重新连接,以避免无限递归。

二、插入多列

QTreeView 控件的一行中插入两个数据,意味着你需要一个至少包含两列的模型(Model)。QTreeView 本身支持多列显示。

2.1 解决方案

  1. 设置模型的列数: 在创建 QStandardItemModel 时,或者之后通过 setColumnCount() 方法,将列数设置为 2
  2. 创建列数据项: 对于要插入的每一行,创建一个 QStandardItem 对象的列表(QList<QStandardItem*>),列表中的每个 QStandardItem 对应一列的数据。
  3. 插入行: 使用 model->appendRow()model->insertRow() 方法,将这个包含多个 QStandardItem 的列表添加到模型中。

完整代码示例

下面的示例演示了如何创建一个两列的 QTreeView,并在一行中插入两个数据。

  1. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTreeView>
#include <QStandardItemModel>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QTreeView *m_treeView;QStandardItemModel *m_model;
};
#endif // MAINWINDOW_H
  1. mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStandardItem>
#include <QList>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("QTreeView 多列示例");// 1. 创建视图和模型m_treeView = new QTreeView(this);m_model = new QStandardItemModel(this);// 2. 设置模型的列数和列标题m_model->setColumnCount(2); // 设置为2列m_model->setHeaderData(0, Qt::Horizontal, "名称");m_model->setHeaderData(1, Qt::Horizontal, "值");// --- 核心步骤:插入包含两个数据的行 ---// 方法一:使用 appendRow() 添加顶层项// 为第一行创建两个 QStandardItemQStandardItem *item1Col1 = new QStandardItem("项目 A");QStandardItem *item1Col2 = new QStandardItem("100");// 创建一个 QList 来存放这一行的所有项QList<QStandardItem*> rowItems1;rowItems1 << item1Col1 << item1Col2;// 将这个列表添加到模型中,作为新的一行m_model->appendRow(rowItems1);// 方法二:使用 insertRow() 在指定位置插入行// 为第二行创建两个 QStandardItemQStandardItem *item2Col1 = new QStandardItem("项目 B");QStandardItem *item2Col2 = new QStandardItem("200");QList<QStandardItem*> rowItems2;rowItems2 << item2Col1 << item2Col2;// 在索引为 1 的位置插入一行(即第二行)m_model->insertRow(1, rowItems2);// --- 进阶:为父项添加多列子项 ---// 父项只需要第一列有内容即可QStandardItem *parentItem = new QStandardItem("父项目");m_model->appendRow(parentItem);// 为父项创建一个两列的子项QStandardItem *childItemCol1 = new QStandardItem("子项目 C");QStandardItem *childItemCol2 = new QStandardItem("300");// 使用父项的 appendRow() 方法parentItem->appendRow(QList<QStandardItem*>() << childItemCol1 << childItemCol2);// 将模型设置到视图m_treeView->setModel(m_model);// 自动调整列宽以适应内容m_treeView->resizeColumnToContents(0);m_treeView->resizeColumnToContents(1);// 展开所有节点m_treeView->expandAll();setCentralWidget(m_treeView);resize(400, 300);
}MainWindow::~MainWindow()
{delete ui;
}
  1. model->setColumnCount(2): 这是最关键的一步,它告诉模型和视图,我们的数据结构是二维的,每行有两个单元格。
  2. QList<QStandardItem*> rowItems: 这个列表是打包一行数据的容器。列表中的第一个 QStandardItem 会放在第一列,第二个会放在第二列,以此类推。
  3. model->appendRow(rowItems): 这个函数接收一个 QList<QStandardItem*>,并将其作为一个完整的行添加到模型的末尾。
  4. parentItem->appendRow(rowItems): 当你要为一个父项添加子项时,同样使用 appendRow(),但这次是在 QStandardItem 对象(父项)上调用。视图会自动将子项显示在父项下方,并正确对齐到相应的列。

2.2 总结

QTreeView 的一行中插入两个数据,本质上是操作一个多列的模型

  • 步骤:
    1. 设置模型列数 setColumnCount(2)
    2. 为一行的每一列创建一个 QStandardItem
    3. 将这些 QStandardItem 放入一个 QList
    4. 使用 model->appendRow(list)parentItem->appendRow(list) 将这一行数据添加到模型中。

这个方法同样适用于插入三列或更多列的数据,只需相应地增加 QStandardItem 的数量即可。

三、插入多列(隐藏)

实现“一行插入两列数据,但只显示第一列,同时能获取两列数据”,核心是隐藏第二列但保留其数据。这样既可以在视觉上不显示第二列,又能在需要时通过模型获取该列的数据。

3.1 实现思路

  1. 创建包含两列的模型(QStandardItemModel),确保两列都有数据。
  2. 通过 QTreeViewsetColumnHidden() 方法隐藏第二列,使其不在界面上显示。
  3. 当需要获取数据时,通过模型的 data() 方法或 item() 方法访问第二列的数据。

3.2 完整代码示例

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTreeView>
#include <QStandardItemModel>
#include <QPushButton>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:// 点击按钮时获取选中行的两列数据void onGetDataClicked();private:Ui::MainWindow *ui;QTreeView *m_treeView;QStandardItemModel *m_model;QPushButton *m_getDataBtn;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStandardItem>
#include <QList>
#include <QVBoxLayout>
#include <QWidget>
#include <QMessageBox>
#include <QModelIndex>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("QTreeView 隐藏列但保留数据示例");// 创建主布局和中心部件QWidget *centralWidget = new QWidget(this);QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);// 1. 创建视图和模型m_treeView = new QTreeView(this);m_model = new QStandardItemModel(this);// 2. 设置模型为2列,并设置列标题(标题也会被隐藏,仅用于内部标识)m_model->setColumnCount(2);m_model->setHeaderData(0, Qt::Horizontal, "名称");  // 第一列标题m_model->setHeaderData(1, Qt::Horizontal, "值");    // 第二列标题(隐藏)// 3. 插入测试数据(每行两列)// 行1:第一列显示"项目A",第二列存储"100"(隐藏)QList<QStandardItem*> row1;row1 << new QStandardItem("项目A") << new QStandardItem("100");m_model->appendRow(row1);// 行2:第一列显示"项目B",第二列存储"200"(隐藏)QList<QStandardItem*> row2;row2 << new QStandardItem("项目B") << new QStandardItem("200");m_model->appendRow(row2);// 行3:父项+子项(子项同样包含两列数据)QStandardItem *parentItem = new QStandardItem("父项目");m_model->appendRow(parentItem);QList<QStandardItem*> childRow;childRow << new QStandardItem("子项目C") << new QStandardItem("300");parentItem->appendRow(childRow);// 4. 设置模型到视图,并隐藏第二列(关键步骤)m_treeView->setModel(m_model);m_treeView->setColumnHidden(1, true);  // 隐藏索引为1的列(第二列)// 优化显示m_treeView->expandAll();  // 展开所有节点m_treeView->resizeColumnToContents(0);  // 自动调整第一列宽度// 5. 添加按钮用于触发获取数据m_getDataBtn = new QPushButton("获取选中行的两列数据", this);connect(m_getDataBtn, &QPushButton::clicked, this, &MainWindow::onGetDataClicked);// 添加控件到布局mainLayout->addWidget(m_treeView);mainLayout->addWidget(m_getDataBtn);setCentralWidget(centralWidget);resize(400, 300);
}MainWindow::~MainWindow()
{delete ui;
}// 槽函数:获取选中行的两列数据
void MainWindow::onGetDataClicked()
{// 获取当前选中项的索引QModelIndex currentIndex = m_treeView->currentIndex();if (!currentIndex.isValid()) {QMessageBox::warning(this, "提示", "请先选中一项");return;}// 获取当前行的索引(row() 是当前项在父项中的行号)int row = currentIndex.row();// 获取当前项的父索引(用于处理子项的情况)QModelIndex parentIndex = currentIndex.parent();// 6. 获取第一列数据(显示的列)QVariant column1Data = m_model->data(m_model->index(row, 0, parentIndex),  // 第一列的索引Qt::DisplayRole);// 7. 获取第二列数据(隐藏的列)QVariant column2Data = m_model->data(m_model->index(row, 1, parentIndex),  // 第二列的索引Qt::DisplayRole);// 显示结果QString info = QString("选中项数据:\n第一列:%1\n第二列:%2").arg(column1Data.toString()).arg(column2Data.toString());QMessageBox::information(this, "数据", info);
}
  1. 隐藏第二列
    通过 m_treeView->setColumnHidden(1, true); 隐藏索引为 1 的列(第二列)。setColumnHidden 只会隐藏列的视觉显示,不会删除数据。

  2. 插入两列数据
    每行数据通过 QList<QStandardItem*> 存储两列内容,例如:

    QList<QStandardItem*> row1;
    row1 << new QStandardItem("项目A") << new QStandardItem("100");  // 第一列显示,第二列隐藏
    m_model->appendRow(row1);
    
  3. 获取隐藏列的数据
    即使列被隐藏,仍可通过模型的 data() 方法结合列索引获取数据:

    // 获取第二列数据(索引为1)
    QVariant column2Data = m_model->data(m_model->index(row, 1, parentIndex), Qt::DisplayRole);
    
    • row:当前选中项在父项中的行号。
    • parentIndex:父项的索引(用于处理树形结构中的子项)。
    • Qt::DisplayRole:获取显示文本(第二列数据以文本形式存储)。
  • 效果说明
    • 界面上只显示第一列数据(如“项目A”“项目B”),第二列完全隐藏。
    • 选中任意项并点击“获取选中行的两列数据”按钮,会弹出对话框显示该行列的两列数据(包括隐藏的第二列)。

这种方式既保证了界面简洁,又保留了数据的完整性,适用于需要“后台存储额外信息但不显示”的场景。

http://www.dtcms.com/a/481617.html

相关文章:

  • selenium常用的等待有哪些?
  • 基于51单片机水位监测控制自动抽水—LCD1602
  • 电脑系统做的好的几个网站wordpress主题很卡
  • 数据结构和算法篇-环形缓冲区
  • iOS 26 性能分析深度指南 包含帧率、渲染、资源瓶颈与 KeyMob 协助策略
  • vs网站建设弹出窗口代码c网页视频下载神器哪种最好
  • Chrome性能优化秘籍
  • 【ProtoBuffer】protobuffer的安装与使用
  • Jmeter+badboy环境搭建
  • ARM 总线技术 —— AMBA 入门
  • 【实战演练】基于VTK的散点凹包计算实战:从代码逻辑到实现思路
  • Flink 状态设计理念(附源码)
  • 23种设计模式——备忘录模式(Memento Pattern)
  • 【LeetCode】73. 矩阵置零
  • 网站开发教材男通网站哪个好用
  • 《3D草原场景技术拆解:植被物理碰撞与多系统协同的6个实战方案》
  • 软件测试—BUG篇
  • OpenAI系列模型介绍、API使用
  • 做网站的可以信吗深圳商城网站建设
  • 关于使用docker部署srs服务器的相关指令
  • 基于M序列编码的水下微弱目标检测方法
  • Ubuntu SSH 免密码登陆
  • vue前端面试题——记录一次面试当中遇到的题(8)
  • FastbuildAI后端WebModule模块注册分析
  • 南昌网站排名网站站群建设方案
  • day9 cpp:运算符重载
  • Qoder上线提示词增强功能,将开发者从 “提示词“ 的负担中解放出来
  • 「机器学习笔记15」深度学习全面解析:从MLP到LSTM的Python实战指南
  • 在ARM版MacBook上构建lldb-mi
  • php网站后台搭建html代码大全简单