QTableWidget 排序功能跟踪

来源:互联网 发布:结婚日子测算软件 编辑:程序博客网 时间:2024/05/29 15:11

最近为了实现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();//发送信号,模型已经改变,请更新数据。

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

0 0