🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873) 接上文[Qt5官方demo解析集25——Extending QML - Methods Example](http://blog.csdn.net/cloud_castle/article/details/37391595) 如果之前看过了我前面介绍粒子系统的朋友,应该对 velocity: AngleDirection {angleVariation: 360; magnitude: 80; magnitudeVariation: 40}   这样的属性设置格式屡见不鲜了,它实际是将一个AngleDirection类型作为velocity的属性值,这样我们就可以通过设置AngleDirection的属性值来达到对velocity更复杂的控制。 在这个例子中,Qt 向我们展示了一种更巧妙的做法 —— 直接使用AngleDirection的属性。 可能你之前经常会使用到font.family: "Ubuntu" 或是 font.pixelSize: 24 ,实际上这里的font 也是一个集合属性。 还是先把我们的项目文件介绍一下: ![](https://box.kancloud.cn/2016-01-18_569cbd08750fc.jpg) 两个自定义的C++类:Person与BirthdayParty,资源文件中是我们的QML文件example.qml。 为了有一个直观的印象,我们先来看看qml的实现: example.qml: ~~~ import People 1.0 import QtQuick 2.0 // For QColor // ![0] BirthdayParty { host: Boy { name: "Bob Jones" shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 } // QML语法基础保证冒号为一个赋值运算 } // 而这个语句保证了属性赋值在大括号内部进行 Boy { name: "Leo Hodges" //![grouped] shoe { size: 10; color: "black"; brand: "Thebok"; price: 59.95 } //![grouped] } // ![1] Boy { name: "Jack Smith" shoe { size: 8 color: "blue" brand: "Luma" price: 19.95 } } // ![1] Girl { name: "Anne Brown" //![ungrouped] shoe.size: 7 // 大括号拆开来实际就变成这几个单独的语句 shoe.color: "red" shoe.brand: "Job Macobs" shoe.price: 699.99 //![ungrouped] } } // ![0] ~~~ 这种赋值方法是如何实现的呢,来看C++的定义: BirthdayParty提供了生日派对的框架,这个类没有变化。birthdayparty.h: ~~~ #ifndef BIRTHDAYPARTY_H #define BIRTHDAYPARTY_H #include <QObject> #include <QQmlListProperty> #include "person.h" class BirthdayParty : public QObject { Q_OBJECT Q_PROPERTY(Person *host READ host WRITE setHost) Q_PROPERTY(QQmlListProperty<Person> guests READ guests) Q_CLASSINFO("DefaultProperty", "guests") public: BirthdayParty(QObject *parent = 0); Person *host() const; void setHost(Person *); QQmlListProperty<Person> guests(); int guestCount() const; Person *guest(int) const; private: Person *m_host; QList<Person *> m_guests; }; ~~~ birthdayparty.cpp: ~~~ #include "birthdayparty.h" BirthdayParty::BirthdayParty(QObject *parent) : QObject(parent), m_host(0) { } Person *BirthdayParty::host() const { return m_host; } void BirthdayParty::setHost(Person *c) { m_host = c; } QQmlListProperty<Person> BirthdayParty::guests() { return QQmlListProperty<Person>(this, m_guests); } int BirthdayParty::guestCount() const { return m_guests.count(); } Person *BirthdayParty::guest(int index) const { return m_guests.at(index); } ~~~ 接下来,Person文件中定义了一个额外的类ShoeDescription用来为shoe属性提供描述。 person.h: ~~~ #ifndef PERSON_H #define PERSON_H #include <QObject> #include <QColor> class ShoeDescription : public QObject // 这个类的作用类似我们前面所说的AngleDirection,用这个自定义类型定义shoe属性 { Q_OBJECT Q_PROPERTY(int size READ size WRITE setSize) // 内部定义了四种属性:尺寸、颜色、品牌、价格 Q_PROPERTY(QColor color READ color WRITE setColor) Q_PROPERTY(QString brand READ brand WRITE setBrand) Q_PROPERTY(qreal price READ price WRITE setPrice) public: ShoeDescription(QObject *parent = 0); int size() const; void setSize(int); QColor color() const; void setColor(const QColor &); QString brand() const; void setBrand(const QString &); qreal price() const; void setPrice(qreal); private: int m_size; QColor m_color; QString m_brand; qreal m_price; }; class Person : public QObject // Person类定义 { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) // ![1] Q_PROPERTY(ShoeDescription *shoe READ shoe) // 将shoe属性设置为只读,否则它需要被冒号赋值 // ![1] public: Person(QObject *parent = 0); QString name() const; void setName(const QString &); ShoeDescription *shoe(); private: QString m_name; ShoeDescription m_shoe; }; class Boy : public Person { Q_OBJECT public: Boy(QObject * parent = 0); }; class Girl : public Person { Q_OBJECT public: Girl(QObject * parent = 0); }; #endif // PERSON_H ~~~ person.cpp: ~~~ #include "person.h" ShoeDescription::ShoeDescription(QObject *parent) : QObject(parent), m_size(0), m_price(0) { } int ShoeDescription::size() const { return m_size; } void ShoeDescription::setSize(int s) { m_size = s; } QColor ShoeDescription::color() const { return m_color; } void ShoeDescription::setColor(const QColor &c) { m_color = c; } QString ShoeDescription::brand() const { return m_brand; } void ShoeDescription::setBrand(const QString &b) { m_brand = b; } qreal ShoeDescription::price() const { return m_price; } void ShoeDescription::setPrice(qreal p) { m_price = p; } Person::Person(QObject *parent) : QObject(parent) { } QString Person::name() const { return m_name; } void Person::setName(const QString &n) { m_name = n; } ShoeDescription *Person::shoe() { return &m_shoe; } Boy::Boy(QObject * parent) : Person(parent) { } Girl::Girl(QObject * parent) : Person(parent) { } ~~~ 最后来看main.cpp: ~~~ #include <QCoreApplication> #include <QQmlEngine> #include <QQmlComponent> #include <QDebug> #include "birthdayparty.h" #include "person.h" int main(int argc, char ** argv) { QCoreApplication app(argc, argv); qmlRegisterType<BirthdayParty>("People", 1,0, "BirthdayParty"); qmlRegisterType<ShoeDescription>(); // 注册该类型,但不进行实例化,这与Person类相同 qmlRegisterType<Person>(); qmlRegisterType<Boy>("People", 1,0, "Boy"); qmlRegisterType<Girl>("People", 1,0, "Girl"); QQmlEngine engine; QQmlComponent component(&engine, QUrl("qrc:example.qml")); BirthdayParty *party = qobject_cast<BirthdayParty *>(component.create()); if (party && party->host()) { qWarning() << party->host()->name() << "is having a birthday!"; if (qobject_cast<Boy *>(party->host())) qWarning() << "He is inviting:"; else qWarning() << "She is inviting:"; Person *bestShoe = 0; for (int ii = 0; ii < party->guestCount(); ++ii) { // 比较每个客人鞋子价格,得出谁穿着最好的鞋 Person *guest = party->guest(ii); qWarning() << " " << guest->name(); if (!bestShoe || bestShoe->shoe()->price() < guest->shoe()->price()) bestShoe = guest; } if (bestShoe) qWarning() << bestShoe->name() << "is wearing the best shoes!"; } else { qWarning() << component.errors(); } return 0; } ~~~ 运行效果: ![](https://box.kancloud.cn/2016-01-18_569cbd088659f.jpg) 可以看到,要想实现类似 ~~~ shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 } ~~~ 这种“群属性”的设置方法,我们只需要设置shoe为只读就可以了,这样QML编译器就不会寻找shoe后面的冒号,进入大括号后的赋值语句,实际上是对ShoeDescription属性的赋值。 那么如果我们想要实现这种写法 ~~~ shoe: ShoeDescription { size: 12; color: "white"; brand: "Bikey"; price: 90.0 } ~~~ 该如何改动呢? 第一点是shoe属性的声明,它必须是可读写的: Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe)  当然还有对应的setShoe()函数的实现。 第二点是在main.cpp中QML类型的注册: qmlRegisterType("People", 1,0, "ShoeDescription"); 这里的ShoeDescription就需要被实例化了。 这样就可以再次运行了。