• Qt之QHeaderView排序


    简述

    在Windows中我们经常会遇到表头排序,比如可以对文件按照名称、修改日期、类型、大小进行排序,方便我们统一的归类查找。

    Qt中,我们可以通过点击表头来对QTableView或QTreeView等一系列高级视图进行排序,对于一般的数据来说-比如:int、QString等,简单的几句代码就可以搞定,因为Qt内部做了很好的排序处理,但是一般情况下,我们需要处理一些特殊格式的数据,这时,我们就不得不自己处理,以达到理想的效果。

    效果

    这里写图片描述

    自定义数据

    定义各列数据及结构体

    #define FILE_NAME_COLUMN 0   // 文件名
    #define DATE_TIME_COLUMN 1   // 修改日期
    #define FILE_SIZE_COLUMN 2   // 文件大小
    
    typedef struct FileRecord
    {
        QString strFileName; // 文件名
        QDateTime dateTime;  // 修改日期
        qint64 nSize;        // 文件大小
    } fileRecord;

    QAbstractTableModel

    源码

    自定义模型

    TableModel::TableModel(QObject *parent)
        : QAbstractTableModel(parent)
    {
    
    }
    
    TableModel::~TableModel()
    {
    
    }
    
    // 更新表格数据
    void TableModel::updateData(QList<FileRecord> recordList)
    {
        m_recordList = recordList;
        beginResetModel();
        endResetModel();
    }
    
    // 行数
    int TableModel::rowCount(const QModelIndex &parent) const
    {
        Q_UNUSED(parent);
    
        return m_recordList.count();
    }
    
    // 列数
    int TableModel::columnCount(const QModelIndex &parent) const
    {
        Q_UNUSED(parent);
    
        return 3;
    }
    
    // 设置表格项数据
    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)
            {
                record.nSize = value.toLongLong();
            }
    
            m_recordList.replace(index.row(), record);
            emit dataChanged(index, index);
            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 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("大小");
            }
        }
        default:
            return QVariant();
        }
    
        return QVariant();
    }
    
    // 表格可选中
    Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return QAbstractItemModel::flags(index);
    
        Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    
        return flags;
    }

    接口说明

    • updateData
      主要用于更新数据,刷新界面。

    • data
      用来显示数据,根据角色(颜色、文本、对齐方式、选中状态等)判断需要显示的内容。

    • setData
      用来设置数据,根据角色(颜色、文本、对齐方式、选中状态等)判断需要设置的内容。

    • headerData
      用来显示水平/垂直表头的数据。

    • flags
      用来设置单元格的标志(可用、可选中、可复选等)。

    使用

    QTableView *pTableView = new QTableView(this);
    TableModel *pModel = new TableModel(this);
    QSortFilterProxyModel *pProxyModel = new QSortFilterProxyModel(this);
    // 设置数据源模型
    pProxyModel->setSourceModel(pModel);
    pTableView->setModel(pProxyModel);
    // 设置可排序
    pTableView->setSortingEnabled(true);
    // 设置按照文件名升序排列
    pTableView->sortByColumn(FILE_NAME_COLUMN, Qt::AscendingOrder);
    
    // 构造数据,更新界面
    QList<FileRecord> recordList;
    
    // 获取随机值
    QTime time = QTime::currentTime();
    qsrand(time.msec() + time.second()*1000);
    
    for (int i = 0; i < 5; ++i)
    {
        int nIndex = qrand()%20 + 1;
        int nHour = qrand()%24;
        int nMinute = qrand()%60;
        int nSecond = qrand()%60;
        int nBytes = qrand()%100000;
    
        QDateTime dateTime(QDate(2016, 5, 1), QTime(nHour, nMinute, nSecond));
    
        FileRecord record;
        record.strFileName = QString("Name %1.cpp").arg(nIndex);
        record.dateTime = dateTime;
        record.nSize = nBytes;
    
        recordList.append(record);
    }
    pModel->updateData(recordList);

    思考

    1. 细心地童鞋可能会发现,当点击表头(文件名)的时候,如果按照升序排列时,顺序依次是:Name 14、Name 19、Name 4、Name 8、Name 9,降序则相反。为什么呢?

      其实这个很好理解,因为文件名所在的列显示的数据类型为QString,而QString排序是按照第一个字母开始比较,直至最后一个字母,例如:Name 19和Name 4,首先比较Name是相同的,当比较1和4(注意这里不是按照整形比较19和4)的时候,发现1比4小,所以Name 19排在Name 4之前。

    2. 对于文件大小的显示,一般情况下,我们显示的是KB、MB、GB等单位,而不会显示字节,那么按照1的说法,在这种情况下,升序排列时,10 K 就会排在8 K之前了,所以我们应该避免这种问题。

    上面所述的简单排序谁都会,如何把前面的数据按照字符串比较,而后面的数据按照整形比较呢?如何将整形显示为字符串,而排序依然正常呢?这都是我们下节要分享的精彩内容,请持续关注!

  • 相关阅读:
    【搜索】棋盘 luogu-3956
    【动态规划】石子合并 luogu-1880
    【动态规划】合唱队形 luogu-
    【模拟】报名签到 luogu-4445
    【排序+贪心】导弹拦截 luogu-1158
    【模拟】不高兴的津津
    【模拟】选数 luogu-1037
    「JOISC2020」建筑装饰 4
    「清华集训」小 Y 和恐怖的奴隶主
    「CF708E」Student's Camp
  • 原文地址:https://www.cnblogs.com/itrena/p/5938375.html
Copyright © 2020-2023  润新知