QTableView自定义Model实现排序

(2020-8-26 更新)

排序是一个常用的功能,QTableView 也可以点击表头进行排序,相关接口:

//允许点击排序
ui->tableView->setSortingEnabled(true);

//按第0列升序
ui->tableView->sortByColumn(0,Qt::AscendingOrder);  

但只对 QTableView 进行设置还不能生效,需要借助 QAbstractItemModel 类的 sort 接口(需要重写 sort 接口进行排序),或者借助  QSortFilterProxyModel 类(可以重写 lessThan 接口自定义排序规则)。

Qt 提供的 QAbstractItemModel 及其派生类是有 sort 接口的,但是没有实现,对于一些简单的排序,可以继承并重写这个虚函数:

#include 
//自定义Model
class MyTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    //... ...
    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
    {
        if(modelData.isEmpty()||modelData.first().count()<=column)
            return;
        //判断升序降序
        const bool is_asc = (order == Qt::AscendingOrder);
        //排序
        std::sort(modelData.begin(), modelData.end(),
                  [column, is_asc, this](const QList &left,const QList &right){
            const QVariant &left_val = left.at(column);
            const QVariant &right_val = right.at(column);
            return is_asc?(left_valright_val);
        });
        //更新view
        dataChanged(index(0,0),index(modelData.count()-1,modelHeader.count()-1));
    }
    // ... ...
private:
    QList> modelData;
};

使用  QAbstractItemModel 的 sort 有个问题,model 在 ResetModel() 重置数据之后不会自动重新排序。这时候我们可以借助 QSortFilterProxyModel 类,不再去重写 sort 函数,就能在重置数据时也自动排序了:

    // 给普通的 ItemModel 加一层 SortProxy
    MyModel *table_model = new MyModel(ui->tableView);
    QSortFilterProxyModel *proxy_model = new QSortFilterProxyModel(this);
    //默认排序role应该是dispalyRole,我们可以修改
    proxy_model->setSortRole(Qt::InitialSortOrderRole);
    proxy_model->setSourceModel(table_model);
    ui->tableView->setModel(proxy_model);

也可以重写 QSortFilterProxyModel 类的 lessThan 接口来实现自己的排序:

#include 
//自定义SortProxy
class MySortProxy: public QSortFilterProxyModel
{
    Q_OBJECT
public:
    BaseTableProxy(QObject *parent = nullptr)
        : QSortFilterProxyModel(parent)
    {
        //排序role,也就是获取model-data时用的role
        setSortRole(Qt::InitialSortOrderRole);
        //本地化,对中文排序有影响
        //在对字符串排序时,是否考虑本地因素,默认false
        setSortLocaleAware(true);
    }
    //... ...
    //重写虚函数接口
    bool lessThan(const QModelIndex &source_left,
                  const QModelIndex &source_right) const override
    {
        //参照源码
        QVariant l = (source_left.model() ? source_left.model()->data(source_left, sortRole()) : QVariant());
        QVariant r = (source_right.model() ? source_right.model()->data(source_right, sortRole()) : QVariant());
        return isVariantLessThan(l, r, sortCaseSensitivity(), isSortLocaleAware());
    }

    //修改自源码QAbstractItemModelPrivate
    bool isVariantLessThan(const QVariant &left,
                           const QVariant &right,
                           Qt::CaseSensitivity cs = Qt::CaseSensitive,
                           bool isLocaleAware = false) const
    {
        //修改源码对无效值得判断
        //if (left.userType() == QVariant::Invalid)
        //    left = false;
        //if (right.userType() == QVariant::Invalid)
        //    right = true;
        //无效值作为0来判断,这样就在正负数之间展示
        if((left.userType() == QVariant::Invalid)||
                (right.userType() == QVariant::Invalid))
            return left.toDouble() < right.toDouble();
        //下面未改动
        switch (left.userType()) {
        case QVariant::Int:
            return left.toInt() < right.toInt();
        case QVariant::UInt:
            return left.toUInt() < right.toUInt();
        case QVariant::LongLong:
            return left.toLongLong() < right.toLongLong();
        case QVariant::ULongLong:
            return left.toULongLong() < right.toULongLong();
        case QMetaType::Float:
            return left.toFloat() < right.toFloat();
        case QVariant::Double:
            return left.toDouble() < right.toDouble();
        case QVariant::Char:
            return left.toChar() < right.toChar();
        case QVariant::Date:
            return left.toDate() < right.toDate();
        case QVariant::Time:
            return left.toTime() < right.toTime();
        case QVariant::DateTime:
            return left.toDateTime() < right.toDateTime();
        case QVariant::String:
        default:
            if (isLocaleAware)
                return left.toString().localeAwareCompare(right.toString()) < 0;
            else
                return left.toString().compare(right.toString(), cs) < 0;
        }
    }
    //... ...
};

QSortFilterProxyModel 还有个问题是:排序之后,表头也会跟着排序,如果表头本身想展示固定行列号就尴尬了。一个临时的解决方法是在 model 的 data 接口返回 visualIndex 的 row(即直接返回当前显示的行数)。

QVariant MySortProxy::data(const QModelIndex & index, int role) const
{
    //index有效值判断可以自己修改
	QModelIndex source_index = mapToSource(index);
	if (index.isValid() && !source_index.isValid())
		return QVariant();
	//orderColumn 就是我们指定的要显示行数的列
	if (source_index.column() == orderColumn && role == Qt::DisplayRole) {
		return QString::number(index.row() + 1);
	}
	return sourceModel()->data(source_index, role);
}

 

你可能感兴趣的:(Qt,略知一二)