原创文章,转载请注明出处,谢谢!

作者:清林,博客名:飞空静渡

QVariant可以表示QT中的大部分类型,它和pascal中的variant类型或c中的void类型有点相似,不过它的使用和c中的union类似,其实现也是用union,在qvariant.h头文件中,我们可以看到这样定义:

view plaincopy to clipboardprint?

class Q_CORE_EXPORT QVariant

{

public:

enum Type {

Invalid = 0,

Bool = 1,

Int = 2,

UInt = 3,

LongLong = 4,

ULongLong = 5,

......

UserType = 127,

#ifdef QT3_SUPPORT

IconSet = Icon,

CString = ByteArray,

PointArray = Polygon,

#endif

LastType = 0xffffffff // need this so that gcc >= 3.4 allocates 32 bits for Type

};

inline QVariant();

...............

}

class Q_CORE_EXPORT QVariant

{

public:

enum Type {

Invalid = 0,

Bool = 1,

Int = 2,

UInt = 3,

LongLong = 4,

ULongLong = 5,

......

UserType = 127,

#ifdef QT3_SUPPORT

IconSet = Icon,

CString = ByteArray,

PointArray = Polygon,

#endif

LastType = 0xffffffff // need this so that gcc >= 3.4 allocates 32 bits for Type

};

inline QVariant();

...............

}

我们可以用type()成员函数来返回它的类型:Type   QVariant::type () const 。

为了取得它的值,我们可以用类似的toT()这样的函数来取得,如toInt()、toString()等。

基本的使用如下:

view plaincopy to clipboardprint?

QDataStream out(...);

QVariant v(123);                // The variant now contains an int

int x = v.toInt();              // x = 123

out << v;                       // Writes a type tag and an int to out

v = QVariant("hello");          // The variant now contains a QByteArray

v = QVariant(tr("hello"));      // The variant now contains a QString

int y = v.toInt();              // y = 0 since v cannot be converted to an int

QString s = v.toString();       // s = tr("hello")  (see QObject::tr())

out << v;                       // Writes a type tag and a QString to out

...

QDataStream in(...);            // (opening the previously written stream)

in >> v;                        // Reads an Int variant

int z = v.toInt();              // z = 123

qDebug("Type is %s",            // prints "Type is int"

v.typeName());

v = v.toInt() + 100;            // The variant now hold the value 223

v = QVariant(QStringList());

QDataStream out(...);

QVariant v(123);                // The variant now contains an int

int x = v.toInt();              // x = 123

out << v;                       // Writes a type tag and an int to out

v = QVariant("hello");          // The variant now contains a QByteArray

v = QVariant(tr("hello"));      // The variant now contains a QString

int y = v.toInt();              // y = 0 since v cannot be converted to an int

QString s = v.toString();       // s = tr("hello")  (see QObject::tr())

out << v;                       // Writes a type tag and a QString to out

...

QDataStream in(...);            // (opening the previously written stream)

in >> v;                        // Reads an Int variant

int z = v.toInt();              // z = 123

qDebug("Type is %s",            // prints "Type is int"

v.typeName());

v = v.toInt() + 100;            // The variant now hold the value 223

v = QVariant(QStringList());

QViriant支持空值的使用,你可以定义一个空的QViriant,并在后面才给它赋值。

view plaincopy to clipboardprint?

QVariant x, y(QString()), z(QString(""));

x.convert(QVariant::Int);

// x.isNull() == true

// y.isNull() == true, z.isNull() == false

// y.isEmpty() == true, z.isEmpty() == true

QVariant x, y(QString()), z(QString(""));

x.convert(QVariant::Int);

// x.isNull() == true

// y.isNull() == true, z.isNull() == false

// y.isEmpty() == true, z.isEmpty() == true

QVariant是定义在QtCore库中的,前面说的qvariant.h就在QtCore目录下,因此它不提供类似于toInt()、toString()这样的函数去转化QtGui中的类型,如QColor、QImage和QPixmap等。但你可以使用 QVariant::value()或者 qVariantValue ()模板函数转化。

view plaincopy to clipboardprint?

QVariant variant;

...

QColor color = variant.value();

QVariant variant;

...

QColor color = variant.value();

相反的赋值就可以这样:

view plaincopy to clipboardprint?

QColor color = palette().background().color();

QVariant variant = color;

QColor color = palette().background().color();

QVariant variant = color;

你可以使用 canConvert() 确定是否可以转化。

我们在使用QT编程时,难免要定义自己需要的类型,但像QT自己的类型如QSzie、QString之类的,都是可以存储在QViriant中的,并且这些QT的类型是可以用在基于QObject类的类型属性中和基于信号和槽的发生机制中。

