Qt 学习 第1节 自定义数据类型应用

来源:互联网 发布:windows loader cw 编辑:程序博客网 时间:2024/06/05 17:21
一、使用QVariant转换
SaveDBDataType saveDBDataType;
QVariant data;
data.setValue(saveDBDataType);设置数据
SaveDBDataType q1 = data.value<SaveDBDataType>();读取数据
二、注册数据类型
qRegisterMetaType
主要是在定义信号槽的时候,传递的参数类型不一定是QT所识别的,QT不识别的就要先注册以下,让QT能够认识,就是用qRegisterMetaType注册
qRegisterMetaType<enum dataType>("enum dataType");
qRegisterMetaType<deviceIdType>("deviceIdType");
qRegisterMetaType<electricCableMetaData>("electricCableMetaData");
qRegisterMetaType<uint8_t>("uint8_t");
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qRegisterMetaType<device_info>("device_info&");
    qRegisterMetaType<QList<device_info>>("QList<device_info>&");

MainWindoww;

………………………………

基本理解
  • Q_DECLARE_METATYPE
  • 如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。
  • 该类型必须有公有的 构造、析构、复制构造 函数
  • qRegisterMetaType 必须使用该函数的两种情况
  • 如果非QMetaType内置类型要在 Qt 的属性系统中使用
  • 如果非QMetaType内置类型要在 queued 信号与槽 中使用
二者关系
二者的代码:
  • Q_DECLARE_METATYPE 展开后是一个特化后的类 QMetaTypeId<TYPE>
  • qRegisterMetaType 将某类型注册中 MetaType 系统中
二者的联系:
  • QMetaTypeId<TYPE>的类中的成员包含对qRegisterMetaType的调用
  • 我们知道类中的成员函数并不一定会被调用(即,该宏并不确保类型被注册到MetaType)。
  • 通过qRegisterMetaType可以确保类型被注册
两个qRegisterMetaType 的联系
  • 无参的qRegisterMetaType函数会通过该成员调用带参数的qRegisterMetaType()
这两个东西真难理清,不妨看看源码吧。
Q_DECLARE_METATYPE
代码来源:src/corelib/kernel/qmetatype.h
#define Q_DECLARE_METATYPE(TYPE) \
QT_BEGIN_NAMESPACE \
template <> \
struct QMetaTypeId< TYPE > \
{ \
enum { Defined = 1 }; \
static int qt_metatype_id() \
{ \
static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (!metatype_id) \
metatype_id = qRegisterMetaType< TYPE >(#TYPE); \
return metatype_id; \
} \
}; \
QT_END_NAMESPACE

