本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873) 接上文[Qt5官方demo解析集29——Extending QML - Property Value Source Example](http://blog.csdn.net/cloud_castle/article/details/37526169) 还记得我们曾经在[Qt5官方demo解析集17——Chapter 3: Adding Property Bindings](http://blog.csdn.net/cloud_castle/article/details/36886779)一文中接触过QML自定义类型的属性绑定吗?如果不记得了,可以移步进行了解。因为项目尺寸的原因,那个例子可能更好理解。 这个例子也是我们Extending QML(扩展QML)系列的最后一个例子了,虽然相较前一个例子也只有小小的改动,不过我们还是把整个工程都完整的看一遍吧~ ![](https://box.kancloud.cn/2016-01-18_569cbd09034fd.jpg) binding.qrc中是我们的qml文件,它实例化了BirthdayParty类以及其所有的子对象。 Person类建立了一个自定义的QML类型,由于它并不是一个可视化组件,且QML任何组件均基于Qt 的元对象系统,因此继承自QObject。 接着定义了ShoeDescription用来对Person类的shoe属性进行描述,使用特定的方法,我们在对shoe赋值时不需要实例化这个ShoeDescription组件。 再定义两个Person的派生类Boy、Girl,可以用来对Person对象分类。 person.h: ~~~ #ifndef PERSON_H #define PERSON_H #include <QObject> #include <QColor> class ShoeDescription : public QObject { Q_OBJECT Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged) // NOTIFY用在属性绑定,当该属性值发生改变时发出信号shoeChanged Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged) // 通过该信号,我们就能使得被绑定的属性值随之发生改变 Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged) Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged) 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); signals: void shoeChanged(); // 定义该shoeChanged()信号 private: int m_size; QColor m_color; QString m_brand; qreal m_price; }; class Person : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) // ![0] Q_PROPERTY(ShoeDescription *shoe READ shoe CONSTANT) // ![0] public: Person(QObject *parent = 0); QString name() const; void setName(const QString &); ShoeDescription *shoe(); signals: void nameChanged(); 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) { if (m_size == s) return; m_size = s; emit shoeChanged(); // 该信号应该在该属性被正确写入后发出 } QColor ShoeDescription::color() const { return m_color; } void ShoeDescription::setColor(const QColor &c) { if (m_color == c) return; m_color = c; emit shoeChanged(); } QString ShoeDescription::brand() const { return m_brand; } void ShoeDescription::setBrand(const QString &b) { if (m_brand == b) return; m_brand = b; emit shoeChanged(); } qreal ShoeDescription::price() const { return m_price; } void ShoeDescription::setPrice(qreal p) { if (m_price == p) return; m_price = p; emit shoeChanged(); } Person::Person(QObject *parent) : QObject(parent) { } QString Person::name() const { return m_name; } void Person::setName(const QString &n) { if (m_name == n) return; m_name = n; emit nameChanged(); } ShoeDescription *Person::shoe() { return &m_shoe; } Boy::Boy(QObject * parent) : Person(parent) { } Girl::Girl(QObject * parent) : Person(parent) { } ~~~ 接下来是我们的主类BirthdayParty,它也是example.qml中的根项目。它有一个以Person指针为参数的host属性,用来指明寿星;有一个以Person列表指针为参数guests属性,用来指明客人,并且该属性被设置为默认属性,这样在QML中没有指明属性的值将被划归它的名下;一个announcement属性,用来被动态改变以播放歌词。另外,该类还定义了一个partyStarted()信号,我们可以在QML中使用onPartyStarted 来响应该信号。 此外,再定义一个BirthdayPartyAttached类,它用来为BirthdayParty提供一个附加属性。 birthdayparty.h: ~~~ #ifndef BIRTHDAYPARTY_H #define BIRTHDAYPARTY_H #include <QObject> #include <QDate> #include <QDebug> #include <qqml.h> #include "person.h" class BirthdayPartyAttached : public QObject { Q_OBJECT Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged) // 该例中大多数属性均定义了属性绑定 public: BirthdayPartyAttached(QObject *object); QDate rsvp() const; void setRsvp(const QDate &); signals: void rsvpChanged(); private: QDate m_rsvp; }; class BirthdayParty : public QObject { Q_OBJECT // ![0] Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged) // ![0] Q_PROPERTY(QQmlListProperty<Person> guests READ guests) Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement) 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; QString announcement() const; void setAnnouncement(const QString &); static BirthdayPartyAttached *qmlAttachedProperties(QObject *); void startParty(); signals: void partyStarted(const QTime &time); void hostChanged(); private: Person *m_host; QList<Person *> m_guests; }; QML_DECLARE_TYPEINFO(BirthdayParty, QML_HAS_ATTACHED_PROPERTIES) #endif // BIRTHDAYPARTY_H ~~~ birthdayparty.cpp: ~~~ #include "birthdayparty.h" BirthdayPartyAttached::BirthdayPartyAttached(QObject *object) : QObject(object) { } QDate BirthdayPartyAttached::rsvp() const { return m_rsvp; } void BirthdayPartyAttached::setRsvp(const QDate &d) { if (d != m_rsvp) { m_rsvp = d; emit rsvpChanged(); } } BirthdayParty::BirthdayParty(QObject *parent) : QObject(parent), m_host(0) { } Person *BirthdayParty::host() const { return m_host; } void BirthdayParty::setHost(Person *c) { if (c == m_host) return; m_host = c; emit hostChanged(); } 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); } void BirthdayParty::startParty() { QTime time = QTime::currentTime(); emit partyStarted(time); } QString BirthdayParty::announcement() const { return QString(); } void BirthdayParty::setAnnouncement(const QString &speak) { qWarning() << qPrintable(speak); } BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object) { return new BirthdayPartyAttached(object); } ~~~ 在该系列第9个例子中,我们接触到了HappyBirthdaySong类,它是一个自定义的Property Value Source,用来为QML属性提供随时间变化的能力,类似于Animation。在该例子中,它被用于announcement属性。 happybirthdaysong.h: ~~~ #ifndef HAPPYBIRTHDAYSONG_H #define HAPPYBIRTHDAYSONG_H #include <QQmlPropertyValueSource> #include <QQmlProperty> #include <QStringList> class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_INTERFACES(QQmlPropertyValueSource) public: HappyBirthdaySong(QObject *parent = 0); virtual void setTarget(const QQmlProperty &); QString name() const; void setName(const QString &); private slots: void advance(); signals: void nameChanged(); private: int m_line; QStringList m_lyrics; QQmlProperty m_target; QString m_name; }; #endif // HAPPYBIRTHDAYSONG_H ~~~ happybirthdaysong.cpp: ~~~ #include "happybirthdaysong.h" #include <QTimer> HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent), m_line(-1) { setName(QString()); QTimer *timer = new QTimer(this); QObject::connect(timer, SIGNAL(timeout()), this, SLOT(advance())); timer->start(1000); } void HappyBirthdaySong::setTarget(const QQmlProperty &p) { m_target = p; } QString HappyBirthdaySong::name() const { return m_name; } void HappyBirthdaySong::setName(const QString &name) { if (m_name == name) return; m_name = name; m_lyrics.clear(); m_lyrics << "Happy birthday to you,"; m_lyrics << "Happy birthday to you,"; m_lyrics << "Happy birthday dear " + m_name + ","; m_lyrics << "Happy birthday to you!"; m_lyrics << ""; emit nameChanged(); } void HappyBirthdaySong::advance() { m_line = (m_line + 1) % m_lyrics.count(); m_target.write(m_lyrics.at(m_line)); } ~~~ 在main.cpp中将这些C++类注册成QML类型后,我们就可以在QML中创建一个实例化的BirthdayParty,并对其属性赋值: example.qml: ~~~ import People 1.0 import QtQuick 2.0 // For QColor // ![0] BirthdayParty { id: theParty HappyBirthdaySong on announcement { name: theParty.host.name } // 属性绑定 host: Boy { name: "Bob Jones" shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } } // ![0] onPartyStarted: console.log("This party started rockin' at " + time); Boy { name: "Leo Hodges" BirthdayParty.rsvp: "2009-07-06" shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 } } Boy { name: "Jack Smith" shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 } } Girl { name: "Anne Brown" BirthdayParty.rsvp: "2009-07-01" shoe.size: 7 shoe.color: "red" shoe.brand: "Marc Jacobs" shoe.price: 699.99 } // ![1] } // ![1] ~~~ 最后,在main.cpp调用这个属性的信息,并基于一定的规则输出这些信息: ~~~ #include <QCoreApplication> #include <QQmlEngine> #include <QQmlComponent> #include <QDebug> #include "birthdayparty.h" #include "happybirthdaysong.h" #include "person.h" int main(int argc, char ** argv) { QCoreApplication app(argc, argv); qmlRegisterType<BirthdayPartyAttached>(); qmlRegisterType<BirthdayParty>("People", 1,0, "BirthdayParty"); qmlRegisterType<HappyBirthdaySong>("People", 1,0, "HappyBirthdaySong"); qmlRegisterType<ShoeDescription>(); 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:"; for (int ii = 0; ii < party->guestCount(); ++ii) { Person *guest = party->guest(ii); QDate rsvpDate; QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false); if (attached) rsvpDate = attached->property("rsvp").toDate(); if (rsvpDate.isNull()) qWarning() << " " << guest->name() << "RSVP date: Hasn't RSVP'd"; else qWarning() << " " << guest->name() << "RSVP date:" << qPrintable(rsvpDate.toString()); } party->startParty(); } else { qWarning() << component.errors(); } return app.exec(); } ~~~ 输出如下: ![](https://box.kancloud.cn/2016-01-18_569cbd0917427.jpg)