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,对界面进行删改。当一个界面插件实现使用该方法有奇效。