摘要: 简述 当使用Qt创建用户界面时,特别是那些带有特殊控制和特征的界面时,开发者通常需要创建新数据类型来扩展或替换Qt现有的的值类型集合。 标准类型,比如:QSize、QColor和QString都可以被存储到QVariant对象中,在基于QObject的类中可用作属性的类型,并且可以在信号-槽通信时发射。 下面,我会创建一个自定义类型,并且说明如何将它集成到Qt的对象模型

简述

当使用Qt创建用户界面时,特别是那些带有特殊控制和特征的界面时,开发者通常需要创建新数据类型来扩展或替换Qt现有的的值类型集合。

标准类型,比如:QSize、QColor和QString都可以被存储到QVariant对象中,在基于QObject的类中可用作属性的类型,并且可以在信号-槽通信时发射。

下面,我会创建一个自定义类型,并且说明如何将它集成到Qt的对象模型中,以便能够以与其他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;
};

这个类同时还提供了一个经常使用的构造函数,以及两个用于获取私有数据的共有成员函数。

使用QMetaType声明类型

Message类仅需要一个合适的实现,以便可以使用。然而,如果没有其他辅助信息,Qt类型系统将无法理解如何存储、检索和序列化该类的实例。例如:我们无法将Message的值保存到QVariant中。

Qt中负责自定义类型的类是QMetaType。为了让这个类识别该类型,当定义这个类时,需要头文件中使用Q_DECLARE_METATYPE()宏:

Q_DECLARE_METATYPE(Message);

这样,就可以将Message的值保存在QVariant对象中,并在以后读取。完整代码可参见Custom Type Example中的示范代码。

所述Q_DECLARE_METATYPE()宏同时也使得这些值可以被用作信号的参数,但是仅限于direct信号槽连接。为了能在信号槽机制中使用自定义类型,我们需要做一些另外的工作。

创建和销毁自定义对象

虽然上面部分中的声明使类型可以在direct信号槽连接中使用,但是无法用于queued信号槽连接中,例如:在不同线程的对象之间所建立的连接。这是因为元对象系统不知道如何在运行时处理自定义类型对象的创建和销毁操作。

为了可以在运行时创建对象,需要调用qRegisterMetaType()模板函数在元对象系统中注册此类型。只要在使用此类型的第一次连接建立前调用注册函数,该类型可被用于queued信号槽连接。

Queued Custom Type Example示例中在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"));
}

如果一个没有被注册的类型被用于queued连接中,在控制台中会输出一条警告信息。例如:

QObject::connect: Cannot queue arguments of type ‘Block’ (Make sure ‘Block’ is registered using qRegisterMetaType().)

使类型可打印

出于调试目的,使一个自定义类型可打印是非常有用的,就像下面的代码一样:

Message message(body, headers);
qDebug() << "Original:" << message;

可以通过为此类型创建流操作符来达到目的,这通常定义在该类型的头文件中:

QDebug operator<<(QDebug dbg, const Message &message);

在Custom Type Example的Message类实现中,我们努力让可打印的内容尽可能的通俗易读:

