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支持的数据类型,包括基本数据类型(如整数、浮点数、布尔值、枚举)、复合数据类型(如字符串、列表、映射、日期时间)、自定义数据类型(通过注册元类型实现)等。
QDataStream out(...);QVariant v(123); // The variant now contains an intint x = v.toInt(); // x = 123out << v; // Writes a type tag and an int to outv = QVariant("hello"); // The variant now contains a QByteArrayv = QVariant(tr("hello")); // The variant now contains a QStringint y = v.toInt(); // y = 0 since v cannot be converted to an intQString 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 variantint z = v.toInt(); // z = 123qDebug("Type is %s", // prints "Type is int"v.typeName());v = v.toInt() + 100; // The variant now hold the value 223v = QVariant(QStringList());
- ✨类型安全的数据访问:QVariant 允许您以一种类型安全的方式访问封装的数据,而不需要显式的类型转换。您可以通过 QVariant::value() 将 QVariant 转换为指定类型 T 的值。
QVariant var = 42;int intValue = var.value<int>(); // 获取整数值
- ✨动态类型识别:Variant 可以存储数据的同时记录其实际类型信息,使得在运行时能够动态地识别和转换数据类型。您可以使用 QVariant::type() 方法获取存储的数据类型。
QVariant var = "Hello";qDebug() << var.type(); // 输出数据类型
- ✨空值表示:QVariant 可以表示空值(即无效值),这在处理可能没有值的情况时非常有用。您可以使用 QVariant::isNull() 或 QVariant::isValid() 方法检查 QVariant 是否为空或有效。
QVariant var; // 未初始化的空值if (var.isNull()) {qDebug() << "Variant is null";}
- ✨通用数据存储和传递:QVariant 在Qt中广泛用于通用数据的存储和传递,例如在信号和槽参数中传递任意类型的数据,或者在模型/视图框架中存储和操作各种类型的数据。下面是在QTreeView中设置一个节点的角色数据。
//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 能够处理自定义的用户数据类型。
#include <QMetaType> // 包含Q_DECLARE_METATYPE宏所需的头文件
//可以通过下面这种方式声明元类型
Q_DECLARE_METATYPE("QVector<AgendaInfo>");
💫 补充说明:
在 Qt 中,Q_DECLARE_METATYPE 宏和 qRegisterMetaType 函数都是用于将自定义类型注册到 Qt 的元类型系统中,以便在信号槽、线程间通信等场景中能够正确地处理这些自定义类型的数据。虽然它们的作用相似,但在不同的情况下适合使用不同的方式。
- 💥Q_DECLARE_METATYPE 宏用于在编译时声明自定义类型作为元类型,以便 Qt 的元类型系统能够识别和处理该类型的数据。通常在自定义类型的声明之后,通过 Q_DECLARE_METATYPE 宏来告知 Qt 如何处理该类型。
- 💥qRegisterMetaType 函数用于在运行时动态注册自定义类型作为元类型。通常用于将自定义类型注册为 Qt 的信号槽系统中的参数类型,以便能够在不同线程间传递该类型的数据。
🌸QVariant实战应用
- ✨范例:
下面我利用QVariant来容纳一个自定义的数据类型ItemInfo,将其与QListWidget中的项(QListWidgetItem)关联起来,将其作为项的用户自定义角色的起始值(Qt::UserRole)的数据。实现主要项目功能如下:- 💫通过QLineEdit(Qt提供的单行文本编辑器控件)来输入ItemInfo的成员变量的值。
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是用户自定义数据结构类型,必须通过声明元类型
- 💫通过QPushButton(Qt提供的普通按钮控件)来打开QFileDialog(文件选择对话框),选择Icon文件的路径,并将图片显示在QLabel中。
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())
- 💫通过QPushButton(Qt提供的普通按钮控件)来生成一个QListWidgetItem,并添加到QListView中,展示出与该Item关联的Icon、Name等信息。
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);// 创建包含节点信息的 QListWidgetItemQListWidgetItem *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 中
}
- 💫通过点击QListWidgetItem,通过QLabel显示出该Item关联的QSize信息。
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