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 数组格式,如下:
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
}
代码运行效果如下:
四、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
}
3 标题delegate数据
4 时长delegate数据
5 歌手delegate数据
6 运行效果
总结
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();
}