本文属于《QTreeView使用系列教程》之一,欢迎查看其它文章。

一、综述

基于QT的Model/View框架,我们可以自定义model实现数据与View分离。

让数据实现排序的方式有2种:

  1. 在QAbstractItemModel子类中重新实现sort()函数;
  2. 在QSortFilterProxyModel代理model子类中重新实现lessThan()函数,并用该子类代理model来包装原始model。

接下来,我们还是在之前自定义model的,代码基础上进行说明。

二、重写QAbstractItemModel的sort()实现排序

在QAbstractItemModel类中sort函数原型如下:

virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);

参数包括column-需要排序的列号;order-排序方式,升序还是降序。

我们在TreeModel类中实现sort函数,如下:

void TreeModel::sort(int column, Qt::SortOrder order)
{// 关键:发送模型layout即将改变信号emit layoutAboutToBeChanged();// 对每个"省份"下所有"人口"执行排序int provinceCount = _rootItem->childCount();for (int i = 0; i < provinceCount; i++){TreeItem* pro = _rootItem->child(i);pro->sort(column, order);}// 关键:发送模型layout已改变信号emit layoutChanged();
}

其中2个信号很关键:

  1. layoutAboutToBeChanged,在我们修改model内部数据前,进行发送。
  2. layoutChanged,在我们修改model内部数据后,进行发送。

小贴士:

这里引申一下,对于更新自定义model数据的方式,目前所知的有2种:

第一种,是更新前使用treeView->setUpdatesEnabled(false)禁止view从model读取数据,然后更新后,使用treeView->setUpdatesEnabled(true)启用view更新。对于model内部数据结构没有发生改变,纯粹是更新现有结构下的数据,这个方式是可以安全刷新model数据的。而对于需要打乱model内部数据结构,如排序,这种方式是不行的,因为View中还有个ModelIndex索引的概念,原始数据被打乱,而对应的索引没有更新,view显示时会有问题。

第二种,就是上文中提到的,使用这2个信号来包围model数据修改,即便排序打乱了model数据结构,但是,在发送layoutChanged信号后,view就会重建QModelIndex索引,故也可以安全刷新model数据,且比setUpdatesEnabled方式更强大。

引发的思考:在纯粹更新model数据,不改结构情况下,推荐使用setUpdatesEnabled方式,效率高,不用重建索引,不推荐使用信号方式,因为每次更新model都会把所有索引全部重建,这样效率可能要低些。若model结构发生改变,则只能使用信号方式了。

好,我们聊回刚才的话题。

我们需要实现对"省份"下"人口"节点的排序,那么
需要遍历每个"省",对每个"省"的子节点"人"进行排序。类似把人看做int,进行冒泡排序。

在TreeItem类中定义小于函数,实现对2个"人"的比较,只有当item1性别为女,item2性别为男时,表示item1小于item2,如下:

// item1的sex < item2的sex,则返回true
bool TreeItem::lessThan_sex(const TreeItem * item1, const TreeItem * item2)
{Person* left = static_cast<Person*>(item1->ptr());Person* right = static_cast<Person*>(item2->ptr());if (left->sex == "woman" && right->sex == "man") // 女士优先{return true;}return false;
}

_children中是未排序的TreeItems,使用qt排序算法qSort进行升序和降序,调用上面的lessThan_sex(),对sex列执行排序。

QList<TreeItem*> _children;   // 子节点
// 对sex列执行排序
void TreeItem::sort_sex(Qt::SortOrder order)
{if (order == Qt::AscendingOrder) // 升序qSort(_children.begin(), _children.end(), lessThan_sex);else  // 降序qSort(_children.rbegin(), _children.rend(), lessThan_sex);
}

此时,_children中是排序后的TreeItems。

我们实现了对sex列的排序逻辑,接下来如法炮制,实现age列排序逻辑。

定义age列的小于函数:

// item1的age < item2的age,则返回true
bool TreeItem::lessThan_age(const TreeItem *item1, const TreeItem * item2)
{Person* left = static_cast<Person*>(item1->ptr());Person* right = static_cast<Person*>(item2->ptr());return (left->age < right->age);
}

对age列执行排序

// 对age列执行排序
void TreeItem::sort_age(Qt::SortOrder order)
{if (order == Qt::AscendingOrder) // 升序qSort(_children.begin(), _children.end(), lessThan_age);else  // 降序qSort(_children.rbegin(), _children.rend(), lessThan_age);
}

