30——Extending QML – Binding Example
最后更新于:2022-04-01 07:22:12
本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873)
接上文[Qt5官方demo解析集29——Extending QML - Property Value Source Example](http://blog.csdn.net/cloud_castle/article/details/37526169)
还记得我们曾经在[Qt5官方demo解析集17——Chapter 3: Adding Property Bindings](http://blog.csdn.net/cloud_castle/article/details/36886779)一文中接触过QML自定义类型的属性绑定吗?如果不记得了,可以移步进行了解。因为项目尺寸的原因,那个例子可能更好理解。
这个例子也是我们Extending QML(扩展QML)系列的最后一个例子了,虽然相较前一个例子也只有小小的改动,不过我们还是把整个工程都完整的看一遍吧~
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-18_569cbd09034fd.jpg)
binding.qrc中是我们的qml文件,它实例化了BirthdayParty类以及其所有的子对象。
Person类建立了一个自定义的QML类型,由于它并不是一个可视化组件,且QML任何组件均基于Qt 的元对象系统,因此继承自QObject。
接着定义了ShoeDescription用来对Person类的shoe属性进行描述,使用特定的方法,我们在对shoe赋值时不需要实例化这个ShoeDescription组件。
再定义两个Person的派生类Boy、Girl,可以用来对Person对象分类。
person.h:
~~~
#ifndef PERSON_H
#define PERSON_H
#include <QObject>
#include <QColor>
class ShoeDescription : public QObject
{
Q_OBJECT
Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged) // NOTIFY用在属性绑定,当该属性值发生改变时发出信号shoeChanged
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged) // 通过该信号,我们就能使得被绑定的属性值随之发生改变
Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged)
Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged)
public:
ShoeDescription(QObject *parent = 0);
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);
signals:
void shoeChanged(); // 定义该shoeChanged()信号
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 NOTIFY nameChanged)
// ![0]
Q_PROPERTY(ShoeDescription *shoe READ shoe CONSTANT)
// ![0]
public:
Person(QObject *parent = 0);
QString name() const;
void setName(const QString &);
ShoeDescription *shoe();
signals:
void nameChanged();
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.cpp:
~~~
#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)
{
if (m_size == s)
return;
m_size = s;
emit shoeChanged(); // 该信号应该在该属性被正确写入后发出
}
QColor ShoeDescription::color() const
{
return m_color;
}
void ShoeDescription::setColor(const QColor &c)
{
if (m_color == c)
return;
m_color = c;
emit shoeChanged();
}
QString ShoeDescription::brand() const
{
return m_brand;
}
void ShoeDescription::setBrand(const QString &b)
{
if (m_brand == b)
return;
m_brand = b;
emit shoeChanged();
}
qreal ShoeDescription::price() const
{
return m_price;
}
void ShoeDescription::setPrice(qreal p)
{
if (m_price == p)
return;
m_price = p;
emit shoeChanged();
}
Person::Person(QObject *parent)
: QObject(parent)
{
}
QString Person::name() const
{
return m_name;
}
void Person::setName(const QString &n)
{
if (m_name == n)
return;
m_name = n;
emit nameChanged();
}
ShoeDescription *Person::shoe()
{
return &m_shoe;
}
Boy::Boy(QObject * parent)
: Person(parent)
{
}
Girl::Girl(QObject * parent)
: Person(parent)
{
}
~~~
接下来是我们的主类BirthdayParty,它也是example.qml中的根项目。它有一个以Person指针为参数的host属性,用来指明寿星;有一个以Person列表指针为参数guests属性,用来指明客人,并且该属性被设置为默认属性,这样在QML中没有指明属性的值将被划归它的名下;一个announcement属性,用来被动态改变以播放歌词。另外,该类还定义了一个partyStarted()信号,我们可以在QML中使用onPartyStarted 来响应该信号。
此外,再定义一个BirthdayPartyAttached类,它用来为BirthdayParty提供一个附加属性。
birthdayparty.h:
~~~
#ifndef BIRTHDAYPARTY_H
#define BIRTHDAYPARTY_H
#include <QObject>
#include <QDate>
#include <QDebug>
#include <qqml.h>
#include "person.h"
class BirthdayPartyAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged) // 该例中大多数属性均定义了属性绑定
public:
BirthdayPartyAttached(QObject *object);
QDate rsvp() const;
void setRsvp(const QDate &);
signals:
void rsvpChanged();
private:
QDate m_rsvp;
};
class BirthdayParty : public QObject
{
Q_OBJECT
// ![0]
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged)
// ![0]
Q_PROPERTY(QQmlListProperty<Person> guests READ guests)
Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement)
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);
void hostChanged();
private:
Person *m_host;
QList<Person *> m_guests;
};
QML_DECLARE_TYPEINFO(BirthdayParty, QML_HAS_ATTACHED_PROPERTIES)
#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)
{
if (d != m_rsvp) {
m_rsvp = d;
emit rsvpChanged();
}
}
BirthdayParty::BirthdayParty(QObject *parent)
: QObject(parent), m_host(0)
{
}
Person *BirthdayParty::host() const
{
return m_host;
}
void BirthdayParty::setHost(Person *c)
{
if (c == m_host) return;
m_host = c;
emit hostChanged();
}
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);
}
void BirthdayParty::startParty()
{
QTime time = QTime::currentTime();
emit partyStarted(time);
}
QString BirthdayParty::announcement() const
{
return QString();
}
void BirthdayParty::setAnnouncement(const QString &speak)
{
qWarning() << qPrintable(speak);
}
BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object)
{
return new BirthdayPartyAttached(object);
}
~~~
在该系列第9个例子中,我们接触到了HappyBirthdaySong类,它是一个自定义的Property Value Source,用来为QML属性提供随时间变化的能力,类似于Animation。在该例子中,它被用于announcement属性。
happybirthdaysong.h:
~~~
#ifndef HAPPYBIRTHDAYSONG_H
#define HAPPYBIRTHDAYSONG_H
#include <QQmlPropertyValueSource>
#include <QQmlProperty>
#include <QStringList>
class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_INTERFACES(QQmlPropertyValueSource)
public:
HappyBirthdaySong(QObject *parent = 0);
virtual void setTarget(const QQmlProperty &);
QString name() const;
void setName(const QString &);
private slots:
void advance();
signals:
void nameChanged();
private:
int m_line;
QStringList m_lyrics;
QQmlProperty m_target;
QString m_name;
};
#endif // HAPPYBIRTHDAYSONG_H
~~~
happybirthdaysong.cpp:
~~~
#include "happybirthdaysong.h"
#include <QTimer>
HappyBirthdaySong::HappyBirthdaySong(QObject *parent)
: QObject(parent), m_line(-1)
{
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;
}
QString HappyBirthdaySong::name() const
{
return m_name;
}
void HappyBirthdaySong::setName(const QString &name)
{
if (m_name == name)
return;
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 << "";
emit nameChanged();
}
void HappyBirthdaySong::advance()
{
m_line = (m_line + 1) % m_lyrics.count();
m_target.write(m_lyrics.at(m_line));
}
~~~
在main.cpp中将这些C++类注册成QML类型后,我们就可以在QML中创建一个实例化的BirthdayParty,并对其属性赋值:
example.qml:
~~~
import People 1.0
import QtQuick 2.0 // For QColor
// ![0]
BirthdayParty {
id: theParty
HappyBirthdaySong on announcement { name: theParty.host.name } // 属性绑定
host: Boy {
name: "Bob Jones"
shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 }
}
// ![0]
onPartyStarted: console.log("This party started rockin' at " + time);
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]
~~~
最后,在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();
}
~~~
输出如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-01-18_569cbd0917427.jpg)