Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
Qt Property Macro (Q_PROPERTY with 95% less code)
Published: 29-05-2024 22:00 | Author: Remy van Elst | Text only version of this article
Table of Contents
Adding properties to QObject based classes is cumbersome. Although the property system and Signals and Slots are great to use, especially with QML, it takes a lot of boilerplate code to add such properties to a class, at least 15 to 20 lines for each property. I've cobbled up a macro that saves you about 95% of lines when using a Q_PROPERTY
. (22 lines to 1 line).
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
Adding a Q_PROPERTY
To add a property to a QObject based class, you have to add the following items to the class:
Q_PROPERTY
statement- Declaration of getter and setter methods
- Definition of getter and setter methods
- Value changed signal definition
- Member variable
Recent versions of Qt Creator have made this more efficient by auto-filling
the Q_PROPERTY
statement and via a right-click you can now go
to Refactor
then Add missing Q_PROPERTY members
, but that is still
more work and more typing than using a macro that does it all for you.
With this macro, QObject::connect
still works as you would expect just as
accessing the property from QML.
There also is the MEMBER
Qt property type but that is still more boilerplate
than this because you still need to write the entire line including notify
signal and member variables.
QObject Property Macro
This is the macro I've defined:
/* Macro to define Q_PROPERTY backed by a regular value
* QP_V = Q_Property, value (not reference) */
#define QP_V(Type, Name) \
private: \
Q_PROPERTY(Type Name READ Name WRITE set##Name NOTIFY Name##Changed FINAL) \
Type _##Name; \
public: \
Type Name() const { return _##Name; } \
public Q_SLOTS: \
void set##Name(Type value) { if (_##Name != value) { _##Name = value; Q_EMIT Name##Changed(value); } } \
Q_SIGNALS: \
void Name##Changed(Type)
// end macro
Due to macro expansion combined with moc
I'm using Q_SIGNALS
, Q_SLOTS
and Q_EMIT
. The regular signals
, slots
and emit
keywords do not work
in this construction. These macro's can also be used if you want to use a
third-party signals and slots system.
You can use it like this:
QP_V(property Type, property Name);
Example:
QP_V(bool, myBool);
The only thing I haven't got working is capitalization of function names.
Convention is to use setMyThingChanged
. With the macro, that becomes setmyThingChanged
.
You can capitalize you variable to fix that.
The macro is simple enough that you can easily update it for for example
CONSTANT
properties or read-only properties (without a WRITE
method).
Usage example:
In my monitoring app there is an update check, which
uses a Q_PROPERTY
. That is now just one line:
QP_V(bool, newVersionAvailable);
Instead of all of these lines in the header file:
private:
Q_PROPERTY(bool newVersionAvailable READ newVersionAvailable WRITE setNewVersionAvailable NOTIFY newVersionAvailableChanged FINAL)```
public:
bool newVersionAvailable() const;
signals:
void newVersionAvailableChanged(bool newVersionAvailable);
public slots:
setNewVersionAvailable(bool newNewVersionAvailable);
private:
bool _newVersionAvailable = false;
And in the implementation (.cpp
) file:
bool MyClass::newVersionAvailable() const
{
return _newVersionAvailable;
}
void MyClass::setNewVersionAvailable(bool newNewVersionAvailable)
{
if (_newVersionAvailable == newNewVersionAvailable)
return;
_newVersionAvailable = newNewVersionAvailable;
emit newVersionAvailableChanged(_newVersionAvailable);
}
Saving all that boilerplate code makes it easier to read the source and figure out what actually happens and matters instead of hundreds and hundreds of lines of boilerplate.
Tags: blog , c++ , cmake , cpp , moc , property , q_property , qmake , qml , qt , qt5