如何在Qt中使用自定義數據類型

Q_DECLARE_METATYPEQt自定義類型

這裏我們使用下面這個struct來做說明(這裏不管是struct還是class都一樣):

  1. struct Player
  2. {
  3.     int number;
  4. QString firstName;
  5.     QString lastName;
  6. };

複製代碼

QVariant 
爲了能在QVariant中使用自定義數據類型做,需要使用Q_DECLARE_METATYPE()來向Qt的元系統聲明這個自定義類型。如下列所示:

  1. struct Player
  2. {
  3.     ...
  4. };
  5. Q_DECLARE_METATYPE(Player);

複製代碼

在作爲QVariant傳遞自定義數據類型時,需要使用QVariant::fromValue()或者qVariantFromValue:

  1. Player player;
  2. object->setProperty("property", QVariant::fromValue(player));

複製代碼

爲了更方便一點,你可以在自定義類型中定義一個QVariant() 類型轉換符:

  1. struct Player
  2. {
  3.     ...
  4.     operator QVariant() const
  5.     {
  6.         return QVariant::fromValue(*this);
  7.     }
  8. };

複製代碼

這樣我們便可以像下面這樣使用了:

  1. Player player;
  2. object->setProperty("property", player);

複製代碼

信號和槽 
對於直接連接類型(默認情況下就是直接連接)而言,使用自定義數據類型做信號參數不需要做其他其他處理,就像內置數據類型一樣:

  1. connect(sender, SIGNAL(playerCreated(const Player&)), receiver, SLOT(addPlayer(const Player&)));

複製代碼

但在跨線程時如果你還這麼做,編譯器就會給出警告了:

  1. QObject::connect: Cannot queue arguments of type 'Player'
  2. (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 
最好是能這樣:

  1. qDebug() << player;

複製代碼

而不是這樣:

  1. qDebug() << "Player(" << player.number << "," << player.firstName << "," << player.lastName << ")";

複製代碼

怎麼做呢?我們需要對QDebug<<操作符重載一下:

  1. inline QDebug operator<<(QDebug debug, const Player& player)
  2. {
  3.         debug.nospace() << "Player("
  4.                 << player.number << ","
  5.                 << player.firstName << ","
  6.                 << player.lastName << ")";
  7.         return debug.space();
  8. }

複製代碼

QDataStream 
跟上面的QDebug很像,我們也需要重載一下<<操作符:

  1. inline QDataStream& operator<<(QDataStream& out, const Player& player)
  2. {
  3.     out << player.number;
  4.     out << player.firstName;
  5.     out << player.lastName;
  6.     return out;
  7. }
  8. inline QDataStream& operator>>(QDataStream& in, Player& player)
  9. {
  10.     in >> player.number;
  11.     in >> player.firstName;
  12.     in >> player.lastName;
  13.     return in;
  14. }

複製代碼

QSettings

QSettings 用QVariant保存鍵值,用QDataStream序列化自定義數據。(參考後面的variantToString函數) 
爲了能在QSettings中使用自定義數據類型,需要讓Qt的元系統知道有此類型,就像上面介紹QVariant部分一樣,另外還要提供相應的QDataStream操作符,還必須註冊這個流操作符:

  1. qRegisterMetaTypeStreamOperators<Player>("Player");

複製代碼

如此處理之後我們就可以像下面這樣使用了:

  1. QSettings settings;
  2. Player player;
  3. settings.setValue("key", player);

複製代碼

  1. QSettings settings;
  2. 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

 

 

If you store types that QVariant can't convert to QString (e.g., QPoint, QRect, and QSize), Qt uses an @-based syntax to encode the type. For example: 
pos = @Point(100 100) 
To minimize compatibility issues, any @ that doesn't appear at the first position in the value or that isn't followed by a Qt type (Point, Rect, Size, etc.) is treated as a normal character.

不能轉化爲 QString 的QVariant 會以@-based 編碼,base爲(Point, Rect, Size, etc.)

Although backslash is a special character in INI files, most Windows applications don't escape backslashes (\) in file paths: 
windir = C:\Windows 
QSettings always treats backslash as a special character and provides no API for reading or writing such entries.

反斜槓不能用在鍵值中

The INI file format has severe restrictions on the syntax of a key. Qt works around this by using % as an escape character in keys. In addition, if you save a top-level setting (a key with no slashes in it, e.g., "someKey"), it will appear in the INI file's "General" section. To avoid overwriting other keys, if you save something using the a key such as "General/someKey", the key will be located in the "%General" section, not in the "General" section.

key用%轉義。頂層的 key會在General段中,General/someKey會在"%General"段中。


Following the philosophy that we should be liberal in what we accept and conservative in what we generate, QSettings will accept Latin-1 encoded INI files, but generate pure ASCII files, where non-ASCII values are encoded using standard INI escape sequences. To make the INI files more readable (but potentially less compatible), call setIniCodec().

意思是生成的ini是ascii的,non-ASCII全部被轉移,latin-1也不例外

發佈了7 篇原創文章 · 獲贊 13 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章