1.创建一个自定义类型
Qt框架下开发时,创建一个自定义类型,需要确保创建的这个自定义类型符合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;
};
Q_DECLARE_METATYPE(Message);
这个类还提供了一个带参数的构造函数和用于访问两个私有成员的公有函数。
2.自定义类型的声明
对于Message
类,仅需要一个简单的实现,即可以使用,但是在Qt框架下,若没有其它的辅助信息,系统将无法理解这个类的存储方式、检索方式以及序列化方式。比如,我们无法将该类的值保存到QVariant
中。因此,我们需要对该类进行声明。
Qt中,负责自定义类型的类是QMetaType
,为了让这个类识别自定义的数据类型,需要在定义Message
类的头文件中使用Q_DECLARE_METATYPE()
宏声明。
Q_DECLARE_METATYPE(Message);
经过声明后,便可以将Message
类的值保存到QVariant
对象中,以便在项目的其它地方读取。用Q_DECLARE_METATYPE()
声明的自定义类型, 其对应的值可用作信号SIGNAL()
的参数,但是仅限于DirectConnection
信号槽连接方式。为了能满足全部的信号槽连接方式,我们还需要做一些其它的处理。
3.自定义对象的创建与销毁
虽然上面声明的自定义类型,可以在DirectConnection
信号槽连接中使用,但是无法用于QueuedConnection
信号连接中,比如,在不同线程间建立的连接。这是因为元对象系统不知道如何在运行时处理自定义类型对象的创建和销毁操作。
为了可以在运行时创建对象、需要调用qRegisterMetaType()
模板函数在元对象系统中注册此类型。只要在使用此类型第一次建立连接前调用注册函数,该类型即可被用于QueuedConnection
信号槽连接。
下面的示例中,在main.cpp
中注册了一个Message
类,而后在Information
类中绑定信号。
//main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc,argv);
//其它项目的代码
qRegisterMetaType<Message>();
//其它项目代码
return a.exec();
}
//下面是Information类的相关调用
Information::Information()
{
informatin_thread = new QThread();
connect(information_thread, SIGNAL(sendMessage(Message)), this, SLOT(receiveMessage(Message)));
//项目的其它代码
}
若类型未经注册而直接绑定在QueuedConnection
信号槽中,则会报如下错误:
QObject::connect: Cannot queue arguments of type ‘Block’ (Make sure ‘Block’ is registered using qRegisterMetaType().)
使用Q_DECLARE_METATYPE()声明了自定义数据类型之后就能在QVariant中使用了
struct Player
{
...
};
Q_DECLARE_METATYPE(Player);
Player player;
object->setProperty("property", QVariant::fromValue(player));
为了更方便一点,你可以在自定义类型中定义一个QVariant() 类型转换符
ruct Player
{
...
operator QVariant() const
{
return QVariant::fromValue(*this);
}
};
Player player;
object->setProperty("property", player);
重载 <<
QDebug
line QDebug operator<<(QDebug debug, const Player& player)
{
debug.nospace() << "Player("
<< player.number << ","
<< player.firstName << ","
<< player.lastName << ")";
return debug.space();
}
QDataStream
inline QDataStream& operator<<(QDataStream& out, const Player& player)
{
out << player.number;
out << player.firstName;
out << player.lastName;
return out;
}
inline QDataStream& operator>>(QDataStream& in, Player& player)
{
in >> player.number;
in >> player.firstName;
in >> player.lastName;
return in;
}
QSettings
QSettings 用QVariant保存键值,用QDataStream序列化自定义数据。(参考后面的variantToString函数)
为了能在QSettings中使用自定义数据类型,需要让Qt的元系统知道有此类型,就像上面介绍QVariant部分一样,另外还要提供相应的QDataStream操作符,还必须注册这个流操作符:
qRegisterMetaTypeStreamOperators<Player>("Player");
如此处理之后我们就可以像下面这样使用了:
QSettings settings;
Player player;
settings.setValue("key", player);
QSettings settings;
Player player = value("key").value<Player>();