QSortFilterProxyModel实现筛选与排序&固定行号(垂直表头序号)

上一篇我们讲述了数据更新相关:

(43条消息) QAbstractTableModel使用详解&数据单条更新&整体更新_恭德的博客-CSDN博客https://blog.csdn.net/luo_bin2010/article/details/125100508?spm=1001.2014.3001.5501这篇我们讲述下筛选与排序功能,里面有一个很尴尬的事情,就是排序之后,用了QSortFilterProxyModel,发现排序的时候,垂直表头的序号也会跟着变动,很难受,网上找了好多都没有找到,最后慢慢摸索出来了,供朋友们分享。言归正传,下面我们进入主题。

【主题】筛选与排序功能的实现

1、入门

QT还是很强大的,他自己有一套自己的排序机制,叫做代理model,即QSortFilterProxyModel。

只需要把实际加载数据的model设置给 QSortFilterProxyModel,然后实现两个函数,就能实现简单的排序啦。

//筛选函数,有两个,从名字很容易区分,一个是筛选列,一个筛选行,我们这里选筛选列的实现。
virtual bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const    //筛选行
virtual bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const //筛选列
//排序函数有一个
virtual bool lessThan(const QModelIndex & left, const QModelIndex & right) const
//【重要!!】触发筛选动作的函数
void invalidateFilter()

了解了如上函数,接下来我们就可以开始写代码了。

(1)自定义CustomSortFilterProxyModel 类,继承自QSortFilterProxyModel。CustomSortFilterProxyModel.h如下

#include <QSortFilterProxyModel>
class CustomSortFilterProxyModel : public QSortFilterProxyModel
{
public:
    explicit CustomSortFilterProxyModel(QObject *parent = nullptr)
        :QSortFilterProxyModel(parent) {}
    //自定义函数,一般定义你要筛选的内容。
    void setFilterContent(const QString &strFilter);

protected:
    //筛选列
    virtual bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const;
    //排序
    virtual bool lessThan(const QModelIndex & left, const QModelIndex & right) const; 

private:
    QString m_strFilterString;
};

(2)CustomSortFilterProxyModel.cpp如下

#include "CustomSortFilterProxyModel.h"

CustomSortFilterProxyModel::CustomSortFilterProxyModel(QObject *parent):QSortFilterProxyModel(parent) 
{
}
//自定义函数,一般定义你要筛选的内容。
void CustomSortFilterProxyModel::setFilterContent(const QString &strFilter)
{
    m_strFilterString = strFilter;
    invalidateFilter();    //一定要调用这个才会重新筛选
}

//筛选列
bool CustomSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{
    QModelIndex source_index = sourceModel()->index(source_row,0,source_parent);
    if(m_strFilterString.isEmpty() || !source_index.isValid()) {
        return true;
    }
    //写你筛选的内容
    QString strValue = source_index.data().toString();    //这是从sourcemodel取的数据,第0列
    //包含了就返回true,就会显示,返回false就不显示。也就是QT框架会自己遍历所有的行,即source_row,
    //通过判断是否包含这个字段,来判断是否显示,函数返回值为true就是该行显示,false就是不显示。这样说应该比较清楚了吧,这样就简单实现了筛选第一列的数据。
    return strValue.contains(m_strFilterString,Qt::CaseInsensitive);
    
}
//排序
bool CustomSortFilterProxyModel::lessThan(const QModelIndex & left, const QModelIndex & right) const
{
    //如果都是QString类型,就比较简单。通过比较left,right数据大小就行了。QT框架也会遍历单列所有数据,其实可以理解为冒泡排序一样。这个函数只是告诉他其中的两个元素的大小而已。
    return left.data().toString() < right.data().toString();
}

(3)按照如下方法就可以简单使用啦

实际保存数据的model上一节已经写了,大家可以参考上一节。
QTableview *table = new QTableview;    //假设我们创建的是一个表格
CustomTableModel * model = New CustomTableModel(table);
CustomSortFilterProxyModel * proxymodel = new CustomSortFilterProxyModel(table);

model.updatedata(XXXX);    //更新model数据。
proxymodel.setSourceModel(model)    //把数据model设置给代理model
table->setModel(proxymodel);    //把代理model设置给视图,这里是QTableview,也可以是其他

