这里我们使用下面这个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也不例外

如何在Qt中使用自定义数据类型的更多相关文章

  1. 自主数据类型:在TVM中启用自定义数据类型探索

    自主数据类型:在TVM中启用自定义数据类型探索 介绍 在设计加速器时,一个重要的决定是如何在硬件中近似地表示实数.这个问题有一个长期的行业标准解决方案:IEEE 754浮点标准.1.然而,当试图通过构 ...

  2. 如何在Qt中处理(接收/发送)MFC或Windows消息(直接覆盖MainDialog::nativeEvent,或者QApplication::installNativeEventFilter安装过滤器,或者直接改写QApplication::nativeEventFilter)

    关于接收: Receive WM_COPYDATA messages in a Qt app. 还有个中文网站: 提问: 如何在Qt中模拟MFC的消息机制 关于发送: 用Qt在Windows下编程,如 ...

  3. 详解如何在Laravel中增加自定义全局函数

    http://www.php.cn/php-weizijiaocheng-383928.html 如何在Laravel中增加自定义全局函数?在我们的应用里经常会有一些全局都可能会用的函数,我们应该怎么 ...

  4. Qt信号之自定义数据类型

    [1]为什么需要自定义数据类型? 内置类型毕竟很有局限性,否则为什么还需要类呢.总之,有时候,我们多么希望信号能发送自定义数据类型. 幸哉~ Qt是支持自定义信号,且自定义信号可以发送自定义数据类型的 ...

  5. QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类

    一般情况下信号槽直接连接方式不会出现问题,但是如果信号与槽在不同线程或Qt::QueuedConnection方式连接,可能会在连接期间报以下类似问题,如: QObject::connect: Can ...

  6. QT总结第3篇:如何在QT中添加.lib,.dll还有.h文件

    因为我在工作的过程中,使用的是第三方提供的库,但是如何将这些库添加到QT的工程中,是个问题,让我恼火了很久,怎么弄都是错的. 下面,我会对这个问题,进行叙述,希望其他人第一次遇到这种问题的时候,可以轻 ...

  7. 如何在SharePoint2010中创建自定义电子邮件警报处理程序

    字段,如项目名称字段中,将被截断到的电子邮件通知中的 70 个字符.要解决 70 个字符的限制,请使用"更多信息"一节中的介绍的方法. 要嵌入电子邮件通知中的其他内容. 您想要更改 ...

  8. (原创)如何在spannableString中使用自定义字体

    最近在做车联网的产品,主打的是语音交互和导航功能,UI给的导航界面可真是够酷炫的.但麻烦的事情也来了,里面的一句话居然用到了三种字体.界面如图所示: 从图中可以看出 500m左前方行驶 居然使用了三种 ...

  9. Knative 实战:如何在 Knative 中配置自定义域名及路由规则

    作者 | 元毅 阿里云智能事业群高级开发工程师 当前 Knative 中默认支持是基于域名的转发,可以通过域名模板配置后缀,但目前对于用户来说并不能指定全域名设置.另外一个问题就是基于 Path 和 ...

随机推荐

  1. Web项目中手机注册短信验证码实现的全流程及代码

    最近在做只能净化器的后台用户管理系统,需要使用手机号进行注册,找了许久才大致了解了手机验证码实现流程,今天在此和大家分享一下. 我们使用的是榛子云短信平台, 官网地址:http://smsow.zhe ...

  2. SpringBoot注解大全 转

    2019年3月17日22:30:10 一.注解(annotations)列表 @SpringBootApplication:包含了@ComponentScan.@Configuration和@Enab ...

  3. Enable Coded UI Testing of Your Controls

    http://msdn.microsoft.com/en-us/library/hh552522.aspx AccessibleObject Class http://msdn.microsoft.c ...

  4. IIC稳定性.VBS

    Sub Main Dim cnt Dim delay Dim time Dim atttime atttime = 20 delay = 3000 time = 50 crt.screen.Send ...

  5. [dev] 中文版内核代码编码规范

    我是我最喜欢的编码规范.并始终坚持. https://www.kernel.org/doc/html/v4.15/translations/zh_CN/coding-style.html

  6. Spring boot 国际化自动加载资源文件问题

    Spring boot 国际化自动加载资源文件问题 最近在做基于Spring boot配置的项目.中间遇到一个国际化资源加载的问题,正常来说只要在application.properties文件中定义 ...

  7. ROS串口通信

    身处机器人行业,不想一直只做低端的单片机控制,老是待在舒适区,所以一直都想学一下ROS系统,但看了几个月资料后,感觉还是云里雾里,似懂非懂,感念似乎都很清楚,但要实际去做,却又感觉无从下手. 于是想先 ...

  8. SecureCRT连接linux步骤

    SecureCRT连接linux步骤  做个笔记,以免隔段时间后忘了 LINUX系统一般都是用来作服务器使用,而且都是通过命令行来操作,为了操作方便我们都会使用第三方软件来远程操作.CRT就是比较常用 ...

  9. gdb调试原理及qemu中的gdbserver

    (一)gdb调试原理 此部分转自:https://blog.csdn.net/u012658346/article/details/51159971     https://www.cnblogs.c ...

  10. 关于windows 7 安装Django和基本使用命令

    一.安装 在安装前需注意Django 1.6以前的版本不支持python 3.×以上的版本. Django 2.×支持python 3.6 安装方法:打开cmd->输入pip install - ...