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

QT开发技术 【元对象系统反射机制高级用法】 二

一、实现根据xml文件创建界面点击切换

在这里插入图片描述

二、xml文件

<?xml version="1.0" encoding="utf-8"?>
<treewidget name="主界面树" ReflectClass="CPageTreeBase"><OneLevelItem name="通信配置" ReflectClass="CCommunConfigWidget"/><OneLevelItem name="地检测试" ReflectClass=""><TwoLevelItem name="任意遥控帧列表" ReflectClass="CYKInsWidget"/></OneLevelItem>
</treewidget>

三、实现拆解

先准备3个需要用的类。

#ifndef CPAGETREEBASE_H
#define CPAGETREEBASE_H#include <QTreeWidget>
#include <QMouseEvent>class CPageTreeBase :public QTreeWidget
{Q_OBJECT
public:enum E_ITEM_LEVEL{E_LEVEL_ONE,E_LEVEL_TWO,};Q_INVOKABLE explicit CPageTreeBase(QWidget* parent = nullptr);virtual ~CPageTreeBase() = default;protected:void mousePressEvent(QMouseEvent* pEvent) override;private:virtual void OnPopMenu(const QPoint& pos);};#endif // CPAGETREEBASE_H#ifndef COMMUNCONFIGWIDGET_H
#define COMMUNCONFIGWIDGET_H#include <QWidget>namespace Ui {
class CCommunConfigWidget;
}class CCommunConfigWidget : public QWidget
{Q_OBJECTpublic:Q_INVOKABLE explicit CCommunConfigWidget(QWidget *parent = nullptr);~CCommunConfigWidget();private:Ui::CCommunConfigWidget *ui;
};#endif // COMMUNCONFIGWIDGET_H#ifndef YKINSWIDGET_H
#define YKINSWIDGET_H#include <QWidget>namespace Ui {
class CYKInsWidget;
}class CYKInsWidget : public QWidget
{Q_OBJECTpublic:Q_INVOKABLE explicit CYKInsWidget(QWidget *parent = nullptr);~CYKInsWidget();CYKInsWidget(const CYKInsWidget& rOthre) = delete;private:Ui::CYKInsWidget *ui;
};#endif // YKINSWIDGET_H

qRegisterMetaType 是一个非常重要的函数,其作用是将自定义类型注册到 Qt 的元对象系统中。
分别把 CPageTreeBase*、CCommunConfigWidget* 和 CYKInsWidget* 这三个指针类型注册到 Qt 的元对象系统中,并且给每个类型指定了对应的字符串名称

    qRegisterMetaType<CPageTreeBase*>("CPageTreeBase*");qRegisterMetaType<CCommunConfigWidget*>("CCommunConfigWidget*");qRegisterMetaType<CYKInsWidget*>("CYKInsWidget*");

下面是读取xml,创建反射创建类的功能

