创建自定义 Qt 类型
创建自定义 Qt 类型
在使用 Qt 创建用户界面(尤其是具有专门控件和功能的用户界面)时,开发人员有时需要创建新的数据类型,以便与 Qt 现有的值类型集一起使用或取而代之。
QSize 、QColor 和QString 等标准类型都可以存储在QVariant 对象中,在基于QObject 的类中用作属性类型,并在信号槽通信中发射。
在本文档中,我们将采用自定义类型,并介绍如何将其集成到 Qt 的对象模型中,使其能以与标准 Qt 类型相同的方式存储。然后,我们将展示如何注册自定义类型,使其能在信号和插槽连接中使用。
创建自定义类型
在开始之前,我们需要确保创建的自定义类型满足QMetaType 的所有要求。换句话说,它必须提供
- 一个公共默认构造函数、
- 一个公共复制构造函数,以及
- 一个公共析构函数。
下面的Message
类定义包括这些成员:
class Message { public: Message() = default; ~Message() = default; Message(const Message &) = default; Message &operator=(const Message &) = default; Message(const QString &body, const QStringList &headers); QStringView body() const; QStringList headers() const; private: QString m_body; QStringList m_headers; };
该类还提供了一个正常使用的构造函数和两个用于获取私有数据的公共成员函数。
使用 QMetaType 声明类型
Message
类只需一个合适的实现即可使用。但是,如果没有 Qt Assistant 的帮助,Qt 的类型系统将无法理解如何存储、检索和序列化该类的实例。例如,我们将无法在QVariant 中存储Message
值。
Qt XML 中负责自定义类型的类是QMetaType 。要让该类了解类型,我们需要在定义该类的头文件中调用Q_DECLARE_METATYPE() 宏:
Q_DECLARE_METATYPE(Message);
这样,Message
的值就可以存储在QVariant 对象中,并在以后进行检索:
QVariant消息检索= qvariant_cast<Message>(stored);qDebug() << "Retrieved:" << retrieved; retrieved= qvariant_cast<Message>(stored);qDebug() << "Retrieved:" << retrieved;
Q_DECLARE_METATYPE() 宏还可以将这些值用作信号的参数,但仅限于直接的信号槽连接。为了使自定义类型普遍适用于信号和插槽机制,我们需要做一些额外的工作。
创建和销毁自定义对象
虽然上一节中的声明使该类型可用于直接信号槽连接,但它不能用于队列信号槽连接,例如不同线程中对象之间的连接。这是因为元对象系统不知道如何在运行时处理自定义类型对象的创建和销毁。
要在运行时创建对象,可调用qRegisterMetaType() 模板函数向元对象系统注册。只要在建立第一个使用该类型的连接之前调用该函数,该类型也可用于队列信号槽通信。
队列自定义类型示例声明了一个Block
类,该类已在main.cpp
文件中注册:
intmain(intargc, char *argv[]) { QApplicationapp(argc,argv); ... qRegisterMetaType<Block>(); ...returnapp.exec(); }
该类型随后将用于window.cpp
文件中的信号槽连接:
Window::Window(QWidget *parent) : QWidget(parent), thread(new RenderThread(this)) { ... connect(thread, &RenderThread::sendBlock, this, &Window::addBlock); ... setWindowTitle(tr("Queued Custom Type")); }
如果未注册类型就在队列连接中使用,控制台将打印警告;例如
QObject::connect: Cannot queue arguments of type 'Block' (Make sure 'Block' is registered using qRegisterMetaType().)
使类型可打印
为了调试目的,使自定义类型可打印通常非常有用,如下面的代码:
Message message(body,headers);qDebug() << "Original:" << message;
这可以通过为类型创建流运算符来实现,流运算符通常在该类型的头文件中定义:
这里对Message
类型的实现做了一些努力,使可打印的表示尽可能具有可读性:
QDebug operator<<(QDebug dbg, const Message &message) { const QList<QStringView> pieces = message.body().split(u"\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; }
当然,发送到调试流的输出可以做得很简单,也可以做得很复杂。需要注意的是,该函数返回的值是QDebug 对象本身,不过这通常是通过调用QDebug 的成员函数maybeSpace() 获得的,该函数会用空格符填充数据流,使其更具可读性。
更多阅读
Q_DECLARE_METATYPE() 宏和qRegisterMetaType() 函数文档包含有关其用途和限制的更多详细信息。
队列自定义类型示例展示了如何使用本文档概述的功能实现自定义类型。
调试技术文档概述了上文讨论的调试机制。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.