Qt之QHeaderView自定义排序(终极版)

简介: 简述本节主要解决自定义排序衍生的第二个问题-将整形显示为字符串,而排序依然正常。下面我们介绍三种方案:委托绘制用户数据辅助列很多人也许会有疑虑,平时都用delegate来绘制各种按钮、图标、图形等操作,它还能排序?当然,它本身是不会排序的,但他的高级用法之一就是-辅助排序。简述委托绘制效果QStyledItemDelegate眼见不

简述

本节主要解决自定义排序衍生的第二个问题-将整形显示为字符串,而排序依然正常。

下面我们介绍三种方案:

  1. 委托绘制
  2. 用户数据
  3. 辅助列

很多人也许会有疑虑,平时都用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);

总结

小小一个排序居然也有这么多门道,真是条条大路通罗马,通过这几节的分享,想必大家对排序有了更深入的了解,更多的知识请参考官方文档。

目录
相关文章
|
1月前
|
存储 机器学习/深度学习 人工智能
Qt魔法书:打造自定义鼠标键盘脚本(二)
Qt魔法书:打造自定义鼠标键盘脚本
35 0
|
3月前
QT自定义信号,信号emit,信号参数注册
使用signals声明返回值是void在需要发送信号的地方使用emit 信号名字(参数)进行发送在需要链接的地方使用connect进行链接ct进行链接。
19 0
QT自定义信号,信号emit,信号参数注册
|
3月前
Qt提升控件类为自定义类
Qt提升控件类为自定义类
|
4月前
|
搜索推荐 C++ 索引
C++ Qt开发:QItemDelegate自定义代理组件
在Qt中,`QStyledItemDelegate` 类是用于创建自定义表格视图(如`QTableView`和`QTableWidget`)的委托类,允许你自定义表格中每个单元格的外观和交互。`QStyledItemDelegate` 是`QItemDelegate` 的子类,提供了更现代、更易用的接口。此处我们将实现对`QTableView`表格组件的自定义代理功能,例如默认情况下表格中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重写编辑框实现选择的效果,代理组件常用于个性化定制表格中的字段类型。
37 0
C++ Qt开发:QItemDelegate自定义代理组件
|
5月前
10 QT - 自定义信号和槽
10 QT - 自定义信号和槽
34 0
|
API 计算机视觉
Qt实用技巧:自定义窗口标题栏
Qt实用技巧:自定义窗口标题栏
Qt实用技巧:自定义窗口标题栏
|
3月前
Qt6学习笔记五(自定义对话框、QMessageBox、QColorDialog、QFileDialog、QFontDialog)
Qt6学习笔记五(自定义对话框、QMessageBox、QColorDialog、QFileDialog、QFontDialog)
41 0
|
1月前
|
开发框架 Linux API
Qt魔法书:打造自定义鼠标键盘脚本(一)
Qt魔法书:打造自定义鼠标键盘脚本
23 0
|
1月前
使用代码实现QT自定义布局
使用代码实现QT自定义布局
|
3月前
Qt6自定义QML控件的方式
Qt6自定义QML控件的方式
69 1

热门文章

最新文章

推荐镜像

更多