// 根据 XML 节点递归创建 QTreeWidgetItem 并挂载部件
QTreeWidgetItem* createTreeItemFromXml(const QDomElement& element, QTreeWidget* treeWidget, QTreeWidgetItem* parent = nullptr, QTabWidget* pTabWidget = nullptr) {QString itemName = element.attribute("name");QString reflectClass = element.attribute("ReflectClass");reflectClass.append("*");QTreeWidgetItem* item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem();item->setText(0, itemName);if (!reflectClass.isEmpty()) {const QMetaObject* metaObject = QMetaType::metaObjectForType(QMetaType::type(reflectClass.toUtf8()));if (metaObject) {QWidget* widget = qobject_cast<QWidget*>(metaObject->newInstance(Q_ARG(QWidget*, nullptr)));if (widget) {// 这里可以将 widget 挂载到树节点上,例如通过设置 item 的 dataitem->setData(0, Qt::UserRole, QVariant::fromValue(widget));pTabWidget->addTab(widget, itemName);}}}// 递归处理子节点QDomNodeList childNodes = element.elementsByTagName("TwoLevelItem");for (int i = 0; i < childNodes.count(); ++i) {QDomElement childElement = childNodes.at(i).toElement();createTreeItemFromXml(childElement, treeWidget, item, pTabWidget);}return item;
}// 解析 XML 文件并创建 CPageTreeBase
QTreeWidget* createTreeWidgetFromXml(const QString& xmlFilePath, QTabWidget* pTabWidget) {if(!pTabWidget){return nullptr;}QFile file(xmlFilePath);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {qDebug() << "Failed to open XML file:" << xmlFilePath;return nullptr;}QDomDocument doc;if (!doc.setContent(&file)) {qDebug() << "Failed to parse XML file:" << xmlFilePath;file.close();return nullptr;}file.close();QDomElement root = doc.documentElement();QString reflectClass = root.attribute("ReflectClass");reflectClass.append("*"); // 追加指针符号const int metaTypeId = QMetaType::type(reflectClass.toUtf8());if (metaTypeId == QMetaType::UnknownType) {qDebug() << "Unknown reflect class:" << reflectClass;return nullptr;}const QMetaObject* metaObject = QMetaType::metaObjectForType(metaTypeId);if (!metaObject) {qDebug() << "No meta object for reflect class:" << reflectClass;return nullptr;}QTreeWidget* treeWidget = qobject_cast<QTreeWidget*>(metaObject->newInstance(Q_ARG(QWidget*, nullptr)));if(!treeWidget){qDebug() << "newInstance fail: treeWidget " << reflectClass;return nullptr;}treeWidget->setHeaderLabel(root.attribute("name"));QDomNodeList itemNodes = root.elementsByTagName("OneLevelItem");qDebug() << "itemNodes.count(): " <<  itemNodes.count();for (int i = 0; i < itemNodes.count(); ++i) {QDomElement itemElement = itemNodes.at(i).toElement();QTreeWidgetItem* item = createTreeItemFromXml(itemElement, treeWidget, nullptr, pTabWidget);treeWidget->addTopLevelItem(item);}return treeWidget;
}

四、主要功能完整实现

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "CommunConfigWidget.h"
#include "YKInsWidget.h"
#include "PageTreeBase.h"
#include <QMetaObject>
#include <QMetaType>
#include <QDomDocument>
#include <QMetaMethod>
#include <QDebug>
#include <QVBoxLayout>// 根据 XML 节点递归创建 QTreeWidgetItem 并挂载部件
QTreeWidgetItem* createTreeItemFromXml(const QDomElement& element, QTreeWidget* treeWidget, QTreeWidgetItem* parent = nullptr, QTabWidget* pTabWidget = nullptr) {QString itemName = element.attribute("name");QString reflectClass = element.attribute("ReflectClass");reflectClass.append("*");QTreeWidgetItem* item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem();item->setText(0, itemName);if (!reflectClass.isEmpty()) {const QMetaObject* metaObject = QMetaType::metaObjectForType(QMetaType::type(reflectClass.toUtf8()));if (metaObject) {QWidget* widget = qobject_cast<QWidget*>(metaObject->newInstance(Q_ARG(QWidget*, nullptr)));if (widget) {// 这里可以将 widget 挂载到树节点上,例如通过设置 item 的 dataitem->setData(0, Qt::UserRole, QVariant::fromValue(widget));pTabWidget->addTab(widget, itemName);}}}// 递归处理子节点QDomNodeList childNodes = element.elementsByTagName("TwoLevelItem");for (int i = 0; i < childNodes.count(); ++i) {QDomElement childElement = childNodes.at(i).toElement();createTreeItemFromXml(childElement, treeWidget, item, pTabWidget);}return item;
}// 解析 XML 文件并创建 CPageTreeBase
QTreeWidget* createTreeWidgetFromXml(const QString& xmlFilePath, QTabWidget* pTabWidget) {if(!pTabWidget){return nullptr;}QFile file(xmlFilePath);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {qDebug() << "Failed to open XML file:" << xmlFilePath;return nullptr;}QDomDocument doc;if (!doc.setContent(&file)) {qDebug() << "Failed to parse XML file:" << xmlFilePath;file.close();return nullptr;}file.close();QDomElement root = doc.documentElement();QString reflectClass = root.attribute("ReflectClass");reflectClass.append("*"); // 追加指针符号const int metaTypeId = QMetaType::type(reflectClass.toUtf8());if (metaTypeId == QMetaType::UnknownType) {qDebug() << "Unknown reflect class:" << reflectClass;return nullptr;}const QMetaObject* metaObject = QMetaType::metaObjectForType(metaTypeId);if (!metaObject) {qDebug() << "No meta object for reflect class:" << reflectClass;return nullptr;}QTreeWidget* treeWidget = qobject_cast<QTreeWidget*>(metaObject->newInstance(Q_ARG(QWidget*, nullptr)));if(!treeWidget){qDebug() << "newInstance fail: treeWidget " << reflectClass;return nullptr;}treeWidget->setHeaderLabel(root.attribute("name"));QDomNodeList itemNodes = root.elementsByTagName("OneLevelItem");qDebug() << "itemNodes.count(): " <<  itemNodes.count();for (int i = 0; i < itemNodes.count(); ++i) {QDomElement itemElement = itemNodes.at(i).toElement();QTreeWidgetItem* item = createTreeItemFromXml(itemElement, treeWidget, nullptr, pTabWidget);treeWidget->addTopLevelItem(item);}return treeWidget;
}MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);qRegisterMetaType<CPageTreeBase*>("CPageTreeBase*");qRegisterMetaType<CCommunConfigWidget*>("CCommunConfigWidget*");qRegisterMetaType<CYKInsWidget*>("CYKInsWidget*");// 获取主窗口的中央部件QWidget* centralWidget = this->centralWidget();// 检查中央部件是否有布局QHBoxLayout* layout = qobject_cast<QHBoxLayout*>(centralWidget->layout());if (!layout) {// 如果没有布局,创建一个垂直布局并设置到中央部件layout = new QHBoxLayout(centralWidget);centralWidget->setLayout(layout);}// 将树控件添加到布局中pTabWiget = new QTabWidget;CPageTreeBase* treeWidget = qobject_cast<CPageTreeBase*>(createTreeWidgetFromXml("D:\\Project\\Test\\test3\\CGCUi.xml", pTabWiget));if (treeWidget) {connect(treeWidget, &CPageTreeBase::itemClicked, this, &MainWindow::SlotTreeItemClicked);layout->addWidget(treeWidget);}layout->addWidget(pTabWiget);}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::SlotTreeItemClicked(QTreeWidgetItem* item, int column)
{QVariant widgetVariant = item->data(0, Qt::UserRole);if (widgetVariant.isValid()) {QWidget* widget = widgetVariant.value<QWidget*>();if (widget) {// 切换 QTabWidget 的当前页pTabWiget->setCurrentWidget(widget);}}
}

六、总结

简易的通过qt的元对象系统,反射创建类,可应用到项目中通过xml,对界面进行删改。当一个界面插件实现使用该方法有奇效。

相关文章:

  • JDK动态代理和CGLIB动态代理的区别?
  • suricata增加单元测试编译失败
  • 3DGS-to-PC:3DGS模型一键丝滑转 点云 or Mesh 【Ubuntu 20.04】【2025最新版!!】
  • STM32 变量存储
  • window 显示驱动开发-指定 DMA 缓冲区的段
  • 2.3 定积分
  • 恰到好处TDR
  • #在 CentOS 7 中手动编译安装软件操作及原理
  • c#修改ComboBox当前选中项的文本
  • ExcelJS库的使用
  • vue2/3 中使用 @vue-office/docx 在网页中预览(docx、excel、pdf)文件
  • 关于nextjs中next-sitemap插件生成文件样式丢失问题及自定义样式处理
  • vue的table表格选择回显不显示
  • vue H5解决安卓手机软键盘弹出,页面高度被顶起
  • 网卡网孔速率的协商是如何进行的?
  • 【小记】word批量生成准考证
  • element中表格文字剧中可以使用的属性
  • 初识 Pandas:Python 数据分析的利器
  • 七、MyBatis-Plus高级用法:最优化持久层开发
  • 力扣2094题解
  • 西王食品连亏三年:主业齐“崩”,研发人员多为专科生
  • 对话郑永年:我们谈判也是为世界争公义
  • 60余年产业积累,“江苏绿心”金湖炼就“超级石油工具箱”
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?
  • 瑞士联邦主席凯勒-祖特尔、联邦副主席帕姆兰会见何立峰
  • 墨西哥宣布就“墨西哥湾”更名一事起诉谷歌