  • 宏展开是一个在Qt的命名空间中的一个类模板的特化 QMetaTypeId<TYPE>
  • 该类含一个enum和一个返回!QMetaType的id的成员函数
qRegisterMetaType(const char *typeName)
代码来源:src/corelib/kernel/qmetatype.h
template <typenameT>
intqRegisterMetaType(constchar *typeName)
{
typedefvoid*(*ConstructPtr)(constT*);
ConstructPtrcptr =qMetaTypeConstructHelper<T>;
typedefvoid(*DeletePtr)(T*);
DeletePtrdptr =qMetaTypeDeleteHelper<T>;

returnQMetaType::registerType(typeName,reinterpret_cast<QMetaType::Destructor>(dptr),
reinterpret_cast<QMetaType::Constructor>(cptr));
}
  • 该函数的核心就是调用了registerType 函数
  • 两个Helper模板函数分别对构造和析构函数进行封装
registerType
代码来源:src/corelib/kernel/qmetatype.cpp
int QMetaType::registerType(const char *typeName, Destructor destructor, Constructor constructor)
函数功能:
  • 根据类型名查找其MetaType类型,如果已存在,则直接返回;否则创建后返回。
  • 创建一个 !QCustomTypeInfo 对象
  • 该对象包含要类型的构造、析构信息,已经规范化后的类型名
  • 该对象存入一个全局的!QVector中
qRegisterMetaType()
看manual,可以知道,qRegisterMetaType 还有一个无参的重载函数。
template <typenameT>
inlineintqRegisterMetaType()
{
returnqMetaTypeId(static_cast<T *>(0));
}
  • 函数看起来和带参数的那个似乎区别很大(难道不是么?)。
  • 手册中告诉我们,执行这个的时候,模板参数T必须用 Q_DECLARE_METATYPE() 声明过
  • 能猜到原因吗?注意看前面 Q_DECLARE_METATYPE() 代码,
  • 对了。类中的成员函数qt_metatype_id中包含对qRegisterMetaType(typeName)的调用
  • 这儿就是辗转调用了这个带参数的qRegisterMetaType函数 
unregisterType(const char *typeName)
函数的作用是取消自己先前注册的某个metatype类型。
前面提到注册信息在一个全局的 QVector<QCustomTypeInfo>中,当取消注册的时候是怎么样的呢?直接删除Vector中相应的项么?源码告诉我们,不是的。
实际是查找到相应的项,清空该项的内容。
for (intv =0;v <ct->count(); ++v)
 {
 if (ct->at(v).typeName == typeName)
 {
 QCustomTypeInfo &inf = (*ct)[v];
 inf.typeName.clear();
 inf.constr = 0;
 inf.destr = 0;
 inf.alias = -1;
 }
}



创建一个自定义类型
在开始之间, 需要确认我们创建的这个自定义类型符合所有QMetaType提供的强制要求. 换句话说, 它必须提供:
  • 一个公共的默认构造函数
  • 一个公共的拷贝构造函数, 和
  • 一个公共的析构函数
下列的 "Message" 类定义包含这些成员:
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;};
这个类同时还提供了一般用途的构造函数和另两个公共用于获取私有数据的成员函数.
将类型声明为QMetaType
"Message" 类仅仅需要一段合适的实现就可以使它成为可用的类. 尽管如此, 如果没有其他辅助信息, Qt类型系统还是无法知道如何保持,读取和序列化此类的实例. 比如, 我们无法将 "Message" 的值保存到 QVariant 中.
在Qt中负责自定义类型的类似 QMetaType. 为了让这个类认识某个类型, 在定义这个类的头文件中使用宏 Q_DECLARE_METATYPE():
Q_DECLARE_METATYPE(Message); //如果不成功则在定义全局变量的地方声明,声明的是类也同样可以在代码中使用其指针
现在可以将 "Message" 的值保存于 QVariant 对象中并在以后读取这些值. 完整代码可参见 自定义类型的例子 中的示范代码.
宏 Q_DECLARE_METATYPE() 同时也使得这些值可能被用作信号的参数, 但是 "仅限于直接信号-槽连接". 为了能在信号-槽机制中普遍使用自定义类型, 我们需要做一些另外的工作.
创建和销毁自定义对象
尽管上一节中的声明使类型可以在直接信号-槽连接中使用, 但是无法用于队列的信号-槽连接中, 比如在不同线程中的对象之间所建立的连接. 这是因为元对象系统在运行时不知道如何处理自定义类型对象的创建和销毁操作.
为了可以在运行时创建对象, 需要调用模板函数 qRegisterMetaType() 在元对象系统中注册此类型. 那么只要你在建立使用此类型的第一次连接前调用注册函数, 该类型可被用于队列的信号槽连接.
队列的自定义类型例子 中声明了一个在 "main.cpp" 文件中注册的 "Block" 类:
int main(int argc, char*argv[]){QApplication app(argc, argv);...qRegisterMetaType<Block>();...return app.exec();}
在文件 "window.cpp" 中, 这个类型后来被用于信号槽连接:
Window::Window(){thread=new RenderThread();...connect(thread, SIGNAL(sendBlock(Block)),this, SLOT(addBlock(Block)));...setWindowTitle(tr("Queued Custom Type"));}
如果一个没有注册的类型被用于队列连接中, 在控制台中会输出一条警告信息; 例如:
QObject::connect: Cannot queue arguments of type 'Block'(Make sure 'Block' is registeredusing qRegisterMetaType().)

原创粉丝点击