60.QT-QabstractTableModel模型、重写sort方法排序

在之前25.QT-模型视图章节中,没有具体描述如何重写model模型,所以本章以QabstractTableModel为例,来谈谈model如何实现.

1.QabstractTableModel常用功能

QAbstractTableModel子类化时,必须覆写:

Int rowCount();
//返回显示的行数

int columnCount();
//返回显示的列数

Qvariant headerData(int section, Qt::Orientation orientation, int role);
//返回标题role角色对应的值
// section:段号,从0开始,对于Qt::Horizontal水平标题,则是每列的标题名,对于Qt::Vertical垂直标题,则是每行的左侧标题名
//orientation:标题类型
//role:对应值是Qt:: ItemDataRole枚举, 对于role角色,常用的有: //Qt::DisplayRole :以文本方式显示数据(QString) //Qt::DecorationRole :将数据作为图标来装饰(QIcon,QPixmap) //Qt::EditRole :可编辑的数据信息显示(QString) //Qt::ToolTipRole :作为工具提示显示(QString) //Qt::StatusTipRole :作为状态栏中显示的数据(QString) //Qt::WhatsThisRole :作为帮助信息栏中显示的数据(QString) //Qt::FontRole :设置字体(QFont) //Qt::TextAlignmentRole :设置模型数据的文本对齐(Qt::AlignmentFlag) //Qt::BackgroundRole :设置模型数据的背景色(QBrush) //Qt::ForegroundRole : 设置模型数据的前景色,比如字体(QBrush) //Qt::SizeHintRole : 设置模型数据的大小
QVariant data(const QModelIndex &index, int role);
//返回index单元格下的role角色数据。通过index可以获取行号和列号

bool setData(const QModelIndex &index, const QVariant &value, int role);
//将index单元格下的role角色设置为value
//对于可编辑模型,必须重写该函数,然后还需要重写flags()
//返回值为true:表示设置成功,然后还需要显式发射dataChanged信号

2.QabstractTableModel可编辑功能

如果不想实现QabstractTableModel可编辑功能, 则调用QTableView ->setEditTriggers(QAbstractItemView::NoEditTriggers)即可.

如果要实现的话,则需要覆写下面函数:

Qt::ItemFlags  flags(const QModelIndex &index);
//设置每个单元格的flag,对于可编辑模型,必须重写它,添加Qt::ItemIsEditable(可编辑属性)
//然后当我们双击时,会默认创建一个编辑组件(这是由 delegate 完成的)然后delegate会调用QAbstractTableModel ::data(index, Qt::EditRole)读取默认编辑值
//当我们编辑完成后, delegate会调用QAbstractTableModel :: setData (index, value, Qt::EditRole)告诉我们是否保存数据.

如果对于可调整行列的模型,可以重写insertRows()、removeRows()、insertColumns()、removeColumns().在实现这些函数时,还需要调用合适的父类函数,用来通知model调整了哪些内容:

insertRows():
//在向数据结构插入新行之前需要调用父类的beginInsertRows(),并且必须在之后立即调用endInsertRows()。

insertColumns():
//在向数据结构插入新列之前需要调用父类的beginInsertColumns(),并且必须在之后立即调用endInsertColumns()。

RemoveRows():
//在删除行之前需要调用父类的beginRemoveRows(),并且必须在之后立即调用endRemoveRows()。

RemoveColumns():
//在删除列之前需要调用父类的beginRemoveColumns(),并且必须在之后立即调用endRemoveColumns()。

注意:如果要重新刷新model数据,则必须在刷新model之前调用beginResetModel(),然后刷新之后调用endResetModel。

或者在刷新之后,emit dataChanged(index(0,0),index(rowCount,columnCount))来进行刷新视图

 

3.model排序之重写sort方法

首先需要调用QtableView->setSortingEnabled(true)使能排序,sort函数声明如下所示:

void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
//当用户点击标题进行降序/升序排序时,会调用该方法
//或者调用QtableView->sortByColumn()时,也会调用该方法
// column:第几列进行排序
// order:升序(AscendingOrder)、降序(DescendingOrder) 

排序方法则使用std::sort()来实现.然后写个sort类来配合column和order实现排序.

进行排序的时候,必须得调用beginResetModel(),endResetModel()进行界面刷新.

4.代码实现

界面如下所示,支持自定义排序:

下载链接:https://download.csdn.net/download/qq_37997682/13709956

 custommodel.h如下所示:

#ifndef CUSTOMMODEL_H
#define CUSTOMMODEL_H

#include <QObject>
#include <QAbstractTableModel>
#include <QModelIndex>
#include <QFont>
#include <QPixmap>

//排序类
class DataSort
{
public:

