ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

QVariant类和属性系统

2021-09-16 16:02:15  阅读:137  来源: 互联网

标签:QVariant 函数 int 系统 类型 属性 构造函数


一、QVariant 类

  1. 使用QObject::property 函数可读取属性的值,使用 QObject::setProperty 函数可以设置属性的值,但是属性有很多种类型,怎样使用 property 函数返回的属性值具有正确的类型呢?为解决这个问题,使用了一个
    QVariant 来描述类型.
  2. QVariant 类用于封装数据成员的类型及取值等信息,该类类似于 C++共用体 union,一个QVariant 对象,一次只能保存一个单一类型的值。该类封装了 Qt 中常用的类型,对于QVariant 不支持的类型 ( 比如用户自定义类型 ) ,则需要
    使 用Q_DECLARE_METATYPE( Type )宏进行注册
  3. QVariant 拥有常用类型的单形参构造函数,因此可把这些常用类型转换为 QVariant 类型,同时 QVariant 还重载了赋值运算符,因此可把常用类型的值直接赋给 QVariant 对象。注:由 C++语法可知,单形参构造函数可进行类型转换
  4. 使用 QVariant 构造函数和赋值运算符,见下面示例;注意:QVariant 没有 char 类型的构造函数,若使用 char 值会被转换为对应的 int 型。 QVariant v(1); //调用 QVariant(int )构造函数创建一个 QVariant
    类型的对象,并把数值 1 保存到 v 中。v=2.2; //调用 QVariant 的赋值运算符,把值 2 保存在 v 之中,因为
    QVariant 是类似于共用体的类,因此同一时间只会保存一个值。
  5. 获取 QVariant 对象存储的值的类型
  • Type type() const :获取 QVariant 对象当前存储值的类型,类型以枚举 QMetaType::Type 的形式表示

  • const char* typeName() const;:以字符串的形式返回 QVariant 对象存储的值的类型若是无效类型则返回 0

  • const char* typeToName(int t):
    把以枚举类型 QMetaType::Type 表示的类型以字符串的形式返回。若枚举值为QMetaType::UnknownType 或不存在,则返回一个空指针。
    QVariant v(1);
    cout<<v.typeName()<<endl; //输出 int
    cout<<v.typeToName(v.type())<<endl; //输出 int

  • 获取和设置 QVariant 对象存储的值
    1. void setValue(const T& v)
    把一个值的副本存储到 QVariant 对象中,若类型 T 是 QVariant 不支持的类型,则使用QMetaType 来存储该值,若 QMetaType 也不能处理,则发生编译错误。注:若是用户自定义类型则需要使用宏 Q_DECLARE_METATYPE(…)进行注册
    2 T value() const
    把存储的值转换为类型 T 并返回转换后的值,存储值本身不会被改变。若 T 是QVariant 支持的类型,则该函数与 toInt、toString 等函数功能完全相同。注:使用该函数时需要使用尖括号指定 T
    的类型,比如 xx.value();
    3 T toT()
    其中 T 是某一类型,比如若 T 是 int,则该函数形式就为 int toInt()。该函数用于把存储的值转换为类型T并返回转换后的值,存储值本身不会被改变。其中比较常用的是 toString函数,该函数可把存储的值转换为 QString 形式,这样便可以字符串的形式输出存储的值。
    注意:没有与自定义类型相对应的 toT函数,比如 class C{};则没有 toC 函数,要把存储的值转换为自定义类型,需要使用 value 函数,且还需对自定义类型注册。
    注意:使用 QVariant 的默认构造函数,将创建一个无效的 QVariant 对象(或空的 QVariant 对象),可通过 isNull()成员函数进行判断。

代码示例:

class C{}; //自定义类型

int main(int argc, char *argv[])
{
	QVariant v('a'); /*QVariant 没有专门的 char 构造函数,此处的字符 a 会被转换为 int 型,因此 v中存储的是数值 97,而不是字符 a 。*/
	cout<<v.value<int>()<<endl; //输出 97
	cout<<v.value<char>()<<endl; //输出 a,将 97 转换为 char 型,并输出转换后的值。
	cout<<v.toChar().toLatin1()<<endl; /*输出 a,原因同上,注意 toChar 返回的类型是 QChar 而不是 char。*/
	cout<<v.toString().toStdString()<<endl; /*输出 97,把存储在 v 中的值转换为 QString,然后以字符串形式输出。*/
	cout<<v.typeName()<<endl; //输出 int,可见存储在 v 中的值的类型为 int
	cout<<v.typeToName(v.type())<<endl; /*输出 int,其中 type 返回存储值的枚举形式表示的类型,而typeToName 则以字符串形式显示该枚举值所表示的类型。*/
	
	char c='b';
	v.setValue(c);
	cout<<v.toString().toStdString()<<endl; //输出 b
	cout<<v.typeName()<<endl; /*输出 char,若是使用 QVariant 构造函数和直接赋值 char 型字符,此处会输出 int,这是 setValue 与他们的区别。*/
	C mc; //自定义类型 C 的对象 mc
	//QVariant v1(mc); //错误,没有相应的构造函数。
	QVariant v2;
	//v2=mc; //错误,没有与类型 C 匹配的赋值运算符函数。
	//v2.setValue(mc); //错误,自定义类型 C 未使用宏 Q_DECLARE_METATYPE 声明。
	return 0; 
 }

