Qt之QHeaderView自定义排序(终极版)
简述
本节主要解决自定义排序衍生的第二个问题-将整形显示为字符串,而排序依然正常。
下面我们介绍三种方案:
- 委托绘制
- 用户数据
- 辅助列
很多人也许会有疑虑,平时都用delegate来绘制各种按钮、图标、图形等操作,它还能排序?当然,它本身是不会排序的,但他的高级用法之一就是-辅助排序。
委托绘制
效果
QStyledItemDelegate
我们可以通过设置显示的文本,然后调用QStyle的drawControl来进行ViewItem的绘制。绘制之后,数据源中的数据依然是qint64的,而我们看到的是绘制之后的文本-QString类型,这样QSortFilterProxyModel默认排序(根据源数据排序)就可以满足我们的要求了。
void SortDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem viewOption(option);
initStyleOption(&viewOption, index);
if (option.state.testFlag(QStyle::State_HasFocus))
viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
// 进行大小转换
if (index.column() == FILE_SIZE_COLUMN)
{
qint64 bytes = index.data().toLongLong();
viewOption.text = bytesToGBMBKB(bytes) ;
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &viewOption, painter, viewOption.widget);
}
else
{
QStyledItemDelegate::paint(painter, viewOption, index);
}
}
眼见不一定为实
通过效果图我们也可以很明显的看出来,其实内部的数据并不是界面显示的字符串,而是原始的qint64类型的数据。
pTableView->setMouseTracking(true);
connect(pTableView, SIGNAL(entered(QModelIndex)), this, SLOT(showToolTip(QModelIndex)));
void MainWindow::showToolTip(const QModelIndex &index)
{
if (!index.isValid())
return;
int nColumn = index.column();
if ((nColumn == FILE_SIZE_COLUMN))
QToolTip::showText(QCursor::pos(), index.data().toString());
}
用户数据
QAbstractTableModel
显示在界面的数据为DisplayRole中的数据,我们可以看到已经通过bytesToGBMBKB转化为字符串,这时我们可以通过设置UserRole添加用户数据将源数据存储起来。
// 表格项数据
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
int nRow = index.row();
int nColumn = index.column();
FileRecord record = m_recordList.at(nRow);
switch (role)
{
case Qt::TextColorRole:
return QColor(Qt::white);
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case Qt::DisplayRole:
{
if (nColumn == FILE_NAME_COLUMN)
{
return record.strFileName;
}
else if (nColumn == DATE_TIME_COLUMN)
{
return record.dateTime;
}
else if (nColumn == FILE_SIZE_COLUMN)
{
return bytesToGBMBKB(record.nSize);
}
return "";
}
case Qt::UserRole:
{
// 新增代码
if (nColumn == FILE_SIZE_COLUMN)
return record.nSize;
}
default:
return QVariant();
}
return QVariant();
}
QSortFilterProxyModel
根据用户源数据进行排序。
bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!source_left.isValid() || !source_right.isValid())
return false;
if ((source_left.column() == FILE_SIZE_COLUMN) && (source_right.column() == FILE_SIZE_COLUMN))
{
// 这里我们所取得数据是用户源数据
QVariant leftData = sourceModel()->data(source_left, Qt::UserRole);
QVariant rightData = sourceModel()->data(source_right, Qt::UserRole);
if (leftData.canConvert<qint64>() && rightData.canConvert<qint64>())
{
return leftData.toLongLong() < rightData.toLongLong();
}
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
辅助列
效果
QAbstractTableModel
设置辅助数据
#define FILE_NAME_COLUMN 0 // 文件名
#define DATE_TIME_COLUMN 1 // 修改日期
#define FILE_SIZE_COLUMN 2 // 文件大小
#define FILE_SIZE_HIDDEN_COLUMN 3 // 文件大小隐藏列,显示为字节
// 列数
int TableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 4;
}
// 设置表格项数据
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid())
return false;
int nColumn = index.column();
FileRecord record = m_recordList.at(index.row());
switch (role)
{
case Qt::DisplayRole:
{
if (nColumn == FILE_NAME_COLUMN)
{
record.strFileName = value.toString();
}
else if (nColumn == DATE_TIME_COLUMN)
{
record.dateTime = value.toDateTime();
}
// 新增代码
else if ((nColumn == FILE_SIZE_COLUMN) || (nColumn == FILE_SIZE_HIDDEN_COLUMN))
{
record.nSize = value.toLongLong();
}
m_recordList.replace(index.row(), record);
emit dataChanged(index, index);
// 新增代码
if ((nColumn == FILE_SIZE_COLUMN) || (nColumn == FILE_SIZE_HIDDEN_COLUMN))
{
int nSizeColumn = (nColumn == FILE_SIZE_COLUMN) ? FILE_SIZE_HIDDEN_COLUMN : FILE_SIZE_COLUMN;
QModelIndex sizeIndex = this->index(index.row(), nSizeColumn);
emit dataChanged(sizeIndex, sizeIndex);
}
return true;
}
default:
return false;
}
return false;
}
// 表格项数据
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
int nRow = index.row();
int nColumn = index.column();
FileRecord record = m_recordList.at(nRow);
switch (role)
{
case Qt::TextColorRole:
return QColor(Qt::white);
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case Qt::DisplayRole:
{
if (nColumn == FILE_NAME_COLUMN)
{
return record.strFileName;
}
else if (nColumn == DATE_TIME_COLUMN)
{
return record.dateTime;
}
else if (nColumn == FILE_SIZE_COLUMN)
{
return bytesToGBMBKB(record.nSize);
}
// 新增代码
else if (nColumn == FILE_SIZE_HIDDEN_COLUMN)
{
return record.nSize;
}
return "";
}
default:
return QVariant();
}
return QVariant();
}
// 表头数据
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (role)
{
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case Qt::DisplayRole:
{
if (orientation == Qt::Horizontal)
{
if (section == FILE_NAME_COLUMN)
return QStringLiteral("名称");
if (section == DATE_TIME_COLUMN)
return QStringLiteral("修改日期");
if (section == FILE_SIZE_COLUMN)
return QStringLiteral("大小");
// 新增代码
if (section == FILE_SIZE_HIDDEN_COLUMN)
return QStringLiteral("大小(字节)");
}
}
default:
return QVariant();
}
return QVariant();
}
QSortFilterProxyModel
这里对第三列进行排序,因为第三列的数据是字符串(当然,也可以反转换),所以使用的辅助列数据,获取字节大小后进行对比。
bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!source_left.isValid() || !source_right.isValid())
return false;
if ((source_left.column() == FILE_SIZE_COLUMN) && (source_right.column() == FILE_SIZE_COLUMN))
{
// 获取辅助列索引
QModelIndex sizeLeftIndex = sourceModel()->index(source_left.row(), FILE_SIZE_HIDDEN_COLUMN);
QModelIndex sizeRightIndex = sourceModel()->index(source_right.row(), FILE_SIZE_HIDDEN_COLUMN);
QVariant leftData = sourceModel()->data(sizeLeftIndex);
QVariant rightData = sourceModel()->data(sizeRightIndex);
if (leftData.canConvert<qint64>() && rightData.canConvert<qint64>())
{
return leftData.toLongLong() < rightData.toLongLong();
}
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
隐藏辅助列
一般来说,辅助列(数据)只对我们处理数据有帮助,而不直接显示在界面上,所以我们可以将其隐藏pTableView->setColumnHidden(FILE_SIZE_HIDDEN_COLUMN, true);
总结
小小一个排序居然也有这么多门道,真是条条大路通罗马,通过这几节的分享,想必大家对排序有了更深入的了解,更多的知识请参考官方文档。