QTreeView使用总结--序

本文详细介绍QTreeView的使用技巧,包括模型视图框架、选中信号处理、自定义代理模型及美化等,帮助读者掌握该组件的高级操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,简介

在Qt开发过程中,我发现树控件即QTreeView使用的非常频繁。

各种批量展示和编辑信息的地方,都用得上该控件。

我的项目在使用QTreeView过程中,用到了各种常规、不常规的功能,并进行过各种改造。

这里将这些知识与技巧作一个总结,分享给大家。

2,《QTreeView使用总结》目录

该系列文章暂拟了下列内容:

1,QTreeView一个简单使用示例

2,常用API介绍

3,表头、行列相关的设置方法

4,Model/View框架介绍

5,选中信号处理

6,单击双击的信号与事件

7,右键菜单

8,风格美化,使用qss样式表

9,使用委托示例:定制item输入效果

10,使用委托示例:定制item显示效果

11,数据过滤,使用代理model,简单过滤

12,数据过滤,使用代理model,自定义过滤条件

13,自定义model示例,大大优化性能和内存

14,自定义model示例,控制对齐和颜色

15,自定义model示例,支持item可编辑

16,定时刷新后,当前选中行的处理

17,改造QTreeView,实现点击空白处取消选中

18,点击空白处取消选中,简单方法

19,添加、删除、清空功能示例

3,源码下载

链接:https://pan.baidu.com/s/1jYEb3SKgIwPnSIsbCiAweg?pwd=ib2l 
提取码:ib2l

若链接失效,也可在Qt学习群文件下载,群号见主页介绍。