二、使用 QObject 类中的成员函数存取属性值与动态属性

1. 注册自定义类型与QMetaType类
1.1 QMetaType 类用于管理元对象系统中命名的类型,该类用于帮助 QVariant 中的类型以及队列中信号和槽的连接。它将类型名称与类型关联,以便在运行时动态创建和销毁该名称。
1.2 QMetaType::Type 枚举类型定义了 QMetaType 支持的类型。其原型为enum Type{void, Bool,Int……UnknowType}
1.3 对于 QVariant 类和属性中使用的自定义类型,都需要进行注册,然后才能使用。使 用宏 Q_DECLARE_METATYPE()声明新类型,使它们可供 QVariant 和其他基于模板的函数使用。调用
qRegisterMetaType()将类型提供给非模板函数
1.4 使用 Q_DECLARE_METATYPE( Type )宏声明类型
1.4.1 使用该宏声明类型之后,会使所有基于模板的函数都知道该类型
1.4.2 使用该宏的类需要具有 public 默认构造函数、public 析构函数和 public 复制构造函数
1.4.3 使用该宏时,被声明的类型 Type 需要是完全定义的类型,因此,该宏通常位于类或结构的声明之后
1.4.4 对于指针类型,需要使用 Q_DECLARE_OPAQUE_POINTER(T)宏进行声明
1.4.5 对于 QVariant 类,只需使用该宏声明类型之后便可使用该类型
1.4.6 若需要在队列中的信号和槽连接中,或 QObject 的属性系统中使用该类型,则还必须调用 qRegsiterMetaType 函数注册该类型,因为这些情况是动态运行的
1.4.7 类型自动注册 指向从 QObject 派生的类的指针类型、使用 Q_ENUM 或 Q_FLAG 注册的枚举、具有 Q_GADGET 宏的类
1.5 使用 int qRegisterMetaType()函数注册类型 使用该函数时需要使用尖括号指定 T 的类型,比如 qRegisterMetaType() 该函数返回 QMetaType 使用的内部 ID 类型 T 必须使用
Q_DECLARE_METATYPE( Type )宏声明 类型注册后,就可以在运行时运态创建和销毁该类型的对象了
被注册的类或结构需要具有 public 默认构造函数、public 析构函数和 public 复制构造函数
2、QVariant
QObject::property(const char* name) const; 获取属性名称为 name 的值,该值以
QVariant 对象的形式返回。若属性 name 不存在,则返回的 QVariant 对象是无效的。
3、 setProperty
函数及动态属性 bool QObject::setProperty(const char* name, const
QVariant & v); 把属性 name 设置为值 v 若属性使用 Q_PROPERTY 进行了声明,且值 v 与属性name 的类型兼容,则把值 v存储在属性 name 中,并返回 true,若值与属性的类型不兼容则属性不会更改,并返回 false。
4、动态属性
若属性 name 未使用 Q_PROPERTY 进行声明,则把该属性和值作为新属性添加到对象中,并返回false,这就是动态属性
动态属性仍可使用 property 进行查询,还可设置一个无效的 QVariant 对象来删除动态属性。
动态属性是基于某个类的实例的,也就是说动态属性是添加到某个类的对象中的,而不是添加到 QMetaObject 中的,这意味着,无法使用QMetaObject 的成员函数获取动态属性的信息。更改动态属性的值,会发送QDynamicPropertyChangeEvent 到该对象。

代码示例

class A
{
public:
	int i;
};
class B
{
public:
	int i;
};
class D
{
	public:D(int)
	{
	}
};//该类无 public 默认构造函数

class E{ };