已经具备对age、sex列排序的函数,再进行一下封装,如下:

void TreeItem::sort(int column, Qt::SortOrder order)
{if (_type != PROVINCE) // 对"省份"节点下进行排序return;if (column == COLUMN_SEX)sort_sex(order);else if (column == COLUMN_AGE)sort_age(order);
}

然后,我们在最上面提到的,重写QAbstractItemModel的sort()中,调用TreeItem的sort函数,如下:

void TreeModel::sort(int column, Qt::SortOrder order)
{// 关键:发送模型layout即将改变信号emit layoutAboutToBeChanged();// 对每个"省份"下所有"人口"执行排序int provinceCount = _rootItem->childCount();for (int i = 0; i < provinceCount; i++){TreeItem* pro = _rootItem->child(i);pro->sort(column, order);}// 关键:发送模型layout已改变信号emit layoutChanged();
}

在for循环中实现了排序,是真实的把TreeItem指针在QList中进行了排序,故用信号包围起来。

最后,记得设置允许排序:

treeView->setSortingEnabled(true);

以上说明,默认已掌握自定义model前提下,否则,可能看的不太懂,源码在文章末尾,感兴趣可以自行阅读。

运行效果:

本章涉及工程代码:

文末,公众号回复:34SortExample1,即可下载。

三、重写QSortFilterProxyModel的lessThan()实现排序

上面那种方法,似乎自己实现的多了点,接下来我们看这种更简单的方法。

使用代理model类QSortFilterProxyModel实现排序,我们重写它的lessThan(),如下:

bool SortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{TreeItem *item1 = static_cast<TreeItem*>(left.internalPointer());TreeItem *item2 = static_cast<TreeItem*>(right.internalPointer());if (left.column() == COLUMN_SEX && right.column() == COLUMN_SEX){return TreeItem::lessThan_sex(item1, item2);}else if (left.column() == COLUMN_AGE && right.column() == COLUMN_AGE){return TreeItem::lessThan_age(item1, item2);}return QSortFilterProxyModel::lessThan(left, right);
}

若left<right,那么返回true,否则返回false。
我们只处理COLUMN_SEX、COLUMN_AGE列排序,其他交给基类默认函数处理。

在TreeItem类中实现item比较sex属性的函数,实现item比较age属性的函数,如下:

// item1的sex < item2的sex,则返回true
bool TreeItem::lessThan_sex(const TreeItem * item1, const TreeItem * item2)
{Person* left = static_cast<Person*>(item1->ptr());Person* right = static_cast<Person*>(item2->ptr());if (left->sex == "woman" && right->sex == "man") // 女士优先{return true;}return false;
}// item1的age < item2的age,则返回true
bool TreeItem::lessThan_age(const TreeItem *item1, const TreeItem * item2)
{Person* left = static_cast<Person*>(item1->ptr());Person* right = static_cast<Person*>(item2->ptr());return (left->age < right->age);
}

然后,使用代理model包装我们的原始model,如下:

SortFilterProxyModel* proxy = new SortFilterProxyModel(treeView);
proxy->setSourceModel(model);
treeView->setModel(proxy);

最后,记得设置允许排序:

treeView->setSortingEnabled(true);

运行效果:

本章涉及工程代码:

文末,公众号回复:34SortExample2,即可下载。


若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

