自定义类型的示例
自定义类型示例展示了如何将自定义类型集成到Qt的元对象系统中。
概述
Qt提供了一系列标准值类型,用于提供丰富而有意义的api。这些类型与元对象系统集成,使它们能够存储在QVariant对象中,在调试信息中写入,并在信号槽通信中在组件之间发送。
定制类型也可以与元对象系统集成,只要它们的编写遵循一些简单的指导原则。在这个示例中,我们介绍了一个简单的Message类,描述了如何使它与QVariant一起工作,并展示了如何扩展它以生成其自身的可打印表示,以便在调试输出时使用。
Message类定义
Message类是一个简单的值类,它包含两条信息(QString和QStringList),每一条信息都可以使用简单的getter函数读取:
class Message
{
public:
Message() = default;
~Message() = default;
Message(const Message &) = default;
Message &operator=(const Message &) = default;
Message(const QString &body, const QStringList &headers);
QString body() const;
QStringList headers() const;
private:
QString m_body;
QStringList m_headers;
};
如果要将类型集成到元对象系统中,则默认构造函数、复制构造函数和析构函数都是必需的,并且必须是公共的。除此之外,我们可以自由地实现任何需要的东西,以使类型做我们想做的事情,因此我们还包括一个构造函数,它允许我们设置类型的数据成员。
为了使类型能够与QVariant一起使用,我们使用Q_DECLARE_METATYPE()宏声明它:
Q_DECLARE_METATYPE(Message);
我们不需要为这个宏编写任何额外的代码。
为了让我们看到每个消息对象发送到调试输出流时的可读描述,我们定义了一个流操作符:
QDebug operator<<(QDebug dbg, const Message &message);
如果您需要在代码中插入跟踪语句以进行调试,则此功能非常有用。
Message类实现
流操作符的实现方式如下:
QDebug operator<<(QDebug dbg, const Message &message)
{
const QString body = message.body();
QVector<QStringRef> pieces = body.splitRef(QLatin1String("\r\n"), Qt::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();
}
在这里,我们希望根据消息体中存储的行数来表示每个值。我们将文本流化为传递给运算符的QDebug对象,并返回从其成员函数maybeSpace()中获得的QDebug对象;这在创建自定义Qt类型文档中有更详细的描述。
为了完整性,我们包括了getter函数的代码:
QString Message::body() const
{
return m_body;
}
QStringList Message::headers() const
{
return m_headers;
}
类型被完全定义、实现并与元对象系统集成后,我们现在就可以使用它了。
使用Message类
在示例的main()函数中,我们展示了如何将消息对象发送到调试流,从而将其打印到控制台:
Message message(body, headers);
qDebug() << "Original:" << message;
您可以用与使用标准Qt值类型完全相同的方式对QVariant使用该类型。下面是如何使用QVariant::setValue()函数存储一个值:
QVariant stored;
stored.setValue(message);
另外,如果使用不支持成员模板函数的编译器,也可以使用QVariant::fromValue()函数。
值可以使用QVariant::value()成员模板函数检索:
Message retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;
retrieved = stored.value<Message>();
qDebug() << "Retrieved:" << retrieved;
进一步阅读
自定义消息类型还可以与直接信号槽连接一起使用。
若要注册一个自定义类型以与排队信号和槽(例如在跨线程通信中使用的那些)一起使用,请参阅排队自定义类型示例。
关于在Qt中使用自定义类型的更多信息可以在创建自定义Qt类型文档中找到。
完整代码
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class Message
{
public:
Message() = default;
~Message() = default;
Message(const Message &) = default;
Message &operator=(const Message &) = default;
Message(const QString &body, const QStringList &headers)
: m_body(body), m_headers(headers) { }
QString body() const
{ return m_body; }
QStringList headers() const
{ return m_headers; }
private:
QString m_body;
QStringList m_headers;
};
Q_DECLARE_METATYPE(Message);
QDebug operator<<(QDebug dbg, const Message &message)
{
const QString body = message.body();
QVector<QStringRef> pieces = body.splitRef(QLatin1String("\r\n"), Qt::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();
}
int main(int argc, char *argv[])
{
QApplication a(argc,argv);
QStringList headers;
headers << "Subject: Hello World"
<< "From: address@example.com";
QString body = "This is a test.\r\nThat is an other test.\r\n";
Message message(body, headers);
qDebug() << "Original:" << message;
QVariant stored;
stored.setValue(message);
qDebug() << "Stored:" << stored;
Message retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;
retrieved = stored.value<Message>();
qDebug() << "Retrieved:" << retrieved;
a.exec();
return 0;
}