上一篇我们讲述了数据更新相关:
(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/216427https://live.csdn.net/v/216427
码字不容易,喜欢就点个赞吧!有问题欢迎交流。