【QVariant类型剖析】

QVariant类型剖析

🌟 官方文档中给出的定义

📘Because C++ forbids unions from including types that have non-default constructors or destructors, most interesting Qt classes cannot be used in unions. Without QVariant, this would be a problem for QObject::property() and for database work, etc.
📖[译文]:由于C ++禁止联合(union)中引用具有自定义构造函数或析构函数的类型,因此这个最有趣的QT类并不能在联合(union)中使用。没有QVariant类的支持,Qt的属性系统和数据库将无法进行正常工作。

📘A QVariant object holds a single value of a single type() at a time. (Some type()s are multi-valued, for example a string list.) You can find out what type, T, the variant holds, convert it to a different type using convert(), get its value using one of the toT() functions (e.g., toSize()) and check whether the type can be converted to a particular type using canConvert().
📖[译文]:QVariant对象每次只能容纳唯一类型的唯一值。(ps: 一些类型是可以包含有多个值,例如:QStringList)。你可以弄清楚T是哪种类型,通过使用convert()转换为不同的类型,通过T()显示转换获取其容纳的值,并通过canConvert()来判断是否能够转换为对应的类型。

📘The methods named toT() (e.g., toInt(), toString()) are const. If you ask for the stored type, they return a copy of the stored object. If you ask for a type that can be generated from the stored type, toT() copies and converts and leaves the object itself unchanged. If you ask for a type that cannot be generated from the stored type, the result depends on the type; see the function documentation for details
📖[译文]:toT()是一个稳定的方法(例如,toInt()、toString())。如果您要求存储的类型,他们会返回存储对象的副本。如果您要求一个可以从存储的类型生成的类型,toT()会复制和转换对象,并保持对象本身不变。如果您要求一个无法从存储的类型生成的类型,结果取决于该类型;有关详细信息,请参阅功能文档

🌟 特性

QVariant 是Qt中用于封装任意类型数据的类,它可以在不同数据类型之间进行转换和存储,是Qt中处理数据的重要工具之一。

  • ✨数据类型的封装和存储 :QVariant 可以封装和存储几乎所有Qt支持的数据类型,包括基本数据类型(如整数、浮点数、布尔值、枚举)、复合数据类型(如字符串、列表、映射、日期时间)、自定义数据类型(通过注册元类型实现)等。
cpp 复制代码
  QDataStream out(...);
  QVariant v(123);                // The variant now contains an int
  int x = v.toInt();              // x = 123
  out << v;                       // Writes a type tag and an int to out
  v = QVariant("hello");          // The variant now contains a QByteArray
  v = QVariant(tr("hello"));      // The variant now contains a QString
  int y = v.toInt();              // y = 0 since v cannot be converted to an int
  QString s = v.toString();       // s = tr("hello")  (see QObject::tr())
  out << v;                       // Writes a type tag and a QString to out
  ...
  QDataStream in(...);            // (opening the previously written stream)
  in >> v;                        // Reads an Int variant
  int z = v.toInt();              // z = 123
  qDebug("Type is %s",            // prints "Type is int"
          v.typeName());
  v = v.toInt() + 100;            // The variant now hold the value 223
  v = QVariant(QStringList());
  • ✨类型安全的数据访问 :QVariant 允许您以一种类型安全的方式访问封装的数据,而不需要显式的类型转换。您可以通过 QVariant::value() 将 QVariant 转换为指定类型 T 的值。
cpp 复制代码
 QVariant var = 42;
 int intValue = var.value<int>(); // 获取整数值
  • ✨动态类型识别 :Variant 可以存储数据的同时记录其实际类型信息,使得在运行时能够动态地识别和转换数据类型。您可以使用 QVariant::type() 方法获取存储的数据类型。
cpp 复制代码
 QVariant var = "Hello";
 qDebug() << var.type(); // 输出数据类型
  • ✨空值表示 :QVariant 可以表示空值(即无效值),这在处理可能没有值的情况时非常有用。您可以使用 QVariant::isNull() 或 QVariant::isValid() 方法检查 QVariant 是否为空或有效。
cpp 复制代码
 QVariant var; // 未初始化的空值
 if (var.isNull()) {
     qDebug() << "Variant is null";
 }
  • ✨通用数据存储和传递:QVariant 在Qt中广泛用于通用数据的存储和传递,例如在信号和槽参数中传递任意类型的数据,或者在模型/视图框架中存储和操作各种类型的数据。下面是在QTreeView中设置一个节点的角色数据。
cpp 复制代码
 //setData()函数为节点的某一列设置一个角色数据
 //其中column是列号role是角色的值, value是QVariant 类型的数。
 void QTreeWidgetItem::setData(int column , int role , const QVariant &value)
 //它为节点的第一列角色Qt::UserRole设置了字符串数据dataStr,Qt::UserRole是枚举类型Qt::ItemDataRole个预定义的值
 item >setData(MainWindow::colitem,Qt::UserRole,QVariant(dataStr)) ;
  • ✨元类型系统的支持:QVariant 和Qt的元类型系统(Meta Type System)密切相关。通过注册自定义类型到元类型系统,您可以使 QVariant 能够处理自定义的用户数据类型。
cpp 复制代码
#include <QMetaType> // 包含Q_DECLARE_METATYPE宏所需的头文件
//可以通过下面这种方式声明元类型
Q_DECLARE_METATYPE("QVector<AgendaInfo>");

💫 补充说明:
在 Qt 中,Q_DECLARE_METATYPE 宏和 qRegisterMetaType 函数都是用于将自定义类型注册到 Qt 的元类型系统中,以便在信号槽、线程间通信等场景中能够正确地处理这些自定义类型的数据。虽然它们的作用相似,但在不同的情况下适合使用不同的方式。

  1. 💥Q_DECLARE_METATYPE 宏用于在编译时声明自定义类型作为元类型,以便 Qt 的元类型系统能够识别和处理该类型的数据。通常在自定义类型的声明之后,通过 Q_DECLARE_METATYPE 宏来告知 Qt 如何处理该类型。
  2. 💥qRegisterMetaType 函数用于在运行时动态注册自定义类型作为元类型。通常用于将自定义类型注册为 Qt 的信号槽系统中的参数类型,以便能够在不同线程间传递该类型的数据。

🌸QVariant实战应用

  • 范例
    下面我利用QVariant来容纳一个自定义的数据类型ItemInfo,将其与QListWidget中的项(QListWidgetItem)关联起来,将其作为项的用户自定义角色的起始值(Qt::UserRole)的数据。实现主要项目功能如下:
    1. 💫通过QLineEdit(Qt提供的单行文本编辑器控件)来输入ItemInfo的成员变量的值。
cpp 复制代码
 struct ItemInfo{//节点信息
     QString name;//节点名称
     QSize size;//节点大小
     QIcon icon;//节点图标
     ItemInfo(const QString &n = QString(), const QSize &s = QSize(),  const QIcon &i = QIcon())
        : name(n), size(s), icon(i)
     {}
 };
// 在结构体定义之后添加Q_DECLARE_METATYPE宏声明
Q_DECLARE_METATYPE(ItemInfo)

ItemInfo是用户自定义数据结构类型,必须通过声明元类型

  1. 💫通过QPushButton(Qt提供的普通按钮控件)来打开QFileDialog(文件选择对话框),选择Icon文件的路径,并将图片显示在QLabel中。
cpp 复制代码
void widget::on_btn_Choose_clicked()
{
    // 选择节点图标文件
    QString fileName = QFileDialog::getOpenFileName(this, "Choose Icon"
                            , "D:/Repositories/PaperlessConferencePCSystem/src/Resources", "Icons (*.jpg;*.png)");

    if (!fileName.isEmpty()) {
        // 添加选择的图标文件到当前图标
        m_currentIcon.addFile(fileName);
        ui->lab_Icon->setPixmap(m_currentIcon.pixmap(50,50));
    }
}

其中getOpenFileName()函数是QFileDialog提供的静态函数,用于返回选中的文件名称。函数原型如下:

[static] QString QFileDialog::getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options())
  1. 💫通过QPushButton(Qt提供的普通按钮控件)来生成一个QListWidgetItem,并添加到QListView中,展示出与该Item关联的Icon、Name等信息。
cpp 复制代码
void widget::on_btn_Add_clicked()
{
    // 获取用户输入的节点信息
    QString name = ui->lEdit_Name->text();
    int width = ui->lEdit_Width->text().toInt();
    int height = ui->lEdit_Height->text().toInt();

    // 创建节点信息结构体
    ItemInfo info(name, QSize(width, height), m_currentIcon);

    // 创建包含节点信息的 QListWidgetItem
    QListWidgetItem *item = new QListWidgetItem(info.icon, info.name);
    // 将 QListWidgetItem 添加到 QListWidget 中
    ui->listWidget->addItem(item);

    // 存储节点信息与 QListWidgetItem 的关联,可使用 QListWidgetItem 的 data() 方法存储
    QVariant variant;
    variant.setValue(info);
    item->setData(Qt::UserRole, variant); // 将节点信息存储到 QListWidgetItem 的 data 中
}
  1. 💫通过点击QListWidgetItem,通过QLabel显示出该Item关联的QSize信息。
cpp 复制代码
void widget::on_listWidget_itemClicked(QListWidgetItem *item)
{
    // 获取点击的 QListWidgetItem 中存储的节点信息
    QVariant variant = item->data(Qt::UserRole);
    if (variant.canConvert<ItemInfo>()) {
        ItemInfo info = variant.value<ItemInfo>();

        // 显示节点的 QSize 信息
        QString sizeInfo = QString("Size: %1 x %2").arg(info.size.width()).arg(info.size.height());
        ui->lab_Status->setText("Item Size"+ sizeInfo);

        ui->lab_Icon->setPixmap(info.icon.pixmap(50,50));
    }
}

🌸项目成果展示

暂时支持图片展示效果,但我会将源码上传到GitHub[获取源码GitHub]
进入软件

填上ItemInfo的信息,并点击AddItem

点击左侧中存在的Item

再添加一个item

相关推荐
PGCCC8 分钟前
【PGCCC】Postgresql 存储设计
数据库·postgresql
PcVue China2 小时前
PcVue + SQL Grid : 释放数据的无限潜力
大数据·服务器·数据库·sql·科技·安全·oracle
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
锐策4 小时前
〔 MySQL 〕数据库基础
数据库·mysql
远歌已逝5 小时前
管理Oracle实例(二)
数据库·oracle
日月星宿~5 小时前
【MySQL】summary
数据库·mysql
爱吃土豆的程序员5 小时前
在oracle官网下载资源显示400 Bad Request Request Header Or Cookie Too Large 解决办法
java·数据库·oracle·cookie
睿思达DBA_WGX5 小时前
Oracle 11g rac 集群节点的修复过程
数据库·oracle
尘浮生6 小时前
Java项目实战II基于微信小程序的移动学习平台的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·学习·微信小程序·小程序
Leo.yuan6 小时前
数据量大Excel卡顿严重?选对报表工具提高10倍效率
数据库·数据分析·数据可视化·powerbi