使用下面這個struct來做說明(這裏不管是struct還是class都壹樣):
struct Player
{
int number;
QString firstName;
QString lastName;
};
QVariant
為了能在QVariant中使用自定義數據類型做,需要使用Q_DECLARE_METATYPE()來向Qt的元系統聲明這個自定義類型。如下列所示:
struct Player
{
...
};
Q_DECLARE_METATYPE(Player);
在作為QVariant傳遞自定義數據類型時,需要使用QVariant::fromValue()或者qVariantFromValue:
Player player;
object->setProperty("property", QVariant::fromValue(player));
為了更方便壹點,妳可以在自定義類型中定義壹個QVariant() 類型轉換符:
struct Player
{
...
operator QVariant() const
{
return QVariant::fromValue(*this);
}
};
這樣我們便可以像下面這樣使用了:
Player player;
object->setProperty("property", player);
信號和槽
對於直接連接類型(默認情況下就是直接連接)而言,使用自定義數據類型做信號參數不需要做其他其他處理,就像內置數據類型壹樣:
connect(sender, SIGNAL(playerCreated(const Player&)), receiver, SLOT(addPlayer(const Player&)));
但在跨線程時如果妳還這麽做,編譯器就會給出警告了:
QObject::connect: Cannot queue arguments of type 'Player'
(Make sure 'Player' is registered using qRegisterMetaType().)
這時我們需要先註冊Player:
qRegisterMetaType<Player>("Player");?
qRegisterMetaType<Player>( );? (上面那個是錯誤的,除非名字剛好和類名壹樣)
connect(sender, SIGNAL(playerCreated(const Player&)), receiver, SLOT(addPlayer(const Player&)));
QDebug
最好是能這樣:
qDebug() << player;
而不是這樣:
qDebug() << "Player(" << player.number << "," << player.firstName << "," << player.lastName << ")";
怎麽做呢?我們需要對QDebug<<操作符重載壹下:
inline QDebug operator<<(QDebug debug, const Player& player)
{
debug.nospace() << "Player("
<< player.number << ","
<< player.firstName << ","
<< player.lastName << ")";
return debug.space();
}
QDataStream
跟上面的QDebug很像,我們也需要重載壹下<<操作符:
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
QSettings 用QVariant保存鍵值,用QDataStream序列化自定義數據。(參考後面的variantToString函數) ?
為了能在QSettings中使用自定義數據類型,需要讓Qt的元系統知道有此類型,就像上面介紹QVariant部分壹樣,另外還要提供相應的QDataStream操作符,還必須註冊這個流操作符:
qRegisterMetaTypeStreamOperators<Player>("Player");
如此處理之後我們就可以像下面這樣使用了:
QSettings settings;
Player player;
settings.setValue("key", player);
QSettings settings;
Player player = value("key").value<Player>();
參考:
QString QSettingsPrivate::variantToString(const QVariant &v)
{
QString result;
switch (v.type()) {
case QVariant::Invalid:
result = QLatin1String("@Invalid()");
break;
case QVariant::ByteArray: {
QByteArray a = v.toByteArray();
result = QLatin1String("@ByteArray(");
result += QString::fromLatin1(a.constData(), a.size());
result += QLatin1Char(')');
break;
}
case QVariant::String:
case QVariant::LongLong:
case QVariant::ULongLong:
case QVariant::Int:
case QVariant::UInt:
case QVariant::Bool:
case QVariant::Double:
case QVariant::KeySequence: {
result = v.toString();
if (result.startsWith(QLatin1Char('@')))
result.prepend(QLatin1Char('@'));
break;
}
#ifndef QT_NO_GEOM_VARIANT
case QVariant::Rect: {
QRect r = qvariant_cast<QRect>(v);
result += QLatin1String("@Rect(");
result += QString::number(r.x());
result += QLatin1Char(' ');
result += QString::number(r.y());
result += QLatin1Char(' ');
result += QString::number(r.width());
result += QLatin1Char(' ');
result += QString::number(r.height());
result += QLatin1Char(')');
break;
}
case QVariant::Size: {
QSize s = qvariant_cast<QSize>(v);
result += QLatin1String("@Size(");
result += QString::number(s.width());
result += QLatin1Char(' ');
result += QString::number(s.height());
result += QLatin1Char(')');
break;
}
case QVariant::Point: {
QPoint p = qvariant_cast<QPoint>(v);
result += QLatin1String("@Point(");
result += QString::number(p.x());
result += QLatin1Char(' ');
result += QString::number(p.y());
result += QLatin1Char(')');
break;
}
#endif // !QT_NO_GEOM_VARIANT
default: {
#ifndef QT_NO_DATASTREAM
QByteArray a;
{
QDataStream s(&a, QIODevice::WriteOnly);
s.setVersion(QDataStream::Qt_4_0);
s << v;
}
result = QLatin1String("@Variant(");
result += QString::fromLatin1(a.constData(), a.size());
result += QLatin1Char(')');
#else
Q_ASSERT(!"QSettings: Cannot save custom types without QDataStream support");
#endif
break;
}
}
return result;
}
qsetting為了讓保存的ini文件能和ascii兼容,所以
我們將ini文件中的鍵值讀入到 QVariant 中,需要兩個步驟:
因為文件內的壹些字符被轉義了,比如 "\x1234\t\0"等,所以需要 unescape
從 unescape 後的字符串構造出 QVariant
當將QVariant寫入文件時:
將 QVariant 轉換成字符串
處理字符串中的特殊字符,即 escape