2、进阶——动态筛选不同列&& 按数据类型排序 &&固定行号

(1)上面是简单的筛选和排序,那么如果我想动态筛选不同列怎么办?很简单,可以制作一个一个筛选列(QComboBox),和筛选内容(QLineEdit)。代码如下(注意,是在上面的基础上修改或追加函数,只写变动内容)

//在CustomSortFilterProxyModel类中追加自定义函数和变量m_filterColumn 。
void CustomSortFilterProxyModel::setFilterColumn(const int &colFilter)
{
    m_filterColumn = colFilter;
    invalidateFilter();    //如果你想在选择列的时候也触发筛选,就可以调用这个函数,否则可以不用调用
}

//实现要稍微改一下
//筛选列
bool CustomSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{
    QModelIndex source_index = sourceModel()->index(source_row,0,source_parent);
    if(m_strFilterString.isEmpty() || !source_index.isValid()) {
        return true;
    }
    //修改的内容,追加了哪一列
    QString strValue = source_index.sibling(source_index.row(),m_filterColumn).data().toString();    
    
    return strValue.contains(m_strFilterString,Qt::CaseInsensitive);
    
}

(2)按数据类型排序。

如果按照转换为QString,排序会有一个问题,就是如果是int类型或者double类型,1,2,3,4,11,13排序之后会变为1,11,13,2,3,4。这就不符合我们的要求了,怎么做呢,简单。只要知道对应列的类型,然后转换为对应列数据类型进行比较就可以了。

//排序修改
bool CustomSortFilterProxyModel::lessThan(const QModelIndex & left, const QModelIndex & right) const
{
    //转换为对应的数据类型,不过在这之前需要在数据model中,追加字段来获取对应的数据类型。
     //CustomModelDataTypeRole是自定义的role,用来获取数据的类型。
    QString datatype = left.data(CustomModelDataTypeRole).toString().toLower();//不想区分大小写可以转lower   
    //区分数据
    if("string" == datatype) {
        return left.data().toString() < right.data().toString();
    } else if ("double" == datatype) {
        return left.data().toDouble() < right.data().toDouble();
    } else if ("int" == datatype) {
        return left.data().toInt() < right.data().toInt();
    } else if ("bool" == datatype) {
        return left.data().toBool() < right.data().toBool();
    } 
    //其他类型也可以自己写,也可以默认转string
    return left.data().toString() < right.data().toString();
}

//顺便提一下上篇讲到的,数据model中需要追加字段。
QVariant CustomTableModel::data(const QModelIndex & index, int role /*= Qt::DisplayRole*/) const
{
    if(!index.isValid()) {
        return QVariant();
    }
 
    int row = index.row();
    if(row < m_data.count()) {
        if(Qt::DisplayRole == role || Qt::EditRole == role) {  
            //这些实现上一篇都有,这里我只写数据类型变更内容,如下
        } else if(CustomModelDataTypeRole == role) {    //可以根据role不同返回需要的值,比如这些值不需要显示
            return m_datatypelist[row];    //可以自己写个list或者什么来保存对应列的数据类型
        } 
        
    }

(3)固定行号(垂直表头序号)

如果我们实现了排序,但是在排序不同列的时候,垂直表头的序号也会变更,我要固定怎么办?找了好多都没找到,最后想通了,不就是表头的数据我给按照行号塞进去就行。所以很简单,上代码。在自定义筛选类中追加headerData函数。如下

//追加headerData函数
QVariant CustomSortFilterProxyModel::headerData(int section,Qt::Orientation orientation,int role) const
{
    //如果是垂直表头,不到sourcemodel(数据model里面去找了),因为找数据model里面就是数据model的行号。所以才会跟着变化。
    if(Qt::Vertical == orientation && Qt::DisplayRole == role) {
        return QString::number(section +1);    //section是从0开始的
    } 
    //其他表头的内容还是从sourcemodel去找
    return sourceModel()->headerData(section,orientation,role);
}

效果:

https://live.csdn.net/v/216427icon-default.png?t=M4ADhttps://live.csdn.net/v/216427

码字不容易,喜欢就点个赞吧!有问题欢迎交流。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值