Qt QML referenceexamples attached Demo hacking
/********************************************************************************************* * Qt QML referenceexamples attached Demo hacking * 说明: * 1. 本源代码来自Qt自带的Example,而本文也仅仅是代码解读,需要有点基础; * 2. 由于是Qt自带Demo,分为几个文件,文件存在联系,而本人把所有代码放在这个文件里,会照成阅读困难; * 3. 由于2中的原因,请尽量在Qt中阅读源程序; * 4. 强烈建议您使用Qt中的FakeVim进行代码阅读,当然这也只是个建议; :) * * 2015-5-17 深圳 晴 南山平山村 曾剑锋 ********************************************************************************************/ \\\\\\\\\-*- 目录 -*-///////// | 一、main.cpp | 二、person.h | 三、person.c | 四、birthdayparty.h | 五、birthdayparty.cpp | 六、example.qml \\\\\\\\\\\\\\\////////////// 一、main.cpp #include <QCoreApplication> #include <QQmlEngine> #include <QQmlComponent> #include <QDebug> #include "birthdayparty.h" #include "person.h" int main(int argc, char ** argv) { /** * The QApplication class manages the GUI application's control flow and main settings. * 初始化并配置GUI界面环境 */ QCoreApplication app(argc, argv); /** * This template function registers the C++ type in the QML system with the name qmlName, * in the library imported from uri having the version number composed from versionMajor * and versionMinor. * * For example, this registers a C++ class MySliderItem as a QML type named Slider for * version 1.0 of a type namespace called "com.mycompany.qmlcomponents": * qmlRegisterType<MySliderItem>("com.mycompany.qmlcomponents", 1, 0, "Slider"); * * qmlRegisterType<Person>("People", 1,0, "Person"): * 1. qmlRegisterType是用来向QML系统注册C++类型的; * 2. 这里相当于向QML系统注册了一个Person 1.0 版本的类; * 3. 指定了命名空间为Person,所以在qml文件中需要用import People 1.0,引入命名空间, * 当然这里也制定了版本号; * 参数说明j: * 1. 泛型<Person>代表要注册进QML系统C++类; * 2. 第一个参数是需要创建的QML命名空间; * 3. 第二、三个参数是对应的QML类型的版本号; * 4. 第四个参数是C++类对应的QML类型的名字; * * 如果注释掉这一行会出现以下错误,编译运行时错误结果: * QQmlComponent: Component is not ready * (qrc:example.qml:42:1: module "People" is not installed) * */ qmlRegisterType<BirthdayPartyAttached>(); qmlRegisterType<BirthdayParty>("People", 1,0, "BirthdayParty"); qmlRegisterType<ShoeDescription>(); qmlRegisterType<Person>(); qmlRegisterType<Boy>("People", 1,0, "Boy"); qmlRegisterType<Girl>("People", 1,0, "Girl"); /** * QQmlApplicationEngine provides a convenient way to load an application from a single QML file. * 创建QML引擎(engine) */ QQmlEngine engine; //QML引擎 /** * The QQmlComponent class encapsulates a QML component definition Components are reusable, * encapsulated QML types with well-defined interfaces. * 个人理解就是加载QML文件的意思,利用component.create()创建对象 */ 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!"; //console output /** * Returns the given object cast to type T if the object is of type T (or of a subclass); * otherwise returns 0. If object is 0 then it will also return 0. * 这里相当于类型判断的意思 */ if (qobject_cast<Boy *>(party->host())) qWarning() << "He is inviting:"; else qWarning() << "She is inviting:"; /** * 这是我自己添加的测试代码,主要用于测试example.qml中的BirthdayParty.rsvp: "2015-05-16"可否写 * 在 BirthdayParty {}里面,测试结果:不一定要写在 Boy {}里面,可以写在任何地方。 */ /** * qmlAttachedPropertiesObject: This returns the attached object instance that has been * attached to the specified attachee by the attaching type T. */ QObject *attached_out = qmlAttachedPropertiesObject<BirthdayParty>(party, false); QDate rsvpDate_out; if (attached_out) rsvpDate_out = attached_out->property("rsvp").toDate(); qWarning() << " zjf " << "RSVP date:" << qPrintable(rsvpDate_out.toString()); for (int ii = 0; ii < party->guestCount(); ++ii) { Person *guest = party->guest(ii); //! [query rsvp] QDate rsvpDate; /** * This returns the attached object instance that has been attached to the * specified attachee by the attaching type T. */ QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false); if (attached) rsvpDate = attached->property("rsvp").toDate(); //! [query rsvp] if (rsvpDate.isNull()) qWarning() << " " << guest->name() << "RSVP date: Hasn't RSVP'd"; else qWarning() << " " << guest->name() << "RSVP date:" << qPrintable(rsvpDate.toString()); } } else { qWarning() << component.errors(); } /** * QStringLiteral: Creating a QString from it is free in this case, and the generated string data * is stored in the read-only segment of the compiled object file. * 这是一个宏,用于创建一个字符串,该字符串存放在自读数据区 * QUrl: The most common way to use QUrl is to initialize it via the constructor by passing a QString. * Otherwise, setUrl() can also be used. * 最常用于初始化一个QUrl的是给其构造函数传一个字符串,此外也可以使用setUrl() * engine.load: Loads the root QML file located at filePath:. * 加载用QML引擎加载要显示的界面 */ //engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); /** * Enters the main event loop and waits until exit() is called. * 进入主事件循环,并等待直到exit()函数被调用 */ //return app.exec(); return 0; } 二、person.h #ifndef PERSON_H #define PERSON_H #include <QObject> #include <QColor> class ShoeDescription : public QObject { /** * The Q_OBJECT macro must appear in the private section of a class definition * that declares its own signals and slots or that uses other services provided * by Qt's meta-object system. * Q_OBJECT宏应该使用在一个类定义时的私有段,其声明了一些信号和槽 */ Q_OBJECT /** * The Property System: * To declare a property, use the Q_PROPERTY() macro in a class that inherits QObject. * Q_PROPERTY(type name * (READ getFunction [WRITE setFunction] | * MEMBER memberName [(READ getFunction | WRITE setFunction)]) * [RESET resetFunction] * [NOTIFY notifySignal] * [REVISION int] * [DESIGNABLE bool] * [SCRIPTABLE bool] * [STORED bool] * [USER bool] * [CONSTANT] * [FINAL]) * 这里采用宏的形式来对变量进行声明定义,由于class默认是私有属性,所以这里我们无法直接 * 访问name、shoeSize,要通过其READ、WRITE函数来进行访问(access) */ 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); //默认构造函数 /** * 接下来是一些READ、WRITE函数的声明 */ 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 { 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; }; /** * 男孩继承自人类 */ 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.c #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) { } 四、birthdayparty.h #ifndef BIRTHDAYPARTY_H #define BIRTHDAYPARTY_H #include <QObject> #include <QDate> #include <qqml.h> #include "person.h" // 这一部分内容基本已经在person.h中已经解释了 class BirthdayPartyAttached : public QObject { Q_OBJECT Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp) public: BirthdayPartyAttached(QObject *object); QDate rsvp() const; void setRsvp(const QDate &); private: QDate m_rsvp; }; class BirthdayParty : public QObject { Q_OBJECT Q_PROPERTY(Person *host READ host WRITE setHost) /** * For QMap, QList, and QValueList properties, the property value is a QVariant whose * value is the entire list or map. Note that the Q_PROPERTY string cannot contain * commas, because commas separate macro arguments. Therefore, you must use QMap as * the property type instead of QMap<QString,QVariant>. For consistency, also use * QList and QValueList instead of QList<QVariant> and QValueList<QVariant>. * * 请注意这种写法, */ Q_PROPERTY(QQmlListProperty<Person> guests READ guests) /** * Any QObject-derived type that is registered as an instantiable QML object type * can optionally specify a default property for the type. A default property is * the property to which an object's children are automatically assigned if they * are not assigned to any specific property. * * The default property can be set by calling the Q_CLASSINFO() macro for a class * with a specific "DefaultProperty" value. For example, the MessageBoard class below * specifies its messages property as the default property for the class: * * 将属性guests设为默认属性,这样在QML文件中就可以简写了 */ 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; //! [static attached] /** * The mechanisms for providing attached objects can be implemented from C++ by * providing classes for the attached object type and attaching type. For the * attached object type, provide a QObject-derived class that defines the attributes * to be made accessible to attachee objects. For the attaching type, provide a * QObject-derived class that. * * implements a static qmlAttachedProperties() with the following signature: */ static BirthdayPartyAttached *qmlAttachedProperties(QObject *); //! [static attached] private: Person *m_host; QList<Person *> m_guests; }; //! [declare attached] /** * Declares additional properties of the given Type as described by the specified Flags. * Current the only supported type info is QML_HAS_ATTACHED_PROPERTIES which declares * that the Type supports attached properties. * * 这里感觉是告诉系统BirthdayParty类附属性,根据example.qml文件里里的写法, * 附属性写法为BirthdayParty.rsvp */ QML_DECLARE_TYPEINFO(BirthdayParty, QML_HAS_ATTACHED_PROPERTIES) //! [declare attached] #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) { m_rsvp = d; } 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); } /** * 实现了attached需要的函数 */ BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object) { return new BirthdayPartyAttached(object); } 六、example.qml import People 1.0 import QtQuick 2.0 // For QColor //! [begin] BirthdayParty { //! [begin] //! [rsvp] Boy { name: "Robert Campbell" /** * 这里为什么是这样实现,目前还不是很清楚,但经过测试发现,貌似这就是attach的的意思。 * 另外个人从BirthdayParty.rsvp代表BirthdayParty的附加属性rsvp. */ BirthdayParty.rsvp: "2009-07-01" } //! [rsvp] // ![1] Boy { name: "Leo Hodges" shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 } BirthdayParty.rsvp: "2009-07-06" } // ![1] host: Boy { name: "Jack Smith" shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 } } /** * 本人自己添加的额外的代码,用于测试附加属性是否可以放在这里,相当于测试作用域的样子 */ BirthdayParty.rsvp: "2015-05-16" //! [end] } //! [end]