QML>6>C++ Pass Complex Data Structure to QML TableView Use C++ Model QAbstractTableModel

QML < 6 > C++ Pass Complex Data Structure to QML TableView uses C++ Model QAbstractTableModel

Preface

TableView tables display commonly used controls and use C++ model s to handle complex data structures and to decouple data and UI.
This article documents Tableview's use of a C++ model example, using Model as the QAbstractTableModel.
Tabular data and styles refer to previous articles: QML>4>Tableview Custom Table Display delegate FontAwesome Canvas

1. QAbstractTableModel class custom CustomDataModel

1 Data format

The data source JSON array format is as follows:

2 Data structure definition

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

3 Data structure field corresponding enumeration declaration

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 Override rowCount

The rowCount function returns the number of rows in the table, coded as follows:

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

5 Override columnCount

The columnCount function returns the number of table columns, which are defined in the QML TableView in this article. The model implements the following functions:

QStringlist head << QStringLiteral("")<< QStringLiteral("Title")<<QStringLiteral("Duration")<< QStringLiteral("singer");
int CustomDataModel::columnCount(const QModelIndex &parent) const
{
     Q_UNUSED(parent);
   return head.size();
}

6 Rewrite roleNames

The roleNames function returns a table column, role QML TableView column, that defines the same role and obtains table cell data based on a Role match with the following code:

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 Rewrite data

The data function is called when the QML TableView updates but with individual data, and the corresponding data is returned from the Role with the following code:

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 Define custom function getDataFromRowByRole

getDataFromRowByRole Gets Role data for specified rows and columns, and custom functions need to add Q_to call function declarations in QML INVOKABLE, code as follows:

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

2. Model is registered with QML

C++ classes are called in QML and need to be registered with QML. There are two ways to register C++ classes, one is to register class type, the other is to register class instance. This article registers C++ classes as follows:

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

QML is used as follows:

import CustomDataModel 1.0
 CustomDataModel
    {
        id:tablemodel;
    }

3. One-to-one correspondence between Model data and TableView columns

TableView does not process data at all, according to Model data. TableView is defined as follows:

    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
    }

The code works as follows:

4. Model column data Role differs from TableView column Role

Call getDataFromRowByRole in QML to get the current table update row data and set the corresponding variable

1 TableView Definition

CustomDataModel
    {
        id:tablemodel;
    }

    TableView
    {
        id:tableview
        anchors.fill: parent
        TableViewColumn{role: "NewFlag"; title: ""; width: 80; elideMode: Text.ElideRight;}
        TableViewColumn{role: "Title"; title: qsTr("Title"); width: 320; elideMode: Text.ElideRight;}
       TableViewColumn{role: "Time"; title: qsTr("Duration"); width: 90;}
        TableViewColumn{role: "Singer"; title: qsTr("singer"); 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 rows delegate data

Variable assignment:

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

3 Title delegate data

4-hour delegate data

5 singer delegate data

6 Running effect

summary

1 json data parsing

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 Custom model Crater

Do not implement a custom model, it will result in a data function
Without calling, the QAbstractItemModel functions selected by the base class at the beginning must be implemented. The code for not calling the data function after the implementation is posted here, and the reason will be analyzed later.

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

Keywords: C++ qml

Added by wlpywd on Wed, 08 Dec 2021 09:39:19 +0200