如何在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也不例外

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值