QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

前言

TableView 表格显示常用控件,使用C++ model 便于处理复杂的数据结构,也更利于数据和UI 解耦 ,
本文记录下 Tableview 使用C++ model示例,使用Model 为QAbstractTableModel。
表格数据和样式参照之前文章:QML <4> Tableview 自定义表格显示 delegate FontAwesome Canvas

一、QAbstractTableModel 类自定义 CustomDataModel

1数据格式

数据源JSON 数组格式,如下:

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

2数据结构定义

struct Music_Data
{
    bool NewFlag;
    bool TrendUp;
    int TrendNum;
    QString ImageSouce;
    QString Title;
    int Time;
    QString Singer;
};
   QVector< Music_Data> vDatas;

3 数据结构字段对应枚举声明

Q_ENUMS(EM_DATA_ROLE)
 enum  EM_DATA_ROLE
  {
      Data_Role_NewFlag = Qt::UserRole+1,
      Data_Role_TrendUp,
      Data_Role_TrendNum,
      Data_Role_ImageSouce,
      Data_Role_Title,
      Data_Role_Time,
      Data_Role_Singer
  };

4 重写rowCount

rowCount函数返回 表格行数,代码如下:

int CustomDataModel::rowCount(const QModelIndex &parent) const
{
     Q_UNUSED(parent);
    return vDatas.size();
}

5 重写columnCount

columnCount 函数返回表格列数,本文中表格列在QML 中TableView 定义,model 实现函数如下,

QStringlist head << QStringLiteral("")<< QStringLiteral("标题")<<QStringLiteral("时长")<< QStringLiteral("歌手");
int CustomDataModel::columnCount(const QModelIndex &parent) const
{
     Q_UNUSED(parent);
   return head.size();
}

6 重写roleNames

roleNames 函数返回表格列role QML TableView 列定义相同Role ,根据Role 匹配获取表格单元格数据,代码如下:

QHash<int, QByteArray> CustomDataModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Data_Role_NewFlag] = "NewFlag";
    roles[Data_Role_Title] = "Title";
    roles[Data_Role_Time] = "Time";
    roles[Data_Role_ImageSouce] = "ImageSouce";
    roles[Data_Role_TrendNum] = "TrendNum";
    roles[Data_Role_TrendUp] = "TrendUp";
    roles[Data_Role_Singer] = "Singer";
    return roles;
}

7 重写data

QML TableView 更新但与个数据时调用data函数,根据Role 返回对应数据,代码如下:

QVariant CustomDataModel::data(const QModelIndex &index, int role) const
{
    if(index.row() >= vDatas.size())
    {
        return QVariant();
    }

    const  Music_Data  rowData  = vDatas.at(index.row());
    switch(role)
    {
    case Data_Role_NewFlag:
    {
        return rowData.NewFlag;
    }break;
    case Data_Role_Time:
    {
        return rowData.Time;
    }break;
    case Data_Role_Title:
    {
        return rowData.Title;
    }break;
    case Data_Role_Singer:
    {
        return rowData.Singer;
    }break;
    case Data_Role_ImageSouce:
    {
        return rowData.ImageSouce;
    }break;
    case Data_Role_TrendNum:
    {
        return rowData.TrendNum;
    }break;
    case Data_Role_TrendUp:
    {
        return rowData.TrendUp;
    }break;
    default: return QVariant();
    }
}

8 定义自定义函数getDataFromRowByRole

getDataFromRowByRole 获取指定行指定列Role 数据,自定义函数要在QML 中调用 函数声明需要添加Q_INVOKABLE,代码如下:

 Q_INVOKABLE QVariant getDataFromRowByRole(int row ,int role);
 QVariant CustomDataModel::getDataFromRowByRole(int row, int role)
{

    QVariant data;
    if(row < vDatas.size())
    {
        Music_Data row_data = vDatas.at(row);

        switch(role)
        {
        case Data_Role_NewFlag:{ data=  QVariant::fromValue(row_data.NewFlag);}break;
        case Data_Role_TrendUp:{ data=  QVariant::fromValue(row_data.TrendUp);}break;
        case Data_Role_TrendNum:{ data=  QVariant::fromValue(row_data.TrendNum);}break;
        case Data_Role_ImageSouce:{ data=  QVariant::fromValue(row_data.ImageSouce);}break;
        case Data_Role_Title:{ data=  QVariant::fromValue(row_data.Title);}break;
        case Data_Role_Time:{ data=  QVariant::fromValue(row_data.Time);}break;
        case Data_Role_Singer:{ data=  QVariant::fromValue(row_data.Singer);}break;
        }
    }
    return data;
}

二、Model 注册到QML