QDebug operator<<(QDebug dbg, const Message &message)
{
const QString body = message.body();
QVector<QStringRef> pieces = body.splitRef("\r\n", QString::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();
}

当然,输出到debug流的信息是简单还是复杂都随你的意思。需要注意的是这个函数的返回值是QDebug对象本身,尽管它通常通过调用QDebug的成员函数maybeSpace()来获得,用空白字符填充流,以使其更具可读性。

转自:https://yq.aliyun.com/articles/62082

Qt之创建自定义类型的更多相关文章

  1. JavaScript之面向对象学习七(动态原型模式、寄生构造函数模式、稳妥构造函数模式创建自定义类型)

    一.动态原型模式 在面向对象学习六中的随笔中,了解到组合构造函数模式和原型模式创建的自定义类型可能最完善的!但是人无完人,代码亦是如此! 有其他oo语言经验的开发人员在看到独立的构造函数和原型时,很可 ...

  2. qt 如何注册自定义类型?

    如何声明自定义类型 如果仅仅在 QVariant 中使用,则仅需要使用 Q_DECLARE_METATYPE 宏进行声明即可. class Custom_ : public QObject { Q_O ...

  3. Lambda语句中创建自定义类型时,也可指定某种特定类型,方法是在new与{}之间写上类型名称

    如: var fc =...ChildFath = fc.Select(c => new Child_Father { child = c.child, father = c.father }) ...

  4. javascript面向对象--自定义类型

    Javascript是基于原型实现面向对象的,因此并没有类和接口,它的对象也与其他基于类的语言中的对象有所不同.在Javascript中,每个对象都是基于一个引用类型创建的,这个引用类型可以是原生类型 ...

  5. sharepoint2010 创建自定义列表

    转:http://boke.25k5.com/kan77298.html 如何创建自定义列表 首先了解创建自定义列表中涉及到的几个名词:栏.内容类型. ①栏:栏即列.字段(Field),MSDN中给出 ...

  6. Javascript 中创建自定义对象的方法(设计模式)

    Javascript 中创建对象,可以有很多种方法. Object构造函数/对象字面量: 抛开设计模式不谈,使用最基本的方法,就是先调用Object构造函数创建一个对象,然后给对象添加属性. var ...

  7. Struts(二十):自定义类型转换器

    如何自定义类型转换器: 1)为什么需要自定义类型转化器?strtuts2不能自动完成字符串到所有的类型: 2) 如何定义类型转化器? 步骤一:创建自定义类型转化器的类,并继承org.apache.st ...

  8. Oracle自定义类型在C#中调用示例

    1.C#代码: 1)using Oracle.DataAccess.Types; using System; using System.Collections.Generic; using Syste ...

  9. Android进阶AIDL使用自定义类型

    原文首发于微信公众号:jzman-blog,欢迎关注交流! 上篇文章中主要介绍从 AIDL 的使用方式以及 Android 开发中不同进程之间的通信,遗留的问题是如何在 AIDL 中使用自定义类型,具 ...

随机推荐

  1. asp.net 中用easyui中的treegird的简单使用

    几乎每个‘数人头’项目中都会用到的功能,这里先记下来,以后直接到这里复制代码就行了,ASP.NET MVC中的使用 数据库用户表中的除了有个parentid父级ID外,我还多加了以个字段,parent ...

  2. SAP NetWeaver BW 7.3介绍

    (摘自SAP 官方 EIM300 SAP NetWeaver BW 7.3 特色功能.前景展望与路线图)

  3. signal.h中的宏定义SIG_DFL及SIG_IGN

    SIG_DFL,SIG_IGN 分别表示无返回值的函数指针,指针值分别是0和1,这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值.SIG_DFL:默认信号处理程序SIG_IGN:忽略信号的处理程 ...

  4. Oracle XQuery 过滤XML查询SQL

    Oralce 支持SQL XQuery查询 一个简单示例: SELECT XMLQuery('for $i in /Videogame return $i/Type' passing by value ...

  5. kvm libvirt 虚拟机管理

    http://www.2cto.com/os/201203/123128.html kvm虚拟机管理一.环境role         hostname    ip                  O ...

  6. DIV+CSS兼容解决DIV最大宽度和最小宽度问题

    在制作网页中,我们经常会碰到min/max-width,min/max-height在IE6底下是无效的,这也是web设计师最头疼的问题之一,以下的方法可以解决这些难题,并且比较简约.当然,如果你还有 ...

  7. vue-router "path" is required in a route configuration

    启用了动态路由,一直提示这个错误,页面打开也是空白,后来发现原来是component参数错误. 正确的写法为: component: () => import ('@/views/own-spa ...

  8. BeautifulSoup 使用select方法详解(通过标签名,类名, id,组合,属性查找)

    import requestsfrom bs4 import BeautifulSoup blslib="html5lib"user_agent="Mozilla/5.0 ...

  9. Android仿华为天气绘制刻度盘

    效果图 能够看到这个自己定义控件结合了颜色渐变.动态绘制刻度.动态水球效果.接下来我们就来看看这个效果是怎样一步一步实现的. 開始自己定义控件 和非常多自己定义控件方式一样须要去基础某种View或者某 ...

  10. 【Android Studio】DDMS的模拟器控制(Emulator Control)不可用

    问题:Win10,Android Studio2.1.3中,创建了一个安卓手机模拟器,但是在DDMS中模拟器控制(Emulator Control)是灰色不可用的(比如想模拟来电和来短信).如下图: ...