<think>我们正在讨论如何为QTreeView实现自定义排。根据引用,Qt提供了两种排方式:一种是使用内置的sortByColumn()和setSortingEnabled(),另一种是自定义排逻辑。当默认的按字符串排不满足需求时,我们需要自定义排规则。 引用[1]提到,默认排是对item的内容(字符串)进行排。如果我们需要根据其他规则(如数值、自定义数据类型等)排,就需要自定义排。 引用[4]展示了如何通过重写排函数来实现自定义排。在该例子中,对TreeItem的_children列表使用qSort进行排,排依据是自定义的比较函数lessThan_sex(根据性别排)。 在Qt中,自定义排主要有两种方式: 1. 在模型(Model)中重写`sort`函数。 2. 使用代理模型(如QSortFilterProxyModel)并重写`lessThan`函数。 下面分别介绍这两种方法: ### 方法1:在自定义模型中重写sort函数(适用于QAbstractItemModel派生的模型) 如果你使用的是QStandardItemModel,你可以通过继承并重写sort函数来实现自定义排。 步骤: 1. 创建一个继承自QStandardItemModel(或QAbstractItemModel)的自定义模型类。 2. 重写`sort`函数,在该函数中实现自定义排逻辑。 示例代码: ```cpp class CustomSortModel : public QStandardItemModel { public: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override { // 在这里实现自定义排逻辑 // 例如,根据列的不同采用不同的排规则 if (column == 0) { // 第一列按字符串排 QStandardItemModel::sort(column, order); } else if (column == 1) { // 第二列按数值排 // 自定义排逻辑 // 获取根节点的所有子项(注意:根节点是QModelIndex()) int rowCount = this->rowCount(); QList<QStandardItem*> items; for (int i = 0; i < rowCount; ++i) { items.append(this->item(i, column)); } // 使用std::sort进行排 std::sort(items.begin(), items.end(), [order](QStandardItem* a, QStandardItem* b) { // 这里假设第二列存储的是数字 bool lessThan = a->text().toInt() < b->text().toInt(); return (order == Qt::AscendingOrder) ? lessThan : !lessThan; }); // 重新排列行(注意:这里只处理了同一级节点,树状结构需要递归处理?) // 注意:重新排列行需要小心,因为树状结构可能有子节点。但QStandardItemModel的sort函数默认只对顶层节点排,子节点排需要递归处理。 // 实际上,我们通常只对当前层级排,而Qt的默认行为也是只对当前层级排(即点击表头时只对顶层节点排,子节点保持原顺)。 // 因此,这里我们只对顶层节点排。 // 重新组织行顺 QList<QList<QStandardItem*>> rows; for (QStandardItem* it : items) { int row = it->row(); QList<QStandardItem*> rowItems = takeRow(row); // 取出这一行 rows.append(rowItems); } // 将排好的行重新插入 for (const QList<QStandardItem*>& row : rows) { appendRow(row); } } } }; ``` 但是,上面的方法比较复杂,而且只处理了顶层节点。实际上,Qt的模型视图框架中,排函数应该能够处理树形结构,即每个父节点下的子节点分别排。 更常见的做法是使用代理模型进行排,这样不需要改变原始模型。 ### 方法2:使用QSortFilterProxyModel并重写lessThan函数(推荐) 步骤: 1. 创建一个继承自QSortFilterProxyModel的类。 2. 重写`lessThan`函数,在该函数中定义两个模型索引的比较规则。 示例代码: ```cpp class CustomSortProxyModel : public QSortFilterProxyModel { protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override { // 获取数据 QVariant leftData = sourceModel()->data(left); QVariant rightData = sourceModel()->data(right); // 根据列的不同采用不同的比较规则 if (left.column() == 1) { // 假设第二列是数值 return leftData.toInt() < rightData.toInt(); } else if (left.column() == 2) { // 第三列可能是自定义类型 // 这里举例:按照字符串长度排 return leftData.toString().length() < rightData.toString().length(); } // 默认按字符串排 return leftData.toString() < rightData.toString(); } }; ``` 使用方式: ```cpp // 原始模型 QStandardItemModel *model = new QStandardItemModel(this); // 填充模型... // 创建代理模型 CustomSortProxyModel *proxyModel = new CustomSortProxyModel(this); proxyModel->setSourceModel(model); // 将代理模型设置到视图 ui->treeView->setModel(proxyModel); // 启用点击表头排 ui->treeView->setSortingEnabled(true); ``` 这样,当用户点击表头时,就会调用我们自定义的`lessThan`函数进行排。 ### 注意事项: 1. 树形结构的排:`lessThan`函数比较的是同一父节点下的两个子节点。代理模型会自动处理树形结构,即每个父节点下的子节点分别排。 2. 性能:对于大型树,排可能会比较慢。如果数据量很大,可以考虑在数据模型中实现排(比如在数据库层面排),或者使用多线程。 3. 自定义数据角色:如果数据不是存储在DisplayRole,比如在UserRole中,那么我们需要在`lessThan`函数中通过指定角色来获取数据。 ```cpp QVariant leftData = sourceModel()->data(left, Qt::UserRole+1); ``` 4. 引用[4]中提到的qSort在Qt5.15中已被弃用,建议使用std::sort。 ### 总结 在Qt中实现QTreeView的自定义排,推荐使用QSortFilterProxyModel并重写lessThan方法,这种方法分离了数据和视图,且不破坏原始模型,更加灵活。 如果原始模型是QStandardItemModel,并且你希望在该模型中直接实现排,可以重写sort函数,但要注意处理树形结构的层级。 根据引用[3]和引用[4],我们还可以通过自定义QHeaderView来实现更复杂的排交互,但通常重写lessThan已经足够。 最后,记得在视图上启用排:`ui->treeView->setSortingEnabled(true);`。 §§相关问题§§ 1. 如何在自定义排中处理树形结构的层级关系? 2. 当使用代理模型进行排时,如何保持展开状态? 3. 如何实现多列联合排(例如先按列A排,再按列B排)? 4. 自定义排时如何避免性能问题(例如超过10万行数据)?</think>### Qt QTreeView 自定义排数据实现方法 在 Qt 中实现 QTreeView 的自定义排,主要有两种核心方法:通过模型类重写排函数或使用代理模型。以下是详细实现方案: --- #### 方法 1:在自定义模型中重写 sort() 函数(推荐) 适用于从 QAbstractItemModel 派生的自定义模型: ```cpp class CustomTreeModel : public QStandardItemModel { public: void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override { // 调用基类方法处理默认排 QStandardItemModel::sort(column, order); // 自定义排逻辑 if (column == 2) { // 对特定列进行自定义排 // 获取根节点 for (int i = 0; i < rowCount(); ++i) { QStandardItem* rootItem = item(i); sortChildren(rootItem, column, order); } } } private: void sortChildren(QStandardItem* parent, int column, Qt::SortOrder order) { if (!parent || parent->rowCount() == 0) return; // 获取所有子节点 QList<QStandardItem*> children; for (int row = 0; row < parent->rowCount(); ++row) { children.append(parent->child(row, column)); } // 自定义排规则(示例:按数值排) std::sort(children.begin(), children.end(), [order](QStandardItem* a, QStandardItem* b) { bool lessThan = a->data(Qt::UserRole).toInt() < b->data(Qt::UserRole).toInt(); return (order == Qt::AscendingOrder) ? lessThan : !lessThan; }); // 重新排列子节点 QList<QList<QStandardItem*>> sortedRows; for (QStandardItem* item : children) { sortedRows.append(parent->takeRow(item->row())); } for (const auto& row : sortedRows) { parent->appendRow(row); } // 递归排子节点的子节点 for (int row = 0; row < parent->rowCount(); ++row) { sortChildren(parent->child(row), column, order); } } }; // 使用示例 CustomTreeModel* model = new CustomTreeModel; ui->treeView->setModel(model); ui->treeView->setSortingEnabled(true); // 启用表头排 ``` **关键点说明**: 1. 重写 `sort()` 函数实现自定义排逻辑 2. 使用 `Qt::UserRole` 存储自定义排数据 3. 递归处理子节点确保整个树结构排正确 4. 通过 `std::sort` 配合 lambda 表达式实现灵活排规则[^4] --- #### 方法 2:使用 QSortFilterProxyModel 实现排 适用于不修改原始模型的情况: ```cpp class CustomSortProxy : public QSortFilterProxyModel { protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const override { // 获取自定义排数据(存储在UserRole) QVariant leftData = sourceModel()->data(left, Qt::UserRole); QVariant rightData = sourceModel()->data(right, Qt::UserRole); // 自定义比较逻辑(示例:按优先级排) if (leftData.isValid() && rightData.isValid()) { return leftData.toInt() < rightData.toInt(); } // 默认文本排 return QSortFilterProxyModel::lessThan(left, right); } }; // 使用示例 QStandardItemModel* sourceModel = new QStandardItemModel; CustomSortProxy* proxyModel = new CustomSortProxy; proxyModel->setSourceModel(sourceModel); ui->treeView->setModel(proxyModel); ui->treeView->setSortingEnabled(true); ``` **优势**: - 无需修改原始模型 - 自动处理树形结构层级关系 - 支持动态排(数据变化时自动重新排)[^1] --- #### 自定义排数据存储 在模型中存储自定义排数据: ```cpp // 创建带自定义数据的项 QStandardItem* item = new QStandardItem("Item Text"); // 存储排用的数值(UserRole) item->setData(42, Qt::UserRole); // 存储优先级标记(UserRole+1) item->setData(true, Qt::UserRole + 1); ``` --- #### 排触发方式 1. **程化排**: ```cpp ui->treeView->sortByColumn(2, Qt::DescendingOrder); ``` 2. **用户点击表头排**: ```cpp // 启用表头排功能 ui->treeView->setSortingEnabled(true); // 设置表头可点击 ui->treeView->header()->setSectionsClickable(true); ``` --- #### 性能优化技巧 1. 对于大型数据集: ```cpp model->layoutAboutToBeChanged(); // 开始批量更新 // 执行排操作... model->layoutChanged(); // 结束批量更新 ``` 2. 使用 `beginResetModel()`/`endResetModel()` 减少界面刷新 3. 对超过 10,000 个节点的树结构,考虑分页加载或延迟排 --- #### 示例场景:按文件类型和大小排 ```cpp bool lessThan(const QModelIndex &left, const QModelIndex &right) const { // 获取文件类型(UserRole) QString leftType = sourceModel()->data(left, Qt::UserRole).toString(); QString rightType = sourceModel()->data(right, Qt::UserRole).toString(); // 获取文件大小(UserRole+1) int leftSize = sourceModel()->data(left, Qt::UserRole+1).toInt(); int rightSize = sourceModel()->data(right, Qt::UserRole+1).toInt(); // 先按类型排,类型相同则按大小排 if (leftType != rightType) { return leftType < rightType; } return leftSize < rightSize; } ``` --- #### 注意事项 1. **树形结构排**:确保排逻辑正确处理父子关系,子节点应随父节点一起移动 2. **数据一致性**:当自定义排数据变化时调用 `dataChanged()` 信号通知视图更新 3. **代理模型**:使用 `QSortFilterProxyModel` 时,原始索引需通过 `mapToSource()`/`mapFromSource()` 转换[^3] 4. **性能监控**:对超大型数据集实现进度指示器,避免界面卡顿
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逆枫゛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值