《QTreeView表项实现排序的2种方式》:系列教程之九相关推荐

  1. form表单、控制器中接收表单提交数据的4种方式

    Form表单 这篇文章主要讲的是form表单的提交 之前我们接触过的form表单元素是在Bootstrap框架里面,这次也将用到Bootstrap框架去布局(见图表1)通过Bootstrap框架布局呈 ...

  2. mysql排序的四种方式

    mysql排序的四种方式 第一种,默认排序 第二种,field函数排序 第三种,条件排序 第四种,多重条件排序 第一种,默认排序 按照 order by 字段1 desc/asc, 字段2 desc/ ...

  3. java找零钱_求一共有多少种方式系列问题(找零钱)

    求一共有多少种方式系列问题(找零钱问题) 背景: 假设有四种面额的钱币 1 元.2 元.5 元和 10 元,一共给我 10 元 那您可以奖赏我 1 张 10 元,或者 10 张 1 元 或者 5 张 ...

  4. Collections.sort方法对list排序的两种方式

    最近在工作中用到对地震等各种条件的排序,这里刚好用到了这个方法,我用的第二种方法   下面请参考 Collections.sort( )分为两部分,一部分为排序规则,一部分为排序算法 . 规则用来判断 ...

  5. oracle并行parallel update两张表_Oracle并行更新的两种方式(merge/update内联视图)

    对于Oracle的两表联合更新的场景(有A.B两表,以A.id=B.id关联,根据B表中的记录更新A表中的相应字段),一般有update内联视图和merge两种方式,下面举例介绍: 创建用例表: cr ...

  6. php对扑克牌进行排序,C#代码实现扑克牌排序的几种方式

    扑克牌游戏,总是能用到很多的手牌排序,总结了几种方式供参考,顺便记录一下方便以后使用. 我做的这个是由(1-13:黑桃A-K || 14 - 26:红桃 || 27 - 39:梅花 || 39 - 5 ...

  7. 创建一对多表结构实例 /操作的三种方式

    例 1.注册App01  完成各项配置 2. 写完后自动生成一个id自增列(主键) 如果不想生成 自己写 创建两张表 3.执行创建语句 (其中还进行了一个小修改) 4.按照之前的方法 打开数据库 并输 ...

  8. element组件table表格/form表单显示弹窗的三种方式

    element组件table表格显示弹窗的两种方式 <el-table-columnlabel="地址"prop="Address"min-width=& ...

  9. form表单提交数据的两种方式——submit直接提交、AJAX提交

    submit提交 form表单本身提供action属性,在action属性中填写数据提交地址后,点击submit类型的按钮即可将数据提交至指定地址,代码如下: <form action=&quo ...

  10. 爆破专栏丨Spring Security系列教程之Spring Security的四种权限控制方式

    原创:一一哥 前言: 在前面的章节中,一一哥 已经给大家介绍了Spring Security的很多功能,在这些众多功能中,我们知道其核心功能其实就是认证+授权. 在前面我们分别基于内存模型.基于默认的 ...

最新文章

  1. CentOS7.6离线安装JDK1.8
  2. Linux运维:cobbler
  3. 可在网络不好的环境下运行的ENet示例程序
  4. Atitit 数据类型体系图 目录 1. 系统常用的数据类型 1 2. H5 form表单新数据类型 2 2.1. TML5 新的 Input 类型 2 2.2. HTML5 的新的表单元素: 2
  5. acer软件保护卡怎么解除_外观精致性能强,配置丰富重量轻、宏碁(Acer)墨舞EX214轻薄笔记本 深度评测...
  6. 企业个人所得税网上申报系统_增值税、企业所得税、个人所得税申报难点梳理...
  7. 在家或者公司如何登录服务器
  8. 怎么才能写好技术文档——这是我的全部经验
  9. 聊聊索引失效?失效的原因是什么?
  10. 设z服从标准正态分布_设Z服从标准正态分布,则P(Z1.33)=
  11. CPLEX仿真能力可行性分析
  12. svn里 is already locked
  13. C#中(int)、Conver.Toint32()、int.Parse()三种类型转换方式的区别
  14. 2021年中国家具行业现状及未来趋势分析:线上渠道将成为新增长点[图]
  15. 中国本土便利店遍地开花,7-Eleven便利店入华为何“水土不服”?
  16. 聚观早报 | 苹果首款头显明年1月亮相;米哈游投资信托「打水漂」
  17. windows11编译OpenCV4.5.0 with CUDA(附注意事项)
  18. Rapidly-Exploring Random Trees(RRT)
  19. 利用ENVI的ROI统计影像信息
  20. 又一个程序猿的奋斗史——第三章 入职

热门文章

  1. matlab 神经网络训练 分类器,matlab神经网络分类器
  2. 计算机专业英语思维导图
  3. java pdf 中文字体_iText生成pdf中文字体解决方案
  4. 电大计算机本科离散数学考试题,2017年电大本科离散数学期末考试复习试题及答案.doc...
  5. 用netbean搭建第一个struts的web项目
  6. office2019安装D盘_office2016or2019安装在D盘的方法-可收藏备用
  7. HTML关闭网页弹出窗口代码
  8. 程序人生(计算机系统大作业)
  9. 央行数字货币在技术上是如何实现的
  10. IBM Power小型机用前面板液晶屏查看HMC端口IP