QTableWidget 排序功能跟踪

最近为了实现QTableView的排序功能,通过网络搜索,未能找到解决办法,一时之下就跟踪了QTableWidget的排序功能,现在记录如下。
新建一个Qt工程,拖入QTableWidget控件,并随便输入几个初始值,添加一个按钮,然后添加一句排序代码:

this->ui->tableWidget->sortItems(0);

上边这一行就是跟踪入口。

具体实现

1.sortItems的实现

/*!
  Sorts all the rows in the table widget based on \a column and \a order.
*/
void QTableWidget::sortItems(int column, Qt::SortOrder order)
{
    Q_D(QTableWidget);
    d->model->sort(column, order);
    horizontalHeader()->setSortIndicator(column, order);
}

上述代码中Q_D宏,与本文无关,如有兴趣,请自行搜索。
排序功能的主要实现是靠model(QTableModule)中的sort函数实现的。
horizontalHeader()->setSortIndicator(column, order); 查看自带的文档介绍,主要是操作视图中的排序指示器的,所有也不用关心。
跟进sort函数:

void QTableModel::sort(int column, Qt::SortOrder order)
{   //1.
    QVector<QPair<QTableWidgetItem*, int> > sortable;
    QVector<int> unsortable;

    sortable.reserve(rowCount());
    unsortable.reserve(rowCount());

    for (int row = 0; row < rowCount(); ++row) {
        if (QTableWidgetItem *itm = item(row, column))
            sortable.append(QPair<QTableWidgetItem*,int>(itm, row));
        else
            unsortable.append(row);
    }
    //2.
     LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
    std::stable_sort(sortable.begin(), sortable.end(), compare);
    //3.
    QVector<QTableWidgetItem*> sorted_table(tableItems.count());
    QModelIndexList from;
    QModelIndexList to;
    for (int i = 0; i < rowCount(); ++i) {
        int r = (i < sortable.count()
                 ? sortable.at(i).second
                 : unsortable.at(i - sortable.count()));
        for (int c = 0; c < columnCount(); ++c) {
            sorted_table[tableIndex(i, c)] = item(r, c);
            from.append(createIndex(r, c));
            to.append(createIndex(i, c));
        }
    }

    emit layoutAboutToBeChanged();

    tableItems = sorted_table;
    changePersistentIndexList(from, to); // ### slow

    emit layoutChanged();
}

排序的核心代码主要就是上边的函数。而这个函数又大致分了三个部分:

1.将要排序列的数据提取出来的放在vector中。

这里使用了两个vector:sortable用来保存有内容的部分。unsortable用来保存当前单元格为空的部分。由于QTableWiget中每一个单元格对应一个QTableWidgetItem,因此这里QPair中保存了该结构的指针以及当前单元格对应的行号。

2.排序部分。

排序函数:

bool QTableModel::itemLessThan(const QPair<QTableWidgetItem*,int> &left,
                               const QPair<QTableWidgetItem*,int> &right)
{
    return *(left.first) < *(right.first);
}

bool QTableModel::itemGreaterThan(const QPair<QTableWidgetItem*,int> &left,
                                  const QPair<QTableWidgetItem*,int> &right)
{
    return (*(right.first) < *(left .first));
}

从这里看使用QPair主要是为了通用吧,可以直接用first成员指代QPair中的第一个成员而不用关心其具体类型。
std::stable_sort 调用的是STL算法部分的排序函数,与sort函数相比,该函数在遇到相同值时将不改变原来的顺序,而sort可能会改变顺序。
LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan);
这一句主要是根据参数order来选择是从大到小,还是从小到大。
而最终比较的实现是依靠QTableWidgetItem中的重载函数实现的。

/*
    Returns \c true if the item is less than the \a other item; otherwise returns
    false.
*/
bool QTableWidgetItem::operator<(const QTableWidgetItem &other) const
{
    const QVariant v1 = data(Qt::DisplayRole), v2 = other.data(Qt::DisplayRole);
    return QAbstractItemModelPrivate::variantLessThan(v1, v2);
}

该函数中首先从QTableWidgetItem对象中取出具体的数据,然后调用variantLessThan比较最终的数据:

/*!
    \internal
    Return \c{true} if \a value contains a numerical type.

    This function is used by our Q{Tree,Widget,Table}WidgetModel classes to sort.
*/
bool QAbstractItemModelPrivate::variantLessThan(const QVariant &v1, const QVariant &v2)
{
    switch(qMax(typeOfVariant(v1), typeOfVariant(v2)))
    {
    case 0: //integer type
        return v1.toLongLong() < v2.toLongLong();
    case 1: //floating point
        return v1.toReal() < v2.toReal();
    default:
        return v1.toString().localeAwareCompare(v2.toString()) < 0;
    }
}

该函数主要是应对同一列的数据,数据类型不同而设置的。当数据类型不同时,将按照类型值较大的来实现排序。

static uint typeOfVariant(const QVariant &value)
{
    //return 0 for integer, 1 for floating point and 2 for other
    switch (value.userType()) {
        case QVariant::Bool:
        case QVariant::Int:
        case QVariant::UInt:
        case QVariant::LongLong:
        case QVariant::ULongLong:
        case QVariant::Char:
        case QMetaType::Short:
        case QMetaType::UShort:
        case QMetaType::UChar:
        case QMetaType::ULong:
        case QMetaType::Long:
            return 0;
        case QVariant::Double:
        case QMetaType::Float:
            return 1;
        default:
            return 2;
    }
}

根据上述描述,最终的数据排序只有三种类型:整形,浮点,字符串。

3.最终的实现。

这一部分看起来比较费力,目前暂时还未全部搞明白。
但是大致是分为两部分。

改变模型中的数据

sorted_table[tableIndex(i, c)] = item(r, c);
tableItems = sorted_table;//tableItems 是module中的内部成员。

以上是把数据部分排序,将单元格内容互换。

QTableWidgetItem *QTableModel::item(int row, int column) const
{
    return item(index(row, column));
}

QTableWidgetItem *QTableModel::item(const QModelIndex &index) const
{
    if (!isValid(index))
        return 0;
    return tableItems.at(tableIndex(index.row(), index.column()));
}

改变模型中的索引

QModelIndexList from;
QModelIndexList to;
changePersistentIndexList(from, to); // ### slow

4.最后部分:

emit layoutAboutToBeChanged();  通知试图,模型将要改变。
tableItems = sorted_table;   //改变模型中的数据
changePersistentIndexList(from, to); // ### slow 改变模型中的索引
emit layoutChanged();//发送信号,模型已经改变,请更新数据。

以上是跟踪的总体过程,希望能对大家有所帮助。

作者:lacoucou
来源:CSDN
原文:https://blog.csdn.net/lacoucou/article/details/61415380
版权声明:本文为博主原创文章,转载请附上博文链接!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值