Using custom data types with Qt
From QtCentreWiki
This article describes how to use custom data types conveniently and efficiently together with various classes of Qt. The following chapters use Player as an example custom data type:
struct Player
{
int number;
QString firstName;
QString lastName;
};
QVariant
Custom data types must be declared to Qt's meta system to be able to use them as custom types in QVariant. Q_DECLARE_METATYPE() macro is usually placed together with data type declaration so that the meta type declaration is available anywhere the custom data type is used:
struct Player
{
...
};
Q_DECLARE_METATYPE(Player);
To pass custom data types as QVariant, you will have to use QVariant::fromValue() (or qVariantFromValue).
Player player;
object->setProperty("property", QVariant::fromValue(player));
To make it a bit more convenient, you can implement a QVariant operator to your custom data type:
struct Player
{
...
operator QVariant() const
{
return QVariant::fromValue(*this);
}
};
This makes it possible to skip QVariant::fromValue() and you can simply write:
Player player;
object->setProperty("property", player);
Signals and slots
In case of direct connections, passing custom data types as signal parameters requires no additional tricks. Just establish the signal slot connection as usual:
connect(sender, SIGNAL(playerCreated(const Player&)), receiver, SLOT(addPlayer(const Player&)));
But as soon as you start passing custom data types across threads, you'll get a warning while attempting to establish a queued connection:
QObject::connect: Cannot queue arguments of type 'Player' (Make sure 'Player' is registered using qRegisterMetaType().)
The solution is as simple as what the warning suggests. You must call qRegisterMetaType() anywhere before establishing the connection:
qRegisterMetaType<Player>("Player");
connect(sender, SIGNAL(playerCreated(const Player&)), receiver, SLOT(addPlayer(const Player&)));
QDebug
It is possible to make qDebug() aware of custom data types. It's way more convenient to write:
qDebug() << player;
than
qDebug() << "Player(" << player.number << "," << player.firstName << "," << player.lastName << ")";
at least when you start doing it in more than one place. Instructions for providing support for the qDebug() stream operator are given in docs and here's yet another example:
inline QDebug operator<<(QDebug debug, const Player& player)
{
debug.nospace() << "Player("
<< player.number << ","
<< player.firstName << ","
<< player.lastName << ")";
return debug.space();
}
QDataStream
Just like with QDebug above, compared to doing it member by member, it is much more convenient and less error prone to serialize custom data types by providing specialized operators:
inline QDataStream& operator<<(QDataStream& out, const Player& player)
{
out << player.number;
out << player.firstName;
out << player.lastName;
return out;
}
inline QDataStream& operator>>(QDataStream& in, Player& player)
{
in >> player.number;
in >> player.firstName;
in >> player.lastName;
return in;
}
QSettings
To be able to use custom data types with QSettings, they must be first made known to Qt's meta system, just like with QVariant, and one must provide corresponding QDataStream operators. In addition to that, one must register the stream operators:
qRegisterMetaTypeStreamOperators<Player>("Player");
That's all needed to achieve such a nice syntax:
QSettings settings;
Player player;
settings.setValue("key", player);
QSettings settings;
Player player = value("key").value<Player>();
Containers
One can do some minor tweaks by providing accurate type information while using custom data types in Qt's container classes.
jpn 13:37, 21 November 2007 (CET)


