最近周末一有空就去图书馆,然后跟着网上qml课程学习一下qml怎么使用,感觉qml在工作中可能还是会用到的。
现在学习到了ListModel篇,跟着视频学一下,敲一下代码,现在分享下学习心得。
总所周知,ListView的model是一个数据模型,需要ListView显示什么样的数据,就得去设置model;
其中,model可以是指定的数字,也可以是ListModel等,但如果遇到复杂情况,使用默认的无法显示功能是,就得自己去自定义了;
本篇博客就是介绍如何在C++端简单的定义model给到qml端使用。
1 在C++端自定义model
首先在QT中新建Model类 MyListModel
新建之后,默认会生成三个函数:
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// 元素的个数 或者 model的大小/长度
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 处理数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
其中,headerData不用管;rowCount主要用于返回元素个数;data用于处理数据返回的给qml的。
然后还需要再手动新建一个函数:
QHash<int, QByteArray> roleNames() const override;
该函数主要用于枚举与qml的映射关系。
既然是有映射关系,那么C++端可以使用枚举作为独立的键值,新建一个枚举:
enum MyRoleName {
Name = Qt::DisplayRole + 1,
Value
};
这里取名为Name和Value,待会在roleNames函数中做映射关系时,映射给qml端使用的变量名也是name和value;
然后在roleNames函数处理映射关系:
QHash<int, QByteArray> MyListModel::roleNames() const
{
// 定义映射关系,C++端的枚举映射QML端使用的变量
QHash<int, QByteArray> mHash;
mHash.insert(MyRoleName::Name, "name"); // 字符串是QML端使用
mHash.insert(MyRoleName::Value, "value"); // 枚举是CPP端使用(用来判断)
return mHash;
}
通过定义哈希容器,将C++端的枚举与字符串组成映射,那么在qml的model中就可以使用name和value;
那么name和value的值是什么呢?如何定义呢?
因为我们给qml端定义了两个映射值,就不能使用普通的QString去存储了,需要自定义一个容器或者类去存储:
class MyData {
public:
MyData(QString s, int v) : m_string(s), m_value(v) {}
QString m_string;
int m_value;
};
在上述代码中,自定义类MyData,里面有QString和int两个元素,用于存储name和value的值;
有了数据结构,就得需要容器去存储,新建QList用于存储MyData:
private:
QList<MyData> m_data; // 存储元素
有了这些数据结构做支撑,就可以在data函数中,通过映射关系给qml端返回相应的值了:
QVariant MyListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
// 通过映射关系,将值返回给qml端使用
if (MyRoleName::Name == role) {
return m_data[index.row()].m_string;
} else if (MyRoleName::Value == role) {
return m_data[index.row()].m_value;
}
// FIXME: Implement me!
return QVariant();
}
data的role参数,用于与枚举做判断,即可得出是哪一个的映射关系;
然后再通过index.row()索引,在m_data容器中获得数据返回回去即可;
注意,这里为什么是使用row()而不是使用column()呢,因为在ListView控件中,是没有用到column的,只用到了row用于标识索引;
m_data的数据是什么?
数据就需要自己插入赋值了,既然是在C++端处理的数据,这不就很简单了嘛;
为了方便,这边只在构造函数中给m_data赋值:
MyListModel::MyListModel(QObject *parent)
: QAbstractListModel(parent)
{
m_data.append(MyData("张三", 111));
m_data.append(MyData("李四", 222));
m_data.append(MyData("王五", 333));
m_data.append(MyData("赵六", 444));
m_data.append(MyData("韩七", 555));
}
最后,还剩一个rowCount函数没有处理,前面也说过了,该函数主要用于返回元素个数,所以:
int MyListModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
return m_data.count();
}
最后,为了方便注册时使用,再加上一个单例函数即可:
static MyListModel *getInstance();
MyListModel *MyListModel::getInstance()
{
static MyListModel *obj = new MyListModel;
return obj;
}
至此,在C++端自定义model已经实现完毕。
2 将自定义model注册到qml端使用
在main函数中,engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 加载前,进行注册:
qmlRegisterSingletonInstance("MyModel", 1, 0, "MyListModel", MyListModel::getInstance());
3 在qml端使用自定义model
首先导入自定义模块:import MyModel 1.0
ListView {
width: 300
height: 500
spacing: 10
model: MyListModel
delegate: Text {
id: txt
text: name + " " + value
}
}
在ListView的model中,直接使用自定义的MyListModel;
然后在delegate中设置文本,就可以直接使用在自定义model中映射的name和value了。
至此,简单的自定义model已经介绍完毕!
4 代码整合
4.1 MyListModel
mylistmodel.h
#ifndef MYLISTMODEL_H
#define MYLISTMODEL_H
#include <QAbstractListModel>
class MyData {
public:
MyData(QString s, int v) : m_string(s), m_value(v) {}
QString m_string;
int m_value;
};
class MyListModel : public QAbstractListModel
{
Q_OBJECT
public:
enum MyRoleName {
Name = Qt::DisplayRole + 1,
Value
};
explicit MyListModel(QObject *parent = nullptr);
static MyListModel *getInstance();
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// Basic functionality: 元素的个数 或者 model的大小/长度
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 处理数据返回给qml使用
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// 生成映射关系
QHash<int, QByteArray> roleNames() const override;
private:
QList<MyData> m_data; // 存储元素
};
#endif // MYLISTMODEL_H
mylistmodel.cpp
#include "mylistmodel.h"
MyListModel::MyListModel(QObject *parent)
: QAbstractListModel(parent)
{
m_data.append(MyData("张三", 111));
m_data.append(MyData("李四", 222));
m_data.append(MyData("王五", 333));
m_data.append(MyData("赵六", 444));
m_data.append(MyData("韩七", 555));
}
MyListModel *MyListModel::getInstance()
{
static MyListModel *obj = new MyListModel;
return obj;
}
QVariant MyListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
// FIXME: Implement me!
}
// 一般用于返回元素个数
int MyListModel::rowCount(const QModelIndex &parent) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
// other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
if (parent.isValid())
return 0;
return m_data.count();
}
QVariant MyListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
// 通过映射关系,将值返回给qml端使用
if (MyRoleName::Name == role) {
return m_data[index.row()].m_string;
} else if (MyRoleName::Value == role) {
return m_data[index.row()].m_value;
}
// FIXME: Implement me!
return QVariant();
}
QHash<int, QByteArray> MyListModel::roleNames() const
{
// 定义映射关系,C++端的枚举映射QML端使用的变量
QHash<int, QByteArray> mHash;
mHash.insert(MyRoleName::Name, "name"); // 字符串是QML端使用
mHash.insert(MyRoleName::Value, "value"); // 枚举是CPP端使用(用来判断)
return mHash;
}
4.2 注册
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "mylistmodel.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// 注册
qmlRegisterSingletonInstance("MyModel", 1, 0, "MyListModel", MyListModel::getInstance());
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
4.3 使用
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.14
import MyModel 1.0
Window {
id: root
visible: true
width: 800
height: 500
title: qsTr("Hello World")
color: "white"
objectName: "window"
ListView {
width: 300
height: 500
spacing: 10
model: MyListModel
delegate: Text {
id: txt
text: name + " " + value
}
}
}