当前位置: 首页 > news >正文

QT 5 同时使用Q_DECLARE_METATYPE(pointdata) 和继承 QObjectUserData

在Qt框架中,QObjectUserDataQ_DECLARE_METATYPE() 宏都与Qt的元对象系统有关,但它们的使用方式有一些特别的限制和兼容性问题。

  1. 关于QObjectUserData

    • QObjectUserData 是一个用来存储用户数据的类。在Qt中,每个 QObject 可以存储一个 QObjectUserData 指针数组,每个指针可以指向一个 QObjectUserData 对象。
    • 这个类通常用于在不修改类定义的情况下,为对象附加额外的数据。
  2. 关于Q_DECLARE_METATYPE()

    • Q_DECLARE_METATYPE() 宏允许类型在Qt的元对象系统中使用,如在 QVariant 中。使用这个宏可以让自定义类型通过 QVariant 进行存储和传递。
    • 宏定义需要类型是完全定义的,且具有公共的默认构造函数、复制构造函数和析构函数。

兼容性问题

  • 由于 QObject 本身和继承自 QObject 的任何类不能被复制,这意味着如果你的类继承自 QObject(或间接通过其他类继承),这个类就不能使用 Q_DECLARE_METATYPE()。因为 QVariant 需要能够复制其存储的类型。
  • 如果你的类仅仅继承自 QObjectUserData 并不包含 QObject 的继承,理论上是可以使用 Q_DECLARE_METATYPE() 的,只要你的类满足可以被复制的要求。但是,这种情况比较少见,因为 QObjectUserData 的设计初衷是作为一种与 QObject 相关的数据扩展。

结论

  • 如果你的类继承自 QObject 或包含 QObject 的成员,则不能使用 Q_DECLARE_METATYPE()
  • 如果你的类仅继承自 QObjectUserData 并且符合可以被复制的条件(例如,提供了公共的构造函数和复制构造函数),那么使用 Q_DECLARE_METATYPE() 是可能的。

 

QObject 本身以及任何从 QObject 继承的类都不能被复制。这是由 QObject 的设计决定的,主要出于以下几个原因:

  1. 信号与槽机制QObject 提供了一个强大的信号与槽机制,用于对象之间的通信。每个 QObject 可以发射信号,这些信号可以被同一个对象或其他对象的槽函数接收。如果 QObject 可以被复制,那么信号和槽的连接就可能会被意外复制,从而导致难以追踪的bug。

  2. 对象的唯一性:每个 QObject 可以有一个父对象和多个子对象。这种父子关系管理着对象之间的层次结构和生命周期。复制 QObject 会复杂化这种父子关系,可能导致父对象和子对象在删除或移动时出现问题。

  3. 设计决策:为了保持对象间关系的清晰和简单,Qt设计者选择禁用 QObject 的复制构造函数和赋值运算符,使它们为私有成员。这样,任何试图复制 QObject 或其派生类的操作都会在编译时被阻止。

因此,如果你需要在 Qt 中使用包含对象状态的类并希望通过 QVariant 来传递,你应该设计这些类为非 QObject 派生类,并确保它们可以安全地被复制和销毁。这样,你就可以使用 Q_DECLARE_METATYPE() 宏来注册你的类型。

“复制”指的是通过拷贝构造函数和拷贝赋值运算符来复制一个对象。在 QObject 的上下文中,以下是具体的含义:

  1. 拷贝构造函数:这是一个构造函数,它初始化一个新对象作为一个已存在对象的副本。在 QObject 的情况下,拷贝构造函数是被明确声明为删除的,这意味着你不能使用一个 QObject 或其派生类的实例来直接初始化另一个实例。

  2. 拷贝赋值运算符:这是一个赋值运算符,它允许将一个对象的状态赋值到另一个已存在的对象上。对于 QObject 及其派生类,拷贝赋值运算符同样被声明为删除,这意味着你不能通过赋值将一个 QObject 或其派生类的状态复制到另一个对象。

这种设计是为了防止对象之间的不当信号连接和复杂化的父子关系,确保 QObject 的对象模型保持简洁和一致。这也意味着在使用 Qt 框架时,应该避免设计需要复制 QObject 或其派生类的场景,而是通过指针和引用来管理对象的生命周期和关系。

这个示例程序主要演示了如何在 Qt 应用程序中创建和使用自定义数据类型 `PointData` 和 `Point`。程序通过结合使用 `Q_DECLARE_METATYPE`、`QObjectUserData`,以及 Qt 的属性和变量系统 (`QVariant`),展示了如何有效地管理和使用自定义数据结构。下面是对程序各部分功能的详细解释:

功能和目的

1.PointData` 类的定义与注册
   - `PointData` 是一个简单的类,用于存储和操作二维坐标点(x, y)。
   - 使用 `Q_DECLARE_METATYPE(PointData)` 宏声明这个类,使其可以被 Qt 的元类型系统识别。这是必要的步骤,以便 `PointData` 对象可以安全地存储在 `QVariant` 类型中,并在 Qt 的信号和槽系统中使用。

2. `Point` 类的定义
   - `Point` 类继承自 `QObjectUserData`,允许它作为用户自定义数据被存储在任何 `QObject` 实例中。这为 `Point` 实例的存储提供了便利,尤其是在需要将额外数据附加到 `QObject` 对象上时。

3. 应用程序主函数的实现
   - 元类型注册:通过 `qRegisterMetaType<PointData>()` 确保 `PointData` 类型被 Qt 系统识别,这是使用自定义类型与 `QVariant` 相互操作的前提。
   - 使用 `QVariant` 管理自定义数据:创建 `PointData` 实例,并通过 `QVariant` 系统演示如何将自定义类型封装和解封,从而验证元类型的注册和使用。
   - 使用 `QObjectUserData`创建 `Point` 实例,并将其作为用户数据附加到 `QObject` 对象上,然后从该对象中检索 `Point` 实例。这展示了如何在运行时动态地管理额外的对象信息。

 总体目的

- 演示 Qt 的灵活性和功能:通过使用 `Q_DECLARE_METATYPE` 和 `QObjectUserData`,这个示例展示了 Qt 如何处理自定义类型的封装和传输,以及如何将额外的数据动态地附加到 `QObject` 实例。
- 教育和示范:代码提供了一个实际的例子,说明了如何在 Qt 应用程序中创建、管理和使用自定义数据类型,是学习 Qt 开发的有用资源。

通过这种方式,示例程序不仅展示了 Qt 的核心概念和功能,还提供了一个关于如何扩展 Qt 应用程序功能的实际案例,特别适用于那些需要在应用程序中处理复杂数据结构和进行高级数据管理的场景。

在 Qt 框架中,Q_DECLARE_METATYPE 宏用于注册一个自定义类型,使之可以使用 Qt 的元类型系统。这种注册使得该类型能够在 Qt 的一些高级特性中使用,如信号和槽系统、变量传递等。具体来说,使用 Q_DECLARE_METATYPE 之后,你的类型就可以安全地用于 QVariant,这是 Qt 用于存储能够包含任何类型的通用容器。

功能和用途

当你声明 Q_DECLARE_METATYPE(PointData) 时,你允许 PointData 类型的对象被包装进 QVariant 对象。这样做有几个好处:

  1. 使用信号和槽:使得 PointData 类型可以安全地作为参数在信号和槽之间传递。没有注册的类型不能用于信号和槽系统中,除非它们是内置的 Qt 类型。

  2. 动态属性:允许你将 PointData 对象作为动态属性添加到任何 QObject 派生类的实例中。

  3. 变体操作:可以在需要类型安全和灵活操作的场景下,将 PointData 存储和检索为 QVariant。这在需要将数据存储在通用容器中时非常有用,例如,在模型视图架构中传递自定义数据。

// pointdata.h
#ifndef POINTDATA_H
#define POINTDATA_H#include <QMetaType>class pointdata
{
public:int x;int y;pointdata();pointdata(int x, int y);int getX() const;int getY() const;void setX(int x);void setY(int y);
};Q_DECLARE_METATYPE(pointdata)#endif // POINTDATA_H

#include "pointdata.h"pointdata::pointdata(): x(0), y(0)
{}pointdata::pointdata(int x, int y): x(x), y(y)
{}int pointdata::getX() const
{return x;
}int pointdata::getY() const
{return y;
}void pointdata::setX(int x)
{this->x = x;
}void pointdata::setY(int y)
{this->y = y;
}

在 Qt 框架中,QObjectUserData 类是用于将用户数据附加到 QObject 类的实例上的基类。它提供了一个方便的方式来存储额外的、动态的信息与 QObject 相关联,而无需修改 QObject 的源代码或继承自 QObject

使用场景和目的

QObjectUserData 主要被设计用于在运行时向 QObject 实例添加自定义数据,而不改变其类定义。这对于如插件系统等需要在没有修改现有类的情况下扩展对象功能的场景特别有用。

基本概念

当你继承自 QObjectUserData 并创建自定义类时,你可以利用 Qt 对象系统的功能,将这个自定义类的实例存储为 QObject 的一部分。每个 QObject 可以存储多个 QObjectUserData 对象,每个对象都与一个唯一的整数键相关联。

// point.h
#ifndef POINT_H
#define POINT_H#include <QObjectUserData>
#include <QSharedPointer>
#include "pointdata.h"class Point : public QObjectUserData
{
public:pointdata data;public:Point();Point(int x, int y);int getX() const;int getY() const;void setX(int x);void setY(int y);QSharedPointer<Point> clone() const;
};#endif // POINT_H
#include "point.h"Point::Point() {}Point::Point(int x, int y): data(x, y)
{}int Point::getX() const
{return data.getX();
}int Point::getY() const
{return data.getY();
}void Point::setX(int x)
{data.setX(x);
}void Point::setY(int y)
{data.setY(y);
}QSharedPointer<Point> Point::clone() const
{return QSharedPointer<Point>::create(data.getX(), data.getY());
}

主函数:

  • 注册 pointdata 类型为 Qt 元类型系统的一部分。这使得 pointdata 类型可以被用于 Qt 的信号和槽机制,并能以类型安全的方式存储在 QVariant 中。
  • 创建一个 pointdata 实例并将其包装进 QVariant 对象。这展示了如何使用自定义类型与 QVariant,以及如何从 QVariant 中提取自定义类型的实例。使用 QVariant 是 Qt 中管理泛型数据的常见方式。
  • 使用 Point 类实例作为用户数据存储在 QObject 中。这个示例演示了如何将自定义的用户数据附加到 Qt 对象,并在之后检索使用这些数据。这是利用 QObjectUserData 实现的功能。

#include <QApplication>
#include <QDebug>
#include "Point.h"
#include "PointData.h"
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);// Register PointData as a metatypeqRegisterMetaType<pointdata>("PointData");// Using PointDatapointdata pd(3, 4);QVariant var = QVariant::fromValue(pd);if (var.canConvert<pointdata>()) {pointdata extractedData = var.value<pointdata>();qDebug() << "PointData: x =" << extractedData.getX() << ", y =" << extractedData.getY();}// Using Point with QObjectUserDataQObject obj;Point *point = new Point(5, 6); // Create a Point instanceobj.setUserData(0, point);      // Store the Point instance as user data in QObject// Retrieve and use the Point instance from QObjectPoint *retrievedPoint = static_cast<Point *>(obj.userData(0));if (retrievedPoint) {qDebug() << "Point: x =" << retrievedPoint->data.getX()<< ", y =" << retrievedPoint->data.getY();}MainWindow w;w.show();return a.exec();
}

输出:

07:05:33: Starting E:\WD\untitled85\build\Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled85.exe...
PointData: x = 3 , y = 4
Point: x = 5 , y = 6
07:05:38: E:\WD\untitled85\build\Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\untitled85.exe 退出,退出代码: 0{1 ?} {2?}

相关文章:

QT 5 同时使用Q_DECLARE_METATYPE(pointdata) 和继承 QObjectUserData

在Qt框架中&#xff0c;QObjectUserData 和 Q_DECLARE_METATYPE() 宏都与Qt的元对象系统有关&#xff0c;但它们的使用方式有一些特别的限制和兼容性问题。 关于QObjectUserData&#xff1a; QObjectUserData 是一个用来存储用户数据的类。在Qt中&#xff0c;每个 QObject 可以…...

【MySQL进阶之路 | 高级篇】范式概述与第一范式

1. 范式简介 在关系型数据库中&#xff0c;关于数据表的设计的基本原则&#xff0c;规则就称为范式。可以理解为&#xff0c;一张数据表的设计结果需要满足的某种设计标准的级别。要想设计一个结构合理的关系型数据库&#xff0c;必须满足一定的范式。 范式的英文名是Normal …...

Open-TeleVision复现及机器人迁移

相关信息 标题 Open-TeleVision: Teleoperation with Immersive Active Visual Feedback作者 Xuxin Cheng1 Jialong Li1 Shiqi Yang1 Ge Yang2 Xiaolong Wang1 UC San Diego1 MIT2主页 https://robot-tv.github.io/链接 https://robot-tv.github.io/resources/television.pdf代…...

Notepad++换安装路径之后,右键打开方式报错:Windows无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目。的处理方法

把Notepad添加到右键打开方式&#xff0c;可以参考下面的3篇文章添加&#xff1a; https://blog.csdn.net/xiaoerbuyu1233/article/details/88287747 https://blog.csdn.net/qq_44000337/article/details/120277317 https://www.cnblogs.com/zhrngM/p/12899026.html 这里主要是…...

【Flutter 面试题】 使用成熟状态管理库的弊端有哪些?

【Flutter 面试题】 使用成熟状态管理库的弊端有哪些? 文章目录 写在前面口述回答补充说明写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,51CTO专家博主。2023博客之星TOP153。 👏🏻 正在学 Flutter 的同学,你好! 😊 …...

Apache Commons技术详解

文章目录 简介官网链接原理基础使用Commons LangCommons Collections 高级使用Commons IOCommons Math 优缺点优点缺点 总结 简介 Apache Commons 是 Apache 软件基金会下的一个项目&#xff0c;旨在提供可重用的Java组件。这些组件覆盖了广泛的编程任务&#xff0c;从字符串处…...

怎样使用 Juicer tools 的 dump 命令将.hic文件转换为交互矩阵matrix计数文件 (Windows)

创作日志&#xff1a; 万恶的生信…一个scHiC数据集没有提供处理好的计数文件&#xff0c;需要从.hic转换。Github一个个好长的文档看了好久才定位到 juicer tools 的dump命令&#xff0c;使用起来比想象中简单。 一、下载Juicer tools 注意&#xff1a;使用Juicer tools的前提…...

【Docker】Docker Desktop - WSL update failed

问题描述 Windows上安装完成docker desktop之后&#xff0c;第一次启动失败&#xff0c;提示&#xff1a;WSL update failed 解决方案 打开Windows PowerShell 手动执行&#xff1a; wsl --set-default-version 2 wsl --update...

基于rsync\unlink 等一套本机备份跨机备份历史备份清理shell 脚本

一 摘要 本文主要介绍一套本地备份、跨机器备份、历史备份清理脚本&#xff0c;使用场景如数据库备份等 二 环境 linux 系列系统 基本都支持&#xff0c;个别命令可能需要微调。 2.1 实验环境 [rootlocalhost rsync]# cat /etc/centos-release CentOS Linux release 7.9.2…...

使用nginx实现一个端口和ip访问多个vue前端

前言&#xff1a;由于安全组要求&#xff0c;前端页面只开放一个端口&#xff0c;但是项目有多个前端&#xff0c;此前一直使用的是一个前端使用单独一个端口进行访问&#xff0c;现在需要调整。 需要实现&#xff1a;这里以80端口为例&#xff0c;两个前端分别是&#xff1a;p…...

Linux云计算 |【第一阶段】SERVICES-DAY5

主要内容&#xff1a; 源码编译安装、rsync同步操作、inotify实时同步、数据库服务基础 实操前骤&#xff1a;&#xff08;所需tools.tar.gz与users.sql&#xff09; 1.两台主机设置SELinnx和关闭防火墙 setenforce 0 systemctl stop firewalld.service //停止防火墙 sy…...

IP第一次综合实验

一、实验拓扑 二、实验要求 1、R6为ISP&#xff0c;接口IP地址均为公有地址&#xff0c;该设备只能配置地址之后不能冉对其进行任何配置 2、R1-R5为局域网&#xff0c;私有Ip地址192.168.1.0/24&#xff0c;请合理分配 3、R1、82、R4&#xff0c;各有两个环回IP地址;R5,R6各…...

Could not load dynamic library ‘cudart64_100.dll‘

python代码报错 Could not load dynamic library cudart64_100.dll; dlerror: cudart64_100.dll not found 2024-07-22 14:19:21.931639: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine…...

四大引用——强软弱虚

目录 一、强引用 二、软引用 三、弱引用 四、虚引用 一、强引用 强引用是在程序代码之中普遍存在的&#xff0c;类似于“Object obj new Object()”&#xff0c;obj变量引用Object这个对象&#xff0c;就叫做强引用。当内存空间不足&#xff0c;Java虚拟机宁愿抛出OutOfMe…...

MySQL--索引(2)

InnoDB 1.索引类型 主键索引(Primary Key) 数据表的主键列使用的就是主键索引。 一张数据表有只能有一个主键&#xff0c;并且主键不能为 null&#xff0c;不能重复。 在 mysql 的 InnoDB 的表中&#xff0c;当没有显示的指定表的主键时&#xff0c;InnoDB 会自动先检查表中是…...

JVM类加载机制详解

Java在运行期才对类进行加载到内存、连接、初始化过程。这使得Java应用具有极高的灵活性和拓展性&#xff0c;可以依赖运行期进行动态加载和动态连接。 主要加载哪些&#xff1f;Java中的数据类型分为基本数据类型和引用数据类型&#xff0c;基本数据类型由虚拟机预先定义&…...

【MATLAB实战】基于UNet的肺结节的检测

数据&#xff1a; 训练过程图 算法简介&#xff1a; UNet网络是分割任务中的一个经典模型,因其整体形状与"U"相似而得名,"U"形结构有助于捕获多尺度信息,并促进了特征的精确重建&#xff0c;该网络整体由编码器,解码器以及跳跃连接三部分组成。 编码器由…...

Elasticsearch基础(五):使用Kibana Discover探索数据

文章目录 使用Kibana Discover探索数据 一、添加样例数据 二、数据筛选 三、保存搜索 使用Kibana Discover探索数据 一、添加样例数据 登录Kibana。在Kibana主页的通过添加集成开始使用区域&#xff0c;单击试用样例数据。 在更多添加数据的方式页面下方&#xff0c;单击…...

爬取百度图片,想爬谁就爬谁

前言 既然是做爬虫&#xff0c;那么肯定就会有一些小心思&#xff0c;比如去获取一些自己喜欢的资料等。 去百度图片去抓取图片吧 打开百度图片网站&#xff0c;点击搜索xxx&#xff0c;打开后&#xff0c;滚动滚动条&#xff0c;发现滚动条越来越小&#xff0c;说明图片加载…...

HTTP 缓存

缓存 web缓存是可以自动保存常见的文档副本的HTTP设备&#xff0c;当web请求抵达缓存时&#xff0c;如果本地有已经缓存的副本&#xff0c;就可以从本地存储设备而不是从原始服务器中提取这个文档。使用缓存有如下的优先。 缓存减少了冗余的数据传输缓存环节了网络瓶颈的问题…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...