C++ 类在QML 中调用,需要将C++ 类注册到QML,注册方式有两种,一种是注册类类型,一种是注册类实例,本文注册C++ 类如下:

  qmlRegisterType<CustomDataModel>("CustomDataModel",1, 0, "CustomDataModel");

QML 使用如下:

import CustomDataModel 1.0
 CustomDataModel
    {
        id:tablemodel;
    }

三、Model 的数据和TableView 列一一对应

TableView根据Model 数据显示,不对数据做任何处理。TableView 定义如下:

    TableView {

        anchors.fill: parent


        TableViewColumn {title: "NewFlag"; role: "NewFlag"; width: 70 }

        TableViewColumn {title: "Title"; role: "Title"; width: 70   }
        TableViewColumn {title: "Time"; role: "Time"; width: 70 }
        TableViewColumn {title: "ImageSouce"; role: "ImageSouce"; width: 70   }
        TableViewColumn {title: "TrendNum"; role: "TrendNum"; width: 70 }
        TableViewColumn {title: "TrendUp"; role: "TrendUp"; width: 70   }
        TableViewColumn {title: "Singer"; role: "Singer"; width: 70 }
        model: tablemodel
    }

代码运行效果如下:
QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

四、Model 的列数据Role和TableView 列Role有差别

在QML 中调用getDataFromRowByRole 获取当前表格更新行数据,并设置相应变量

1 TableView 定义

CustomDataModel
    {
        id:tablemodel;
    }

    TableView
    {
        id:tableview
        anchors.fill: parent
        TableViewColumn{role: "NewFlag"; title: ""; width: 80; elideMode: Text.ElideRight;}
        TableViewColumn{role: "Title"; title: qsTr("标题"); width: 320; elideMode: Text.ElideRight;}
       TableViewColumn{role: "Time"; title: qsTr("时长"); width: 90;}
        TableViewColumn{role: "Singer"; title: qsTr("歌手"); width: 120;}
        headerDelegate:header_delegate
        itemDelegate:  item_delegate
        rowDelegate: Rectangle
        {
            height: styleData.row < 3 ?image_row_height:row_height;
            //border.color:"gray";
            //color: styleData.alternate?"lightgray":"white"
        }

        model:tablemodel

    }

2 排行delegate 数据

变量赋值方式:

 property var new_flag :
                        {
                          var data = tablemodel.getDataFromRowByRole(styleData.row,CustomDataModel.Data_Role_NewFlag)
                          return data
                         }

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

3 标题delegate数据

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

4 时长delegate数据

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

5 歌手delegate数据

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

6 运行效果

QML < 6 > C++ 向QML 传递复杂数据结构 TableView 使用C++ Model QAbstractTableModel

总结

1 json 数据解析

void CustomDataModel::parseJsonData()
{
   
    const QString  filepath = ":/data/music_data.json";

    QFile file(filepath);
    if(!file.exists())
    {
        return ;
    }
    if(!file.open(QFile::ReadOnly))
    {
        return;
    }
    QByteArray  jsonstr = file.readAll();


    QJsonParseError parseerror;
    QJsonDocument doc = QJsonDocument::fromJson(jsonstr,&parseerror);
    if(parseerror.error !=  QJsonParseError::NoError)
    {
        qDebug() <<"parseerror";
        return;
    }
    QJsonObject obj = doc.object();

    QJsonArray data = obj["Data"].toArray();

    foreach(auto item , data)
    {
        Music_Data  data;
        data.Time =item["Time"].toInt() ;
        data.NewFlag= item["NewFlag"].toBool();
        data.TrendUp=item["TrendUp"].toBool();
        data.TrendNum=item["TrendNum"].toInt();
        data.ImageSouce=item["ImageSouce"].toString();
        data.Singer=item["Singer"].toString();
        data.Title=item["Title"].toString();
        vDatas.push_back(data);
    }
}

2 自定义model 踩坑

实现自定义model的时候不要实现,实现后造成data函数
不调用,最开始基类 选择的QAbstractItemModel 这两个函数是必须要实现,这里先贴出当时实现后不调用data函数时的代码,待后面分析原因,

virtual QModelIndex index(int row, int column,
                             const QModelIndex &parent = QModelIndex()) const ;
QModelIndex CustomDataModel::index(int row, int column, const QModelIndex &parent) const
{
   return  QModelIndex();
}
virtual QModelIndex parent(const QModelIndex &child) const ;
QModelIndex CustomDataModel::parent(const QModelIndex &child) const
{
    Q_UNUSED(child);
    return QModelIndex();
}
上一篇:excel数据导入数据库


下一篇:Python + MySQL 批量查询百度收录