标题的「数据」可以是:
类的对象、类的属性、类的方法、信号,或者就是单纯的某个值。
QML 与 C++ 通过 QQmlContext 通讯。
一般传输数据时机是:QQmlEngine 构造好了,但没有加载任何 qml 文档之前。
单纯一个值
QQmlApplicationEngine engine;engine.rootContext()->setContextProperty("cppVal", 50);// QMLtitle: 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);//QMLtitle: 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;}// QMLConnections {target: cppObjonCppSignal: {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_OBJECTQ_PROPERTY(int qmlAttr MEMBER attr)public:explicit Compose(QObject *parent=nullptr): QObject(parent){}private:int attr;};class CppData : public QObject{Q_OBJECTQ_PROPERTY(Compose *qmlCompose MEMBER compose)public:explicit CppData(QObject *parent = nullptr) : QObject(parent){compose = new Compose(this);}private:Compose *compose;};// main.cppqmlRegisterType<Compose>("ZZ", 1, 0, "A");qmlRegisterType<CppData>("ZZ", 1, 0, "B");// QMLimport ZZ 1.0B {id: bqmlCompose: A {qmlAttr: 56}}B {id: bbqmlCompose{ // 分组属性,生命周期由父对象控制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));//QMLdelegate: 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));// QMLdelegate: Text {text: model.modelData.data + ": " + number}
继承 QAbstractListModel
还是先来说说使用 QML作为数据模型的情况,关键是了解 role 概念:
ListModel {id: fruitModelListElement {name: "Apple" // 这是一个 role, 不是属性cost: 2.45 // 这又是一个 role}ListElement {name: "Orange"cost: 3.25}}ListView {anchors.fill: parentmodel: fruitModeldelegate: Row {Text { text: "Fruit: " + name } // 在 delegate 中访问 role 的两种方式Text { text: "Cost: $" + model.cost }}}
在 QAbstractListModel 的基类 QAbstractItemModel 有一个 public 的虚函数:
// 定义一堆 roleenum 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
