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


    简述

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

    下面我们介绍三种方案:

    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);

    总结

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

  • 相关阅读:
    【翻译自mos文章】 11gR1版本号 asmcmd的新命令--cp、md_backup、md_restore
    Android实现ListView或GridView首行/尾行距离屏幕边缘距离
    iOS-为方便项目开发在pch加入一些经常使用宏定义
    [ACM] FZU 1686 神龙的难题 (DLX 反复覆盖)
    Cocos2d-x Touch事件处理机制
    在linux環境下安裝jprofiler_linux_8_0_2.sh
    QT5 Failed to load platform plugin &quot;windows&quot; 终极解决方式 命令行问题
    我们想要如何子的生活?
    javaEE mvc样例具体解释
    安装Kali Linux操作系统Kali Linux无线网络渗透
  • 原文地址:https://www.cnblogs.com/itrena/p/5938373.html
Copyright © 2020-2023  润新知