如果我们想要我们自己自定义的类型可以有QT自己类型的功能的话,我们就必须注册我们的类型到QT中,这样我们才可以在我们在信号和槽的通讯机制中使用我们的类型。

在我们想要把我们的类型注册到QT中,我们必须满足QMedaType类型的要求,这有三点是必须的要求(以后章节说的也要满足这三点要求)。

1、必须要有一个公有的构造函数。

2、必须要有一个公有的拷贝构造函数。

3、必须要有一个公有的虚构函数。

下面使用QT文档中的一个例子来说明:

第一步:我们首先可以定义一个满足上面三点要求的自定义的类。

view plaincopy to clipboardprint?

#ifndef MESSAGE_H

#define MESSAGE_H

#include #include #include class Message

{

public:

Message();

Message(const Message &other);

~Message();

Message(const QString &body, const QStringList &headers);

QString body() const;

QStringList headers() const;

private:

QString m_body;

QStringList m_headers;

};

Q_DECLARE_METATYPE(Message);

QDebug &operator

#ifndef MESSAGE_H

#define MESSAGE_H

#include #include #include class Message

{

public:

Message();

Message(const Message &other);

~Message();

Message(const QString &body, const QStringList &headers);

QString body() const;

QStringList headers() const;

private:

QString m_body;

QStringList m_headers;

};

Q_DECLARE_METATYPE(Message);

QDebug &operator

第二步:注册我们的类型

我们的自定义的类型,在QT中的QVariant中的,因为在QVariant中并不知道怎么存储和获取我们的类型。因此我们就必须使我们的类型成为和QString一样的通用类型,这就需要QT中的QMetaType来完成了。我们需要调用 Q_DECLARE_METATYPE这个宏来完成。

Q_DECLARE_METATYPE(Message);

这就可以使我们的Message可以存储在QVariant中了。Q_DECLARE_METATYPE可以使我们的类型使用在信号的参数中,但是直接使用在信号-槽的通讯中的,但不可以用在基于消息队列中的信号-槽的机制中的,例如我们在线程中的通讯,上面的那种定义就做不到了。这是因为,上面的那种定义是用宏来静态定义的,在QT中的元对象系统中并不知道在运行时怎么创建和销毁我们的自定义的对象。我将在后一章讲解我们自定义的类型完全用在信号-槽的通讯机制中的做法。

这是Message的实现代码:

view plaincopy to clipboardprint?

#include "message.h"

Message::Message()

{

}

Message::Message(const Message &other)

{

m_body = other.m_body;

m_headers = other.m_headers;

}

Message::~Message()

{

}

Message::Message(const QString &body, const QStringList &headers)

{

m_body = body;

m_headers = headers;

}

QDebug &operator

QStringList pieces = message.body().split("\r\n", QString::SkipEmptyParts);

if (pieces.isEmpty())

dbg.nospace() << "Message()";

else if (pieces.size() == 1)

dbg.nospace() << "Message(" << pieces.first() << ")";

else

dbg.nospace() << "Message(" << pieces.first() << " ...)";

return dbg.maybeSpace();

}

QString Message::body() const

{

return m_body;

}

QStringList Message::headers() const

{

return m_headers;

}

#include "message.h"

Message::Message()

{

}

Message::Message(const Message &other)

{

m_body = other.m_body;

m_headers = other.m_headers;

}

Message::~Message()

{

}

Message::Message(const QString &body, const QStringList &headers)

{

m_body = body;

m_headers = headers;

}

QDebug &operator

QStringList pieces = message.body().split("\r\n", QString::SkipEmptyParts);

if (pieces.isEmpty())

dbg.nospace() << "Message()";

else if (pieces.size() == 1)

dbg.nospace() << "Message(" << pieces.first() << ")";

else

dbg.nospace() << "Message(" << pieces.first() << " ...)";

return dbg.maybeSpace();

}

QString Message::body() const

{

return m_body;

}

QStringList Message::headers() const

{

return m_headers;

}

最后看下我们的main函数。

view plaincopy to clipboardprint?

#include #include #include "message.h"

int main(int argc, char *argv[])

{

QCoreApplication app(argc, argv);

QStringList headers;

headers << "Subject: Hello World"

<< "From: ";

QString body = "This is a test.\r\n";

Message message(body, headers);

qDebug() << "Original:" << message;

QVariant stored;

stored.setValue(message);

qDebug() << "Stored:" << stored;

Message retrieved = stored.value();

qDebug() << "Retrieved:" << retrieved;

retrieved = qVariantValue(stored);

qDebug() << "Retrieved:" << retrieved;

return 0;

}

