DiagramDesigner/PropertyEditor/README_zh.md

160 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<QDir>(),
[](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<QQuickItem*>(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<IPropertyTypeCustomization>
{
public:
// 用于装配当前属性行的编辑器
virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder);
// 用于扩展当前属性行的子项
virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder);
};
```
之后使用如下接口进行注册:
``` c++
QQuickDetailsViewManager::Get()->registerPropertyTypeCustomization<QCustomType, PropertyTypeCustomization_CustomType>();
```
## TODO
- 撤销重做
- 容器操作
- 扩展元信息