标题的「数据」可以是:
类的对象、类的属性、类的方法、信号,或者就是单纯的某个值。
QML 与 C++ 通过 QQmlContext 通讯。
一般传输数据时机是:QQmlEngine 构造好了,但没有加载任何 qml 文档之前。
单纯一个值
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("cppVal", 50);
// QML
title: cppVal
类的对象和类的方法
类的方法必须是以下两种之一:
- Q_INVOKABLE 标记的 public 函数
- public 槽函数
C++ 函数的参数中的 QObject* 类型,相当于 QML 中的对象 id 或引用对象的 var 值。
C++ 函数的返回值也会转换成对应的 js 类型。
public:
Q_INVOKABLE int cppFunc(int a, int b);
CppData cd;
engine.rootContext()->setContextProperty("cppObj", &cd);
//QML
title: cppObj.cppFunc(4, 8)
类的信号
在 QML 中,可以通过 Connections 对象来接收 C++ 的信号
C++ 定义的信号的参数名(val),QML 可以直接用
可以看到,QML 想调用类的方法或者接收类的信号,都是要传一个类的对象给 QML .
// C++
signals:
void cppSignal(int val);
int CppData::cppFunc(int a, int b)
{
emit cppSignal(88);
return a + b;
}
// QML
Connections {
target: cppObj
onCppSignal: {
console.log(val);
}
}
Component.onCompleted: {
cppObj.cppFunc(4, 9)
win.show()
}
还有一种方法在 QML 中接收 C++ 的信号:
把 C++ 的类注册到元对象系统,这时这个类就相当于一个组件,在组件内使用 onSignal 来处理。
类的属性
类的属性可以分三种:基本类型如 int,对象类型如 Persion,列表类型
由于 QList
基本类型
// 法一(较繁琐)
Q_PROPERTY(QString qmlName READ name WRITE setName NOTIFY nameChanged)
signals:
void nameChanged();
public:
QString name() const;
void setName(const QString &name);
private:
QString m_name;
// 法二(简洁)
Q_PROPERTY(QString qmlName MEMBER m_name NOTIFY nameChanged)
signals:
void nameChanged();
private:
QString m_name
对象类型
class Compose : public QObject
{
Q_OBJECT
Q_PROPERTY(int qmlAttr MEMBER attr)
public:
explicit Compose(QObject *parent=nullptr): QObject(parent){}
private:
int attr;
};
class CppData : public QObject
{
Q_OBJECT
Q_PROPERTY(Compose *qmlCompose MEMBER compose)
public:
explicit CppData(QObject *parent = nullptr) : QObject(parent)
{
compose = new Compose(this);
}
private:
Compose *compose;
};
// main.cpp
qmlRegisterType<Compose>("ZZ", 1, 0, "A");
qmlRegisterType<CppData>("ZZ", 1, 0, "B");
// QML
import ZZ 1.0
B {
id: b
qmlCompose: A {
qmlAttr: 56
}
}
B {
id: bb
qmlCompose{ // 分组属性,生命周期由父对象控制
qmlAttr: 57
}
}
Component.onCompleted: {
console.log(b.qmlCompose.qmlAttr)
}
列表类型
一般情况下数据都是从 cpp 中获取,所以这里只讨论使用 cpp 作为数据模型的情况。
可以粗略分为三种:
QStringList
//C++
QStringList aa;
aa << "aaaaa" << "bbbb" << "ccccc";
engine.rootContext()->setContextProperty("aa", QVariant::fromValue(aa));
//QML
delegate: Text{text:modelData} // 或者 model.modelData
QList
// C++
Q_PROPERTY(QString data MEMBER m_data NOTIFY dataChanged)
Q_PROPERTY(int number MEMBER m_number NOTIFY numberChanged)
QList<QObject*> ls;
ls.append(new Name("aaa", 1));
ls.append(new Name("bbb", 2));
ls.append(new Name("ccc", 3));
engine.rootContext()->setContextProperty("ls", QVariant::fromValue(ls));
// QML
delegate: Text {text: model.modelData.data + ": " + number}
继承 QAbstractListModel
还是先来说说使用 QML作为数据模型的情况,关键是了解 role 概念:
ListModel {
id: fruitModel
ListElement {
name: "Apple" // 这是一个 role, 不是属性
cost: 2.45 // 这又是一个 role
}
ListElement {
name: "Orange"
cost: 3.25
}
}
ListView {
anchors.fill: parent
model: fruitModel
delegate: Row {
Text { text: "Fruit: " + name } // 在 delegate 中访问 role 的两种方式
Text { text: "Cost: $" + model.cost }
}
}
在 QAbstractListModel 的基类 QAbstractItemModel 有一个 public 的虚函数:
// 定义一堆 role
enum AbstractListRoles{
NameRole=Qt::UserRole+1,
ColorRole,
NumberRole
};
// 给每个 role 起个名称,方便 qml 中使用
QHash<int, QByteArray> QAbstractItemModel::roleNames() const
{
QHash<int,QByteArray> roles;
roles.insert(AbstractListRoles::NameRole,"myname");
roles.insert(AbstractListRoles::ColorRole,"mycolor");
roles.insert(AbstractListRoles::NumberRole,"mynumber");
return roles;
}
维护 QList 列表,实现 data、rowcount 方法
private:
QList<AbstractList> m_abstractList; //抽象列表类容器
int AbstractListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_abstractList.count();
}
QVariant AbstractListModel::data(const QModelIndex &index, int role) const
{
if(index.row()<0||index.row()>=m_abstractList.count())
return QVariant();
const AbstractList &abstractList=m_abstractList[index.row()];
if(role==AbstractListRoles::NameRole)
return abstractList.name();
else if(role==AbstractListRoles::ColorRole)
return abstractList.color();
else if(role==AbstractListRoles::NumberRole)
return abstractList.number();
return QVariant();
}
参考文档:
https://blog.csdn.net/qq_35173114/article/details/80873842