最近为了实现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
版权声明:本文为博主原创文章,转载请附上博文链接!