    int  mColumn;
    Qt::SortOrder   mSortOrder;
    DataSort(int column, Qt::SortOrder order)
        : mColumn(column)
        , mSortOrder(order)
    {}
    bool operator()(const QVector<QString>* v1, const QVector<QString>*  v2)
    {
        int compare = 0;        //>0:大于 <0:小于
        bool ret=false;
        switch ( mColumn )
        {
            case 0 :     //序号,需要判断数字
            case 3 :     //信号ID,需要判断数字
                compare = v1->at(mColumn).toInt() -  v2->at(mColumn).toInt();
                break;
            default :    //其它,只判断字符串
                compare = v1->at(mColumn).compare(v2->at(mColumn));
                break;
        }

        if(compare==0)      //相等必须返回flase,否则的话,对于一列相同的值进行降序,那么会一直返回true,从而死循环
        {
            return false;
        }
        else
            ret = compare>0?false:true;

        if ( mSortOrder == Qt::DescendingOrder )    //降序
        {
            ret =  !ret;
        }
        return ret;
    }
};

class CustomModel : public QAbstractTableModel
{
    Q_OBJECT


public:
    explicit CustomModel(QAbstractTableModel *parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    Qt::ItemFlags flags(const QModelIndex &index) const;

    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);

public slots:
    void UpdateData(void);

private:
    QList<QVector<QString> * > m_data;
    int m_columnCount;
    int m_rowCount;
    QFont m_Font;
    QPixmap m_icon;

signals:

};

#endif // CUSTOMMODEL_H

 custommodel.cpp如下所示:

#include "custommodel.h"
#include <QDateTime>
#include <QDebug>
CustomModel::CustomModel(QAbstractTableModel *parent) : QAbstractTableModel(parent)
{
    m_columnCount = 5;     //5行
    m_rowCount = 60;

    m_Font.setFamily("Microsoft Yahei");
    m_Font.setPixelSize(17);

    m_icon.load(":alarm");
    m_icon = m_icon.scaled(20,22,Qt::KeepAspectRatio,Qt::SmoothTransformation);

    UpdateData();
}


void CustomModel::UpdateData(void)
{
    beginResetModel();

      int count = m_data.size();
      if(count > 0)
      {
         for(int i = 0; i < count; i++)
        {
            delete m_data.at(i);
        }
        m_data.clear();
      }

for (int i = 0; i < m_rowCount; i++) {

        QVector<QString>* line = new QVector<QString>(m_columnCount);
        line->replace(0,QString("%1").arg(i+1));   
        line->replace(1,"显示器");                   
        line->replace(2,"未显示");                   
        line->replace(3,QString("%1").arg(qrand()%100));
        line->replace(4,QDateTime::currentDateTime().addDays(-10).toString("hh:mm:ss"));
        m_data.append(line);
    }
    endResetModel();
   //emit dataChanged(index(0,0),index(m_data.count()-1,columnCount()-1));   //和beginResetModel()、endResetModel() 本质一样
}


int CustomModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)        //由于parent未使用,所以通过Q_UNUSED去掉编译警告
    return m_data.count();
}

int CustomModel::columnCount(const QModelIndex &parent) const      //
{
    Q_UNUSED(parent)
    return m_columnCount;
}

QVariant CustomModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal) {
        switch (section) {
            case 0 :
                return "序号";
            case 1 :
                return "设备";
            case 2 :
                return "状态";
            case 3 :
                return "信号ID";
            case 4 :
                return "上报时间";
            default :
                return "";
        }
    } else {
        return QString("%1").arg(section + 1);
    }
}

QVariant CustomModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole) {              //显示内容
        return m_data[index.row()]->at(index.column());
    } else if (role == Qt::EditRole) {          //正在启动编辑,返回当前默认编辑值
        return m_data[index.row()]->at(index.column());
    } else if (role == Qt::TextAlignmentRole) {   //内容排版
        return Qt::AlignCenter;
    } else if (role == Qt::FontRole) {           //字体
        return m_Font;
    } else if (role == Qt::DecorationRole) {       //图标
        if (index.column() == 2)
            return m_icon;
    }

    return QVariant();
}

bool CustomModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {   //编辑完成,保存数据到model,并返回true
        m_data[index.row()]->replace(index.column(), value.toString());
        emit dataChanged(index, index);             //重新实现setData()函数时,必须显式发出该信号。
        return true;
    }
    return false;
}

Qt::ItemFlags CustomModel::flags(const QModelIndex &index) const
{
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;       //设置item可编辑
}


void CustomModel::sort(int column, Qt::SortOrder order)
{
    beginResetModel();
    DataSort comp(column,order);
    std::sort(m_data.begin(), m_data.end(),comp);
    endResetModel();
}

第二种排序方法则是通过使用QsortFilterProxyModel代理类实现排序,QsortFilterProxyModel类用来为model和view之间提供强大的排序和过滤支持,并且无需对模型中的数据进行任何转换,也无需对模型在中数据进行修改。

未完待续.下章学习:61.QT-QSortFilterProxyModel代理实现排序、过滤

 

posted @ 2020-12-16 16:49  诺谦  阅读(4820)  评论(8编辑  收藏  举报