人分男人和女人,鞋的属性增加了,商标,颜色,价格,尺寸
class Person : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(ShoeDescription *shoe READ shoe) public: Person(QObject *parent = 0); QString name() const; void setName(const QString &); ShoeDescription *shoe(); private: QString m_name; ShoeDescription m_shoe; };
鞋这里当组合对象使用了,在人的内部作为一个属性。
重点看BirthdayParty和HappyBirthdaySong这两个类。
class BirthdayParty : public QObject { Q_OBJECT Q_PROPERTY(Person *host READ host WRITE setHost) Q_PROPERTY(QQmlListProperty<Person> guests READ guests) // ![0] Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement) // ![0] 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); private: Person *m_host; QList<Person *> m_guests; };
过生日的主人有一个m_host,参加者有多个m_guests,这里Q_CLASSINFO简单说明一下就是给这个类注册一个属性一个值,比如类的作者:wdj,时间:20120511。当然通过元对象是可以获取了。
再强调一下In QML, the type of a list properties - and the guests property is a list of people - are all of type QQmlListProperty<T>. QQmlListProperty is simple value type that contains a set of function pointers.原文意思所有的属性都是函数指针,看脚本
host: Boy { name: "Bob Jones" shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } }
host:啥意思,(1)BirthdayParty类的一个属性(2)代表BirthdayParty一个函数指针,明白了吧,这个类为qml提供了3个属性(函数指针)host,announcement
QML_DECLARE_TYPEINFO(BirthdayParty, QML_HAS_ATTACHED_PROPERTIES)这个宏我们应该是配合static BirthdayPartyAttached *qmlAttachedProperties(QObject *);来一起使用,qt帮助有一点点晦涩,开始没看懂,结合上面的例子,我个人觉得就是类似友元定义,(Qt叫Attached Property), BirthdayParty使用 BirthdayPartyAttached 属性(私有信息),呵呵,这样体现C++低耦合,我们就当是一个类去访问他的友元比较容易懂了。
class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) // ![0] Q_PROPERTY(QString name READ name WRITE setName) // ![1] public: HappyBirthdaySong(QObject *parent = 0); virtual void setTarget(const QQmlProperty &); // ![1] 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] };看这个类首先看看关于 QQmlPropertyValueSource介绍,用过JS,特别是AngularJS库的人知道我们说定义一个属性值,然后再HTML位置指定,当我们JS程序改变这个值的时候,页面自动就刷新了。这个类就是做这个用的,Property Value Sources中对此有介绍,有点晦涩,不过看代码应该能明白。
HappyBirthdaySong这个类声明是固定的,想用Property Value Sources就得这么继承着写,Q_OBJECT和Q_INTERFACES(QQmlPropertyValueSource)随之写上,没啥好说了,virtual void setTarget(const QQmlProperty &);虚函数必备,在这个类定义了一个定时器,实时去刷新m_target这个值,那么这个值绑定到哪里了呢,看脚本:HappyBirthdaySong on announcement,OK,绑定到了BirthdayParty的announcement了,
void BirthdayParty::setAnnouncement(const QString &speak) { qWarning() << qPrintable(speak); }这个属性每次被修改后就调用这个函数, qPrintable等价QString的toLocal8Bit().constData().
整个例子差不多分析完了,看main.cpp,qmlRegisterType将C++类型注册到QML引擎中,这里提一下没有参数的注册,This is an overloaded function.This template function registers the C++ type in the QML system. Instances of this type cannot be created from the QML system.原文在此,一个重载的模板函数,最后一句,这个类型的实例不可以被创建来自于QML系统。我理解是保护的作用,这里所有的人必须用派生类,辨别男女,如果你用一个Person对象的话显然程序凌乱了,这里起到一个保护作用,类型注册进去,但是不允许创建实例(C++中虚基类,单体都是这样)。
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(); }
qmlAttachedPropertiesObject获取那个友元对象,记得前面说的吗?
这里需要用标准字符看打印信息QTextCodec* codec = QTextCodec::codecForName("UTF-8");可以发现都是中文了。
最后我修正了下脚本:
import People 1.0 import QtQuick 2.0 // For QColor // ![0] BirthdayParty { HappyBirthdaySong on announcement { name: "Bob Jones" } // ![0] onPartyStarted1: console.log("This party started rockin' at " + time); host: Boy { name: "Bob Jones" shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } } guests:[ 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] }
这里需要说明脚本中的槽调用,在C++中定义了PartyStarted1信号,脚本中相应对应的槽就是on+信号名:后面输出响应信息即可。
最后,我觉得把参加人的对象写明确一些适合看代码(红色部分),不然会有些迷惑,原文不写guest,确可以将除host外的person全部划入guest我其实不是很理解,路过的可以留言,大家共同进步吧。