且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Qt QML referenceexamples attached Demo hacking

更新时间:2022-05-08 22:50:41

/*********************************************************************************************
 *                       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]