# QDetailsView QDetailsView 受虚幻引擎的属性面板所启发,借助Qt的反射系统,可以轻易地搭建对象的属性编辑器。 其核心在于: - 编写基于类型的控件编辑器,自动根据对象的反射结构来组织编辑器布局。 - 使用QML GPU渲染,基于预览视图的控件管理。 ## 使用 它的使用非常简单,只需要使用 **Q_PROPERTY(...) ** 声明 `QObject` 的元属性: ``` c++ class QCustomObject :public QObject { Q_OBJECT Q_PROPERTY(int Int READ getInt WRITE setInt) Q_PROPERTY(float Float READ getFloat WRITE setFloat) ... }; ``` 然后创建 `QDetailsView`,并指定Object: ``` c++ QCustomObject obj; QDetailsView view; view.setObject(&obj); view.show(); ``` 你就能得到: ![image-20250826114654194](resources/image-20250826114654194.png) ## 定制化 ### 关于 QPropertyHandle `QPropertyHandle` 是QDetailsView操作属性的统一入口,它通常通过如下接口构建: ``` c++ static QPropertyHandle* QPropertyHandle::FindOrCreate( QObject* inParent, // 父对象,它会持有PropertyHandle周期 QMetaType inType, // 该属性的元类型 QString inPropertyPath, // 该属性的路径字段 Getter inGetter, // 该属性的获取器 Setter inSetter // 该属性的设置器 ); ``` 为了保证属性值的变动可以被DetailsView响应,所有对值的修改请统一使用PropertyHandle的接口: ``` C++ QPropertyHandle* handle = QPropertyHandle::Find(object, "propertyName"); if(handle){ handle->setVar(QVariant::fromValue(newValue)); } ``` `QPropertyHandle`创建时要求指定`parent`,它将会作为子对象挂载上去,因此它的生命周期将跟随父对象,如果要清理,请调用: ``` c++ static void QPropertyHandle::Cleanup(QObject* inParent); ``` ### 自定义枚举 对于枚举类型,需要使用类内定义的方式,并通过**Q_ENUM(...)**声明: ``` c++ class QCustomObject: public QObject { Q_OBJECT public: enum QCustomEnum { One, Two, Three }; Q_ENUM(QCustomEnum); }; ``` ### 自定义类型编辑器 非`QObject`的自定义类型,首先需要在定义类型时使用宏**Q_DECLARE_METATYPE(...)**进行声明。 对于只有单个编辑器控件的特定类型,可以使用如下接口直接注册类型编辑器: ``` c++ QQuickDetailsViewManager::Get()->registerTypeEditor( metaType, [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { } ); ``` 在源代码的`QQuickDetailsViewBasicTypeEditor.cpp`目录下有许多参考示例,比如QDir: ``` c++ registerTypeEditor( QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* { QQmlEngine* engine = qmlEngine(parent); QQmlContext* context = qmlContext(parent); QQmlComponent comp(engine); comp.setData(R"( import QtQuick; import QtQuick.Controls; import "qrc:/resources/Qml/ValueEditor" DirectorySelector{ anchors.verticalCenter: parent.verticalCenter width: parent.width } )", QUrl()); QVariantMap initialProperties; initialProperties["parent"] = QVariant::fromValue(parent); auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context)); if (!comp.errors().isEmpty()) { qDebug() << comp.errorString(); } valueEditor->setParentItem(parent); valueEditor->setProperty("value", handle->getVar()); connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant))); connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant))); return valueEditor; } ); ``` 对于具有多行或者行为较为复杂的编辑器控件,可以通过派生`IPropertyTypeCustomization`来扩展属性编辑器,它提供两个虚函数: ``` c++ class IPropertyTypeCustomization :public QEnableSharedFromThis { public: // 用于装配当前属性行的编辑器 virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder); // 用于扩展当前属性行的子项 virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder); }; ``` 之后使用如下接口进行注册: ``` c++ QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization(); ``` ## TODO - 撤销重做 - 容器操作 - 扩展元信息