本文主要是解析Qt->QML->referenceexamples->attached示例的Demo,更多的是为了了解QML在Qt中的工作机制。
/*********************************************************************************************
* 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]