
OpenUSD PrimHints 权威指南用 uiHints 控制 Prim 在 UI 中的显示分组与条件展示【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSDPrimHints 是 OpenUSDUniversal Scene Description中一类面向 Prim 级别的 UI 提示UI Hints机制用于告诉 DCC 工具或应用程序Prim 下的属性应当如何按显示分组display groups组织、哪些分组默认展开或折叠、以及哪些分组在什么条件下才显示。本文将以 docs/user_guides/schemas/usdUI/PrimHints.md 为骨架结合仓库中pxr/usd/usdUI的源码实现与测试用例系统讲解 PrimHints 的字段语义、USDA 编写方式、Python API 用法以及与ObjectHints、PropertyHints的协作关系。读完本文你将能够为任意 Prim 编写一套可被工具直接消费的 UI 呈现规范。图注一个 mock 场景浏览器中应用 PrimHints 后的 UI 效果示意其中 Controller 分组默认展开Widget Settings 分组默认折叠详见下文示例。PrimHints 的定位UI Hints 体系中的 Prim 级入口在 OpenUSD 的 UsdUI 域中UI Hints 是一组类 schemaschema-like的元数据约定它们统一存放在对象的uiHints字典元数据metadata中用于描述一个对象在 UI 中的呈现方式。UI Hints 按作用对象划分为四层见 docs/user_guides/schemas/usdUI/overview.mdObjectHints适用于任何 Prim 或属性Property的通用提示如用户可见的displayName、是否在 UI 中隐藏的hidden。PrimHints本文主题Prim 级提示例如displayGroupsExpanded哪些显示分组默认展开与displayGroupsShownIf哪些显示分组在什么条件下才显示。PropertyHints属性级提示如属性归属的displayGroup、控制属性是否显示的shownIf表达式。AttributeHints属性值级提示如枚举值标签valueLabels及其顺序valueLabelsOrder。从源码看PrimHints 对应 C 类UsdUIPrimHints定义于 pxr/usd/usdUI/primHints.h其继承自UsdUIObjectHintspxr/usd/usdUI/objectHints.h即 Prim 天然同时具备 ObjectHints 的能力。UsdUIPrimHints的注释明确指出它是schema-like包装器解释UsdPrim实例上uiHints字典中的字段并提供便捷 API并非正式 schema不派生自UsdSchemaBase。文档 PrimHints.md 开头也强调该文件不是自动生成的文件头部注释注明 This file isnotgenerated, as UIHints are not defined in schema.usda。也就是说displayGroupsExpanded、displayGroupsShownIf这类 PrimHints 字段并未像普通 schema 属性那样定义在 pxr/usd/usdUI/schema.usda 中而是由UsdUIPrimHints等 API 按约定解释uiHints字典键值。字典键的 token 定义在 pxr/usd/usdUI/objectHints.h 的USDUI_HINT_KEYS宏中包括uiHints、displayName、displayGroup、hidden、shownIf、valueLabels、valueLabelsOrder、displayGroupsExpanded、displayGroupsShownIf。一个直观的最小示例PrimHints 文档给出了如下示例一个名为ControllerA的 Prim拥有多个属性并分属两个显示分组 Controller 和 Widget Settings。Prim 级uiHints指出DCC 工具初始应完整展开 Controller 分组、不展开 Widget Settings 分组且 Widget Settings 仅在表达式widgetReadOnlyMode 0求值为真时才显示。def ControllerA ( uiHints { # UI hints from ObjectHints string displayName ControllerA bool hidden 0 dictionary displayGroupsExpanded { bool Controller 1 bool Widget Settings 0 } dictionary displayGroupsShownIf { string Widget Settings widgetReadOnlyMode 0 } } ) { float controlValue 1.0 ( uiHints { string displayName value string displayGroup Controller } ) bool controlIsOffset false ( uiHints { string displayName is offset string displayGroup Controller } ) bool showWidget true ( uiHints { string displayName show string displayGroup Widget Settings } ) color3f[] widgetColor (1.0, 0.5, 0.5) ( uiHints { string displayName color string displayGroup Widget Settings } ) float widgetSize 10.0 ( uiHints { string displayName size string displayGroup Widget Settings } ) bool widgetReadOnlyMode 0 }注意displayName与hidden属于 ObjectHints在uiHints字典内以注释标明来源displayGroup属于 PropertyHints标注每个属性归入哪个显示分组。PrimHints 的两组字典则控制这些分组在 UI 中的整体呈现。文档同时给出了该 Prim 在场景浏览器中的 mock 效果图uihints-primhints.svg其中widgetReadOnlyMode为false即 0时Widget Settings 分组可见。PrimHints 字段详解displayGroupsExpandedUSD 类型dictionary以显示分组名称为键的字典指示各显示分组在 UI 中默认是展开1/true还是折叠0/false。未在字典中列出的分组工具可自行决定默认状态见下文源码默认值分析。对应 C API 位于 pxr/usd/usdUI/primHints.hVtDictionary GetDisplayGroupsExpanded() const返回整个展开字典bool SetDisplayGroupsExpanded(const VtDictionary expanded)整体写入展开字典bool GetDisplayGroupExpanded(const std::string group) const查询单个分组是否默认展开bool SetDisplayGroupExpanded(const std::string group, bool expanded)设置单个分组的展开状态。从 pxr/usd/usdUI/primHints.cpp 的实现可以看到几个关键细节读取通过_prim.GetMetadataByDictKey(UsdUIHintKeys-UIHints, UsdUIHintKeys-DisplayGroupsExpanded, dict)从uiHints字典中按键取子字典若未authoring返回空字典。写入校验SetDisplayGroupsExpanded会遍历字典所有条目若存在非bool值则通过TF_CODING_ERROR报错并拒绝写入测试用例 testUsdUIHints.py 中也验证了传非 bool 值的错误路径。单分组查询默认值GetDisplayGroupExpanded使用VtDictionaryGetbool(expandedDict, group, VtDefault false)即未authoring 的分组默认按false折叠处理。测试同样断言了GetDisplayGroupExpanded(non-existent)返回False见 testUsdUIHints.py。嵌套分组的扁平化存储SetDisplayGroupExpanded特意不使用SetMetadataByDictKey直接写单键而是先读整字典、改条目、再整字典写回。源码注释解释得很清楚因为分组名本身可能含冒号分隔符如A:B:C直接按字典键写入会把它们变成嵌套子字典而我们希望所有分组条目平铺在displayGroupsExpanded顶层dictionary displayGroupsExpanded { bool A 1 bool A:B 1 bool A:B:C 1 }而不是dictionary displayGroupsExpanded { dictionary A { dictionary B { bool C 1 } } }组合语义该字段是字典值其合成值composed value是所有相关编辑目标edit targets中条目级per-entry覆盖合并的结果而非整个字典整体覆盖。这一语义同样适用于displayGroupsShownIf在头文件注释中明确说明。displayGroupsShownIfUSD 类型dictionary以显示分组名称为键的字典键对应的值是基于SdfBooleanExpressionpxr/usd/sdf/booleanExpression.h的表达式字符串。表达式求值为真时对应显示分组才在 UI 中显示。对应 C APIpxr/usd/usdUI/primHints.hVtDictionary GetDisplayGroupsShownIf() constbool SetDisplayGroupsShownIf(const VtDictionary shownIf)std::string GetDisplayGroupShownIf(const std::string group) constbool SetDisplayGroupShownIf(const std::string group, const std::string shownIf)。primHints.cpp 中SetDisplayGroupsShownIf的写入校验要求所有值必须是std::string否则TF_CODING_ERROR拒绝写入。测试用例 testUsdUIHints.py 中同样覆盖了给DisplayGroupsShownIf传 int 值报错的场景。与displayGroupsExpanded不同GetDisplayGroupShownIf/SetDisplayGroupShownIf的单分组读写使用_MakeKeyPath(UsdUIHintKeys-DisplayGroupsShownIf, TfToken(group))拼接键路径_MakeKeyPath以命名空间分隔符:连接见 pxr/usd/usdUI/objectHints.h即直接按displayGroupsShownIf:group字典键读写。测试验证未设置的分组GetDisplayGroupShownIf返回空字符串。显示分组Display Groups与条件表达式的完整语义PrimHints 并非孤立存在它与显示分组机制、布尔表达式机制紧密耦合。以下语义出自 overview.md 的 Display Groups and Property Order 与 Working With Conditional UI Hints 两节是理解 PrimHints 两字段的前提。分组归属与嵌套属性通过 PropertyHints 的displayGroup声明自己归属的显示分组PropertyHints.md一个属性只能属于一个分组。分组可嵌套分组名中使用:分隔符例如GroupA:NestedGroup表示NestedGroup是GroupA的子分组。分组是可选的但能帮助工具把相关属性聚合展示、提升操作效率。Python API 设置分组归属property prim.GetProperty(myProperty) hints UsdUI.PropertyHints(property) hints.SetDisplayGroup(Custom Properties)分组呈现由 Prim 控制Prim 通过displayGroupsExpanded和displayGroupsShownIf控制分组在 UI 中的呈现这正是 PrimHints 的核心职责。一个更丰富的综合示例来自 overview.mddef TreeA ( uiHints { string displayName Tree template dictionary displayGroupsExpanded { bool Trunk settings 1 bool Body settings 1 bool Body settings:Branch settings 0 bool Body settings:Leaf settings 1 } dictionary displayGroupsShownIf { string Body settings:Leaf settings trunkSize ! 1 } } ) { color3f trunkColor (0.6, 0.3, 0.0) ( uiHints { string displayName color string displayGroup Trunk settings } ) int trunkSize 2 ( uiHints { string displayName size string displayGroup Trunk settings dictionary valueLabels { int huge 3 int sapling 1 int standard 2 } token[] valueLabelsOrder [sapling, standard, huge] } ) float bodyRadius 5.0 ( uiHints { string displayName size string displayGroup Body settings } ) float branchDensity 1.0 ( uiHints { string displayName density string displayGroup Body settings:Branch settings } ) float branchLength 5.0 ( uiHints { string displayName length string displayGroup Body settings:Branch settings } ) color3f leafColor (0.4, 0.7, 0.25) ( uiHints { string displayName color string displayGroup Body settings:Leaf settings } ) float leafComplexity 1.0 ( uiHints { string displayName complexity string displayGroup Body settings:Leaf settings string shownIf leafStyle 1 } ) int leafStyle 1 ( uiHints { string displayName style string displayGroup Body settings:Leaf settings dictionary valueLabels { int acute 1 int obtuse 2 int truncate 3 } token[] valueLabelsOrder [acute, obtuse, truncate] } ) string tempNotes ( uiHints { bool hidden 1 } ) reorder properties [trunkColor, trunkSize, bodyRadius, branchDensity, branchLength, leafColor, leafStyle, leafComplexity] }这个例子展示了 UI Hints 四层协同ObjectHintsdisplayName、hidden、PropertyHintsdisplayGroup、shownIf、AttributeHintsvalueLabels、valueLabelsOrder与 PrimHintsdisplayGroupsExpanded、displayGroupsShownIf。其中Body settings:Leaf settings分组同时受displayGroupsShownIf表达式trunkSize ! 1和组内属性leafComplexity的shownIfleafStyle 1双重条件控制。该示例的 UI mock 见 uihints-example-mock.svg。属性顺序对分组的影响Prim 通过reorder properties或Usd.Prim.SetPropertyOrder()控制属性在 UI 中的顺序。带显示分组时工具应按属性首次引用分组的位置排列分组并按 Prim 属性顺序排列组内属性。例如def PropertyOrderPrimWithDisplayGroups ( uiHints { string displayName Example dictionary displayGroupsExpanded { bool Group A 1 bool Group B 1 } } ) { reorder properties [attribute4, attribute2, attribute1, attribute3] int attribute1 1 ( uiHints { string displayGroup Group B } ) int attribute2 2 int attribute3 3 ( uiHints { string displayGroup Group B } ) int attribute4 4 ( uiHints { string displayGroup Group A } ) }其 UI 呈现顺序的 mock 图见 uihints-propertyorder.svg。另外注意旧的displayGroupOrderPrim 元数据字段已废弃不应与属性顺序/分组相关 UI Hints 混用。布尔表达式语法与求值语义displayGroupsShownIf的值以及 PropertyHints 的shownIf是布尔表达式字符串求值基于SdfBooleanExpression。表达式通常用于测试包含该 Prim 的某个属性的合成值resolved value。支持的操作符见 overview.md 与 booleanExpression.h 中的运算符枚举文档操作符含义等于!不等于小于小于等于大于大于等于逻辑与\|\|逻辑或!一元逻辑非表达式支持一元!与括号分组。例如!(status active || level 5)表示仅当status不等于active且level小于等于 5 时才显示。条件表达式示例overview.mddef PrimUsingExpressions ( uiHints { dictionary displayGroupsShownIf { string Deformation parameters materialHardness 2.0 } } ) { float bendAmount 0.0 ( uiHints { string displayGroup Deformation parameters string displayName Bend amount } ) float bendDirection 0.0 ( uiHints { string displayGroup Deformation parameters string displayName Bend direction } ) float fractureAmount 0.0 ( uiHints { string displayGroup Deformation parameters string displayName Fracture amount string shownIf isFractured true } ) float materialHardness 10.0 bool isFractured false }这里Deformation parameters分组的显示条件是materialHardness 2.0而组内fractureAmount属性还要满足isFractured true才会显示。visibility 判定规则对象级hidden提示始终与shownIf一起参与判定。即属性在 UI 中可见当且仅当shownIf表达式求值为真且hidden不为真。这一规则同样适用于分组displayGroupsShownIf表达式为假或分组Prim的hidden为真都会导致分组不可见。用 API 读写 PrimHintsC / PythonC API构造UsdUIPrimHints hints(prim);读取/写入见上文字段对应 API。要点未authoring 时GetDisplayGroupsExpanded()返回空VtDictionaryGetDisplayGroupExpanded(group)返回false默认折叠GetDisplayGroupShownIf(group)返回空字符串。写入字典时displayGroupsExpanded要求全部bool值、displayGroupsShownIf要求全部string值否则报TF_CODING_ERROR。字典字段的合成值按条目覆盖per-entry override合并跨编辑目标时不是整体替换。Python APIUI Hints 的 Python 绑定在 pxr/usd/usdUI 目录的wrap*文件中如 wrapPrimHints.cpp。典型用法from pxr import Usd, UsdUI stage Usd.Stage.CreateInMemory() prim stage.DefinePrim(/MyPrim) hints UsdUI.PrimHints(prim) hints.SetDisplayGroupsExpanded({Controller: True, Widget Settings: False}) hints.SetDisplayGroupShownIf(Widget Settings, widgetReadOnlyMode 0) print(hints.GetDisplayGroupExpanded(Controller)) # True print(hints.GetDisplayGroupShownIf(Widget Settings)) # widgetReadOnlyMode 0推荐使用 API 而非直接读写uiHints元数据API 在未authoring 时提供合理的回退值。overview.md 中给出的例子对一个uiHints为空的 PrimUsdUI.ObjectHints(prim).GetDisplayName()返回空字符串而直接prim.GetMetadata(uiHints).get(displayName)返回None。只有自定义 UI hint 键等场景才需要直接访问uiHints字典。兼容性与迁移注意overview.md 明确指出displayName、hidden、displayGroup过去是独立的元数据字段通过UsdObject/UsdProperty提供现已废弃。UI Hints API 在uiHints字典中未authoring 对应值时会回退查找这些旧字段以保持向后兼容但新内容不应再author 这些独立字段应统一使用 UI Hints API 或uiHints字典。这一回退行为在 objectHints.h 与 propertyHints.h 的注释中被标注为临时的将在未来版本移除。测试验证从 testUsdUIHints.py 看行为契约仓库测试 pxr/usd/usdUI/testenv/testUsdUIHints.py 直接覆盖了 PrimHints 的关键行为契约GetDisplayGroupsExpanded()返回与写入一致的字典GetDisplayGroupExpanded(k)对每个键返回对应值对不存在的分组non-existent返回False默认折叠。GetDisplayGroupsShownIf()/GetDisplayGroupShownIf(k)同理不存在的分组返回空字符串。向SetDisplayGroupsShownIf传入非字符串值如{group: 5}会触发错误拒绝写入。对group:subgroup这类嵌套分组名SetDisplayGroupExpanded(group, True)后GetDisplayGroupExpanded(group)与GetDisplayGroupExpanded(group:subgroup)均独立生效且整字典按平铺结构读取——印证了上文嵌套分组名平铺存储的实现细节。未authoring 的 Prim其 PrimHints 相关读取均返回空字典/False/空字符串验证了默认回退行为。测试同时覆盖了 ObjectHintsdisplayName、hidden与 PropertyHintsdisplayGroup、shownIf的回退与读写可用于在改动 UI Hints 行为后做回归验证。小结PrimHints 通过displayGroupsExpanded与displayGroupsShownIf两个字典字段把显示分组如何呈现的决定权交给数据本身前者声明各分组默认展开/折叠状态后者用SdfBooleanExpression表达式条件化控制分组可见性。它与 ObjectHintsdisplayName/hidden、PropertyHintsdisplayGroup/shownIf、AttributeHintsvalueLabels/valueLabelsOrder共同构成完整的 UI Hints 体系全部承载在uiHints字典元数据中由UsdUIPrimHints等类 schema API 解释读写。需要强调的边界UI Hints 本质是建议suggestions最终呈现方式由消费它的工具或应用程序决定overview.md 明确说明。因此在 DCC 工具、资产浏览器或节点编辑器中消费这些提示时应把 PrimHints 作为默认布局的输入同时允许用户覆盖。本文所有字段语义、默认值未设置分组默认折叠、未设置表达式默认显示、类型校验与兼容回退行为均可在 primHints.h、primHints.cpp 与 testUsdUIHints.py 中找到直接依据。【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考