Reputation: 11732
The Qt docs contain this example:
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
void setAuthor(const QString &a) {
if (a != m_author) {
m_author = a;
emit authorChanged();
}
}
QString author() const {
return m_author;
}
signals:
void authorChanged();
private:
QString m_author;
};
Is there a way to avoid writing all this boilerplate code just to define a QML-interoperable property? For example, since this code is calling the Q_PROPERTY macro anyway, can't this macro do the dirty work for me and define all the rest automatically?
Note: I've found a nice-looking set of macros here but it's not an official part of Qt, so I'm not sure if it's free of gotchas.
Upvotes: 4
Views: 666
Reputation: 4085
There is no standard solution for this, but then I looked on this issue while ago I found this, QmlTricks which I think is really nice implementation.
MEMBER is a way to go, but sometimes you might need more control of what's happening inside setter/getter methods, so to be honest I don't use it often.
I made my own subset with some additional macros like QObject's support for properties, custom setter/getter etc.
#define QOBJECT_CONSTANT_PROPERTY(typeName, propertyName, getterName, setterName) \
public: \
Q_PROPERTY (typeName * propertyName READ getterName CONSTANT) \
protected: \
QPointer<typeName> m_##getterName; \
public: \
typeName * getterName () const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName (typeName * newVal) { \
bool ret = false; \
if ((ret = m_##getterName != newVal)) { \
m_##getterName = newVal; \
} \
return ret; \
} \
public:
#define QOBJECT_WRITABLE_PROPERTY(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY(typeName * propertyName READ getterName WRITE setterName NOTIFY signalName) \
protected: \
QPointer<typeName> m_##getterName; \
public: \
typeName * getterName() const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName(typeName * newVal) { \
bool ret = false; \
if ((ret = (m_##getterName != newVal))) { \
m_##getterName = newVal; \
emit signalName(#propertyName); \
} \
return ret; \
} \
Q_SIGNALS: \
void signalName(const char *); \
public:
#define QOBJECT_READONLY_PROPERTY(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY (typeName * propertyName READ getterName NOTIFY signalName) \
protected: \
QPointer<typeName> m_##getterName; \
public: \
typeName * getterName () const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName (typeName * newVal) { \
bool ret = false; \
if ((ret = (m_##getterName != newVal))) { \
m_##getterName = newVal; \
emit signalName (#propertyName); \
} \
return ret; \
} \
Q_SIGNALS: \
void signalName (const char *); \
public:
#define WRITABLE_PROPERTY(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY (typeName propertyName READ getterName WRITE setterName NOTIFY signalName) \
protected: \
typeName m_##getterName; \
public: \
typeName getterName () const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName (const typeName & newVal) { \
bool ret = false; \
if ((ret = (m_##getterName != const_cast<typeName&>(newVal)))) { \
m_##getterName = newVal; \
emit signalName (#propertyName); \
} \
return ret; \
} \
Q_SIGNALS: \
void signalName ( const char * ); \
public:
#define WRITABLE_PROPERTY_USER_GETTER(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY(typeName propertyName READ getterName WRITE setterName NOTIFY signalName) \
protected: \
typeName m_##getterName; \
public Q_SLOTS: \
bool setterName(const typeName & newVal) { \
bool ret = false; \
if ((ret = (m_##getterName != const_cast<typeName&>(newVal)))) { \
m_##getterName = newVal; \
emit signalName(#propertyName); \
} \
return ret; \
} \
Q_SIGNALS: \
void signalName(const char *); \
public:
#define WRITABLE_PROPERTY_USER_SETTER(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY (typeName propertyName READ getterName WRITE setterName NOTIFY signalName) \
protected: \
typeName m_##getterName; \
public: \
typeName getterName () const { \
return m_##getterName; \
} \
Q_SIGNALS: \
void signalName ( const char * ); \
public:
#define READONLY_PROPERTY(typeName, propertyName, getterName, setterName, signalName) \
public: \
Q_PROPERTY (typeName propertyName READ getterName NOTIFY signalName) \
protected: \
typeName m_##getterName; \
public: \
typeName getterName () const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName (const typeName & newVal) { \
bool ret = false; \
if ((ret = (m_##getterName != const_cast<typeName&>(newVal)))) { \
m_##getterName = newVal; \
emit signalName (#propertyName); \
} \
return ret; \
} \
Q_SIGNALS: \
void signalName (const char *); \
public:
#define CONSTANT_PROPERTY(typeName, propertyName, getterName, setterName) \
public: \
Q_PROPERTY (typeName propertyName READ getterName CONSTANT) \
protected: \
typeName m_##getterName; \
public: \
typeName getterName () const { \
return m_##getterName; \
} \
public Q_SLOTS: \
bool setterName (const typeName & newVal) { \
bool ret = false; \
if ((ret = m_##getterName != newVal)) { \
m_##getterName = const_cast<typeName&>(newVal); \
} \
return ret; \
} \
public:
#define LIST_PROPERTY(CLASS, NAME, TYPE) \
public: \
static int NAME##_count (QQmlListProperty<TYPE> * prop) { \
CLASS * instance = qobject_cast<CLASS *> (prop->object); \
return (instance != NULL ? instance->m_##NAME.count () : 0); \
} \
static void NAME##_clear (QQmlListProperty<TYPE> * prop) { \
CLASS * instance = qobject_cast<CLASS *> (prop->object); \
if (instance != NULL) { \
instance->m_##NAME.clear (); \
} \
} \
static void NAME##_append (QQmlListProperty<TYPE> * prop, TYPE * obj) { \
CLASS * instance = qobject_cast<CLASS *> (prop->object); \
if (instance != NULL && obj != NULL) { \
instance->m_##NAME.append (obj); \
} \
} \
static TYPE * NAME##_at (QQmlListProperty<TYPE> * prop, int idx) { \
CLASS * instance = qobject_cast<CLASS *> (prop->object); \
return (instance != NULL ? instance->m_##NAME.at (idx) : NULL); \
} \
QList<TYPE *> get_##NAME##s (void) const { \
return m_##NAME; \
} \
private: \
QList<TYPE *> m_##NAME;
Upvotes: 4
Reputation: 49329
You can avoid the implementation of accessor functions if you use MEMBER
properties. Then you only need the member and the optional notify signal.
class Message : public QObject {
Q_OBJECT
Q_PROPERTY(QString author MEMBER m_author NOTIFY authorChanged)
QString m_author;
public:
// ...
signals:
void authorChanged();
};
The macro approach should work fine, however there are two problems with the one you have linked:
So using MEMBER
you can simplify it further:
#define QPROP(type, name) \
private: \
Q_PROPERTY(type name MEMBER m_ ## name NOTIFY name ## Changed ) \
type m_ ## name; \
public: \
Q_SIGNAL void name ## Changed();
And then just:
class Message : public QObject {
Q_OBJECT
QPROP(QString, author)
};
Upvotes: 2