本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873) 接上文[Qt5官方demo解析集28——Extending QML - Signal Support Example](http://blog.csdn.net/cloud_castle/article/details/37512407) 我们经常会在QML代码中使用Animation和bindings,以使得我们的程序具有更好的动态性能。那么,类似NumberAnimation这种QML类似实际上是提供了一个算法来为属性提供动态变化的数值,或者说是提供了一个值的集合。这里Qt将其称作“属性值来源”(Property Value Source),并为这些QML类型提供了一个通用的接口,即QQmlPropertyValueSource。通过继承这个类,我们可以实现自定义的Property Value Source。 在前面的项目中添加一个类happybirthdaysong,用来自定义地控制BirthdayParty中announcement属性的变化: ![](https://box.kancloud.cn/2016-01-18_569cbd08ccdd9.jpg) 这个demo向我们展示了这个自定义的过程,happybirthdaysong.h: ~~~ #ifndef HAPPYBIRTHDAYSONG_H #define HAPPYBIRTHDAYSONG_H #include <QQmlPropertyValueSource> #include <QQmlProperty> #include <qqml.h> #include <QStringList> // ![0] class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource // 由于QQmlPropertyValueSource是一个接口类 { // 我们还需要继承QObject Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) // 声明接口 // ![0] Q_PROPERTY(QString name READ name WRITE setName) // name属性用来设置生日歌的对象 // ![1] public: HappyBirthdaySong(QObject *parent = 0); virtual void setTarget(const QQmlProperty &); // 用来指明作用的属性对象 // ![1] // <PropertyValueSource> on <property>时被调用 QString name() const; // 自定义属性的读写函数 void setName(const QString &); private slots: void advance(); // 更新函数,每秒输出一句歌词 private: int m_line; QStringList m_lyrics; QQmlProperty m_target; QString m_name; // ![2] }; // ![2] #endif // HAPPYBIRTHDAYSONG_H ~~~ happybirthdaysong.cpp: ~~~ #include "happybirthdaysong.h" #include <QTimer> HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent), m_line(-1) // 初始化m_line为-1 { // 使advance()第一次被调用时输出第一句歌词 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; // 该类型作用于某个属性时,Qt会使用这里的函数 } QString HappyBirthdaySong::name() const { return m_name; } void HappyBirthdaySong::setName(const QString &name) // 初始化歌词,并带上“姓名”参数 { 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 << ""; } void HappyBirthdaySong::advance() // 循环显示的好方式 { m_line = (m_line + 1) % m_lyrics.count(); m_target.write(m_lyrics.at(m_line)); } ~~~ Person类没有变化,而BirthdayParty类则单纯地添加了一个属性announcement来使上面的Source能作用其上,它与其他属性没有不同,类型为QString,用来赋予不同的歌词。 ~~~ Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement) ~~~ 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(); } ~~~ 最后,这个Source需要通过 on 这样的语句来调用, 因此在QML文件中添加了HappyBirthdaySongonannouncement{name:"BobJones"},使得这个程序得以循环地为Bob Jones唱生日快乐歌: example.qml: ~~~ import People 1.0 import QtQuick 2.0 // For QColor // ![0] BirthdayParty { HappyBirthdaySong on announcement { name: "Bob Jones" } // ![0] onPartyStarted: console.log("This party started rockin' at " + time); host: Boy { name: "Bob Jones" shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } } 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] ~~~ 效果如下: ![](https://box.kancloud.cn/2016-01-18_569cbd08e3c11.jpg)