//声明类型
Q_DECLARE_METATYPE(A)
Q_DECLARE_METATYPE(B)
//Q_DECLARE_METATYPE(D) //错误,类 D 没有公有的默认构造函数
//Q_DECLARE_METATYPE(E) //错误,因为父类 QObject 的复制构造函数、赋值运算符等是私有的。
int main(int argc, char *argv[])
{
	//注册类型
	qRegisterMetaType<B>();
	//qRegisterMetaType<E>(); //错误,类型 E 未使用宏 Q_DECLARE_METATYPE(T)声明

	A ma; 
	ma.i=1;

	B mb; 
	mb.i=2;

	//QVariant v1(ma); //错误,没有相应的构造函数。

	QVariant v;
	v.setValue(ma); //将对象 ma 存储在 v 之中
	cout<<v.value<A>().i<<endl; //输出 1。
	cout<<v.typeName()<<endl; //输出 A
	
	cout<<v.toString().toStdString()<<endl; //输出一个空字符,因为 ma 是一个对象,不是一个值。
	
	//自定义类型需要使用 userType 才能返回正确的类型 ID。
	cout<<v.typeToName(v.userType())<<endl; //输出 A
	cout<<v.typeToName(v.type())<<endl; //不一定输出 A。

	A ma1;
	ma1=v.value<A>(); //把存储在 v 之中的对象 ma 赋值给 ma1
	cout<<ma1.i<<endl; //输出 1,可见赋值成功。
	
	B mb1;
	//mb1=v.value<A>(); //错误,类型不相同。
	
	mb1=v.value<B>(); //正确,由类型 A 转换到类型 B 失败,此时 value 会返回一个默认构造的值。
	cout<<mb1.i<<endl; //输出 0。
	return 0; 
}			

动态属性代码示例

class B{public:int i;};
class C{public:int i;};
class D{public:int i;};

Q_DECLARE_METATYPE(B)
Q_DECLARE_METATYPE(C)

class Z:public QObject
{ 
	Q_OBJECT
public: 
	Z(){}
	
	Q_PROPERTY(B b READ fb WRITE gb)
	Q_PROPERTY(C c READ fc WRITE gc)
	Q_PROPERTY(D d READ fd WRITE gd)
	
	B fb()
	{
		return m_mb;
	} 
	void gb(B x)
	{
		m_mb=x;
	}
	
	C fc()
	{
		return m_mc;
	}
	void gc(C x)
	{
		m_mc=x;
	}
	
	D fd()
	{
		return m_md;
	}
	void gd(D x)
	{
		m_md=x;
	}
	
	B m_mb;
	C m_mc; 
	D m_md; 
};


int main(int argc, char *argv[])
{
	//注册类型
	qRegisterMetaType<B>();
	qRegisterMetaType<C>();
	B mb; 
	C mc; 
	D md; 
	Z mz;
	mb.i=2; 
	mc.i=3; 
	md.i=4;
	
	mz.gb(mb); 
	mz.gc(mc); 
	mz.gd(md);
	
	//使用 porperty 和 setProperty 存取属性值。
	
	//mz.property("d"); //错误,不能使用 property 函数访问属性 d,因为属性 d 的类型 D 未注册。
	
	mz.property("MMM"); /*这是正确的,因为属性 MMM 不存在,所以,返回的是一个空的 QVariant 对象,可见,属性不存在与属性的类型未注册是不同的。*/
	
	cout<<mz.fd().i<<endl; /*输出 4。虽然不能使用 property 函数访问属性 d,但仍可使用存取函数访问该属性的值。*/
	
	QVariant v; B mb1;
	mb1.i=6; v.setValue(mb1);
	//mz.setProperty("b",mb1); //错误,第二个参数的类型不匹配。
	mz.setProperty("b",v); //正确设置属性 b 的值的方法,把属性 b 的值设置为 v 中存储的值 mb1。
	mz.setProperty("c",v); /*正确,但是属性 c 的类型与 v 中存储的值的类型不兼容,因此属性 c 不会被更改*/
	
	mz.setProperty("c",7); //原因同上。
	cout<<mz.property("b").typeName()<<endl; //输出 B,输出属性 b 的类型
	cout<<mz.property("c").typeName()<<endl; //输出 C,输出属性 c 的类型
	cout<<mz.property("b").value<B>().i<<endl; //输出 6。输出的是 mb1.i 的值。
	cout<<mz.property("c").value<C>().i<<endl; //输出 3,属性 c 的值并未被更改。
	
	//动态属性
	mc.i=7; v.setValue(mc);
	// mz.setProperty("w",mc); //错误,第二个参数的类型不匹配。
	mz.setProperty("x",v); //动态属性,新增加属性 x,并设置其值为 v 中存储的值(即 mc)
	cout<<mz.property("x").typeName()<<endl; //输出 C,即动态属性 x 的类型。
	cout<<mz.property("x").value<C>().i<<endl; //输出 7。 Z mz1;
	//cout<<mz1.property("x").typeName()<<endl; //错误,动态属性 x 是基于对象 mz 的。
	cout<<mz1.property("b").typeName()<<endl; //输出 B,属性 b 不是动态属性。
	return 0; 
}

标签:QVariant,函数,int,系统,类型,属性,构造函数
来源: https://blog.csdn.net/Stone_OverLooking/article/details/120329863

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有