#include #include #include "message.h"

int main(int argc, char *argv[])

{

QCoreApplication app(argc, argv);

QStringList headers;

headers << "Subject: Hello World"

<< "From: ";

QString body = "This is a test.\r\n";

Message message(body, headers);

qDebug() << "Original:" << message;

QVariant stored;

stored.setValue(message);

qDebug() << "Stored:" << stored;

Message retrieved = stored.value();

qDebug() << "Retrieved:" << retrieved;

retrieved = qVariantValue(stored);

qDebug() << "Retrieved:" << retrieved;

return 0;

}

我们在注册我们的Message类后,我们就可以使我们的Message类对象存储在我们的QVariant的对象中了。在main函数中,我们可以看到

QVariant stored;

stored.setValue(message);

而从QVariant中获得我们的对象可以这样:

Message retrieved = stored.value();

pro文件:

HEADERS   = message.h

SOURCES   = main.cpp \

message.cpp

下一章讲解用这个例子用在信号-槽的通讯机制中,后一章将讲解自定义类型在消息队列的信号-槽的机制,即在多线程通讯中使自定义类型在信号-槽中的运用。

昨天调试项目时,突然发现如下消息:

QObject::connect: Cannot queue arguments of type 'ERROR_LEVEL'

(Make sure 'ERROR_LEVEL' is registered using qRegisterMetaType().)

其中ERROR_LEVEL只是我定义的枚举类型即enum ERROR_LEVEL。然后在Qt的信号-槽函数的参数中使用了这个枚举型,在发送信号时就出现了上述警告。上面警告的大概意思是信号队列中无法使用ERROR_LEVEL类型,要使用qRegisterMetaType()注册该类型后方可使用。

通常使用的connect,实际上最后一个参数使用的是Qt::AutoConnection类型:

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )

Qt支持6种连接方式,其中3中最主要:

Qt::DirectConnection(直连方式)

当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。(信号与槽函数关系类似于函数调用,同步执行)

Qt::QueuedConnection(排队方式)

当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信,异步执行)

Qt::AutoConnection(自动方式)

Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。

我的项目中的确跨线程使用了ERROR_LEVEL为参数类型的信号,因此使用的应当是排队方式的信号-槽机制,出现“队列中无法使用ERROR_LEVEL类型”的警告信息就可以理解了。放狗搜了一圈,有篇文章提供了个这样的解决方案:

connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),

this,SLOT(sendRes(QUuid,QByteArray,bool)));

改为:

connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),

this,SLOT(sendRes(QUuid,QByteArray,bool)), Qt::DirectConnection);

这样做的确能使警告信息消失,因为Qt官方文档写了:

With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes.

即使用排队方式的信号-槽机制,Qt的元对象系统(meta-object system)必须知道信号传递的参数类型。这里手动改为直连方式,Qt的元对象系统就不必知道参数类型了,于是警告信息消失。但这样做是不安全的,见Qt官方文档:

Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.

因此,咱还是老老实实地用qRegisterMetaType()注册类型吧,见代码:

头文件:/*! \file errorlevel.h*/#ifndef ERRORLEVEL_H#define ERRORLEVEL_H/*! \enum ERROR_LEVEL* 错误等级的定义。通常用来在传递错误消息时标记错误的等级。*/enum ERROR_LEVEL{NORMAL, /*!< 普通错误,通常不需要处理,可以记录或者显示错误消息。*/CRITICAL /*!< 严重错误,需要紧急处理,如果没有妥善处理,可能导致后续操作失败。*/};#endif // ERRORLEVEL_H

cpp文件:/*! \file errorlevel.cpp*/#include "errorlevel.h"/*! 模板函数显式实例化,注册ERROR_LEVEL到元对象系统。这样才可以在信号-槽* 队列里使用ERROR_LEVEL类型参数。*/int i = qRegisterMetaType("ERROR_LEVEL");元类型注册方法受益于这篇文章。

linux创建自定义组件qt,QT中的元对象系统:创建自定义的QT类型相关推荐

  1. 基于Qt5.14.2和mingw的Qt源码学习(三) — 元对象系统简介及moc工具是如何保存类属性和方法的

    基于Qt5.14.2和mingw的Qt源码学习(三) - 元对象系统简介及moc工具是如何保存类属性和方法的 一.什么是元对象系统 1.元对象系统目的 2.实现元对象系统的关键 3.元对象系统的其他一 ...

  2. Qt工作笔记-Qt元对象系统解析【2合1】

    博文转载地址: https://blog.csdn.net/spwper/article/details/51332187 说Qt信号与槽是一个很好机制,不如说Qt的元对象系统很强大.这也是大家讲Qt ...

  3. Qt元对象系统:QMetaObject

    一.描述 此类包含有关Qt对象的元信息.Qt为应用程序中使用的每个 QObject 子类创建一个 QMetaObject 实例,该实例存储 QObject 子类的所有元信息. 二.静态成员函数 1.Q ...

  4. 【QT】QT元对象系统

    QT元对象系统(Meta-Object-System) 元对象系统 元对象系统是一个基于标准C++的扩展,为QT提供了信号与槽机制.实时类型信息.动态属性系统. 元对象系统的三个基本条件:类必须继承自 ...

  5. 1.QT元对象系统、信号槽概述、宏Q_OBJECT

    一.元对象系统(Meta-Object System) Qt添加C++原本不具备的元对象系统,元对象系统提供了信号槽机制,运行时类型信息和动态属性系统. 元对象系统基于三点: 1.元对象系统为以QOb ...

  6. Qt元对象系统(Meta-Object)(四)、Moc源代码分析

    目录 前言 打开源代码 源码追踪解析 总结 前言   前面讲了那么多,Qt的元对象系统是多么多么厉害,多么多么好,那么Moc是具体是怎么工作的,所谓源码面前,了无秘密,下面让我们一探究竟. 打开源代码 ...

  7. 微信小程序 - 进阶(自定义组件、promis化、mobx、分包、自定义tabBar)

    文章目录 一.自定义组件 1.创建组件 2.引用组件 3.组件和页面区别 4.组件样式 5.data.methods.properties 6.小程序的 data 和 properties 区别 7. ...

  8. qt中实现息屏开平mousepress_Qt元对象(Meta-Object)系统与反射

    前言 给部门内部培训Qt相关的一些注意点用的,我提取出来放到自己的博客上了.欢迎各位大佬吐嘈 原文链接 Qt元对象(Meta-Object)系统与反射 - C++ Programer​www.cryf ...

  9. Qt4_在Qt设计师中集成自定义窗口部件

    在Qt设计师中集成自定义窗口部件 在Qt设计师中使用自定义窗口部件之前,我们必须让Qt设计师先察觉到它们的存在.有两种方法可以完成这一任务:改进法(promotion)和插件法(pluigin). 改 ...

最新文章

  1. 2021年春季学期-信号与系统-第十次作业参考答案-第四小题
  2. Node.js 15 正式发布,14 将支持到 2023 年
  3. 解码resources时里面是空的_深度解码超实用的OpenStack Heat
  4. K8s普通用户配置权限解决User “system:anonymous“ cannot list nodes at the cluster scope
  5. 面向对象编程设计模式--简单工厂模式讲解(历史上最简单明白的例子)
  6. java符号%3e%3e是什么意思,终于找到了!有了它你就可以读懂字节码了!
  7. python基础编程题、积分面积_Python基础编程题100列目录
  8. redis 从节点如何选举从节点升级为主节点_Redis哨兵的配置和原理
  9. 1个报表工具,5大体系,60种可视化图表,靠这些打动领导就够了
  10. eclipse导入maven项目时报Could not calculate build plan: Plugin org.apache.maven.plugins:maven-resources...
  11. iOS 8.0正式公布啦
  12. Papervision3d QuadTree四叉树相机的示例
  13. HTML5 界面元素 Canvas 参考手册
  14. oracle导入dmp文件数据不全,Oracle dmp文件损坏恢复案例
  15. 常见出血性疾病的实验诊断题库【1】
  16. 吉利汽车科创板上市终止:2019年利润腰斩,还存在两大风险问题
  17. 北京博奥智源,浅谈术语管理服务器软件开发所需功能设计
  18. NoSQL学习笔记之MongoDB-01初识NoSQL
  19. 钽电容的选用和使用标准
  20. 适用于Windows/Mac的最好视频剪辑软件

热门文章

  1. @程序员,React 使用如何避坑?
  2. 罗永浩与锤子手机撇清关系;微软回应「高管传奇」经历;Rust 1.38 稳定版发布 | 极客头条...
  3. 程序员如何掌握 React 开发的黄金法则? | 技术头条
  4. “杀死” APP 的留白设计!
  5. 如何绘制高大上的词云图?
  6. 余承东回应“卸任”传闻:服务还未拼尽全力,岂敢先溜
  7. Java程序员春招三面蚂蚁金服,丹丹丹学妹哭着对我说:学长
  8. linux服务器分区方案
  9. 【Struts1.2总结系列】struts-config.xml配置
  10. python交互式帮助的进入、使用和退出_python退出交互式???