news 2026/9/17 3:24:12

OpenUSD 属性级 UI 提示(PropertyHints)完全指南:displayGroup 分组与 shownIf 条件显隐

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenUSD 属性级 UI 提示(PropertyHints)完全指南:displayGroup 分组与 shownIf 条件显隐

OpenUSD 属性级 UI 提示(PropertyHints)完全指南:displayGroup 分组与 shownIf 条件显隐

【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD

PropertyHints(属性级 UI 提示)是 OpenUSD 的 UsdUI 域中专门用于描述"属性(attribute 与 relationship)在 DCC 工具或应用 UI 中如何呈现"的元数据机制,核心解决两个问题:属性归入哪个显示分组(displayGroup),以及基于表达式条件控制属性是否显示(shownIf)。阅读完本文,你将掌握在.usda中编写 PropertyHints、通过UsdUI.PropertyHintsAPI 读写这些提示、利用:分隔符构建嵌套显示组,以及用SdfBooleanExpression布尔表达式实现属性动态显隐的完整实战方案。

PropertyHints 是什么

PropertyHints 是 OpenUSD 中描述属性(property)级 UI 呈现方式的一组"类 schema"(schema-like)API 与元数据约定。在 PropertyHints.md 中,它的定位被明确为:为在 UI 中呈现的属性提供 UI 提示,覆盖属性所属的显示分组(display group,如果有)以及条件控制属性是否显示在 UI 中的表达式

PropertyHints 属于 UsdUI 的 UI Hints 体系中的属性层级。完整的 UI Hints 分组包括(见 overview.md):

  • ObjectHints:适用于任意 prim 或属性的通用提示,如显示名(displayName)、是否隐藏(hidden);
  • PrimHints:prim 级提示,如显示组的展开/折叠状态与显示条件;
  • PropertyHints:属性级提示,如属性所属的显示分组、属性的显示条件;
  • AttributeHints:仅属性(attribute)级提示,如属性值标签及标签的显示顺序。

文档特别指出:没有专属于 relationship 的 "RelationshipHints" 分组,因为没有任何提示是 relationship 独有的——relationship 使用与 attribute 相同的 PropertyHints(测试 testUsdUIHints.py 中的test_RelationshipHintsFromAsset正是用UsdUI.PropertyHints(rel)读取 relationship 的displayGroupshownIf)。

在 UsdUI 的 schema 设计上,UI Hints 并非schema.usda中正式定义的 schema 类,而是直接以名为uiHints的**字典类型元数据(metadata dictionary)**落在 prim / property 之上。因此 PropertyHints.md 开头就注明该文件不是从schema.usda生成的。UsdUIPropertyHints也明确标注为 "schema-like" wrapper——它解释UsdProperty上的uiHints字典字段并提供便捷 API,但并不继承UsdSchemaBase(见 propertyHints.h)。

UI hints 只是对 UI 呈现方式的"建议"(suggestions),最终呈现方式由实现 UI 的工具或应用决定(见 [overview.md](https://link.gitcode.com/i/032e85633f96fdb72a221715c36f4721#L162-L166))。

一个完整的 PropertyHints 示例

原文档给出的基础示例如下:一个 prim 上的多个属性各自指定了显示分组,其中同时演示了 attribute 与 relationship 两种 property 的写法:

def "PrimWithPropertyHints" ( uiHints = { string displayName = "Example prim" bool hidden = 0 } ) { int exampleAttribute = 1 ( uiHints = { string displayName = "An example attribute" bool hidden = 0 string displayGroup = "Display Group 1" string shownIf = "showProperties == 1" } ) rel exampleRelationship ( uiHints = { string displayName = "An example relationship" bool hidden = 0 string displayGroup = "" string shownIf = "showProperties == 1" } ) bool showProperties = true }

要点拆解:

  • displayNamehidden属于 ObjectHints,对所有对象通用(详见 ObjectHints.md);
  • displayGroupshownIf是本篇主角 PropertyHints;
  • relationship 与 attribute 使用完全一致的uiHints字典结构;
  • 注意showProperties是属性shownIf表达式引用的"变量",其解析值是整个表达式的求值输入(见下文"布尔表达式求值")。

PropertyHints 字段详解

displayGroup(显示分组)

USD 类型string

displayGroup指定属性所属的显示分组名称。一个属性只能属于一个显示分组。显示分组是可选的,但把相关属性聚拢到同一分组能显著提升 UI 中的查找效率。

嵌套分组:通过在分组名中使用:分隔符声明"嵌套"显示组。例如displayGroup设为"GroupA:NestedGroup"的属性,属于 "NestedGroup" 显示组,而 "NestedGroup" 是 "GroupA" 的子组。这一语法与 PrimHints 的displayGroupsExpanded/displayGroupsShownIf完全一致,可对照 PrimHints.md 中的示例(如"Body settings:Branch settings")以及测试资产 hints.usda 中的displayGroup = "a group:a nested group"来加深理解。

空字符串的特殊含义displayGroup = ""表示属性不归入任何显示组,直接落在分组之外的顶层属性列表中。

底层实现:从 propertyHints.cpp 可以看到,GetDisplayGroup()首先通过GetMetadataByDictKey(UsdUIHintKeys->UIHints, UsdUIHintKeys->DisplayGroup, &group)读取uiHints.displayGroup;若字典中没有该键,则回退(fallback)到已弃用的UsdProperty::GetDisplayGroup()旧式字段。这一回退行为是临时的,将在未来版本移除。SetDisplayGroup()则始终写入uiHints字典,不再调用已弃用的UsdObject::SetDisplayGroup();只有当环境变量USDUI_WRITE_LEGACY_UI_HINTS开启时,才会同步写入旧字段(见 propertyHints.cpp 与 objectHints.h)。

shownIf(显示条件表达式)

USD 类型string

shownIf是一个字符串形式的布尔表达式,用于控制属性是否显示在 UI 中:表达式求值为true时显示,否则在 UI 中省略。该表达式基于 SdfBooleanExpression 实现,通常用来测试包含该属性的 prim 上某个已解析属性(attribute)的值

hidden的协作shownIf与对象级(object-level)的hidden提示同时参与属性可见性判定。即:只要shownIf求值为false或者hiddentrue,属性就不可见(见 overview.md 与 ObjectHints.md)。

底层实现GetShownIf()/SetShownIf()同样围绕uiHints字典中的shownIf键读写(见 propertyHints.cpp)。与displayGroup不同,shownIf没有旧式字段可回退,未授权时返回空字符串。

使用 API 读写 PropertyHints

虽然uiHints是普通元数据字典,可以直接读写,但官方推荐始终使用 API,原因有二:API 在提示未授权时提供合理的回退值,且封装了键路径与类型检查。

Python API

from pxr import Usd, UsdUI stage = Usd.Stage.CreateInMemory() prim = stage.DefinePrim('/MyPrim') attr = prim.CreateAttribute('myProperty', Sdf.ValueTypeNames.Int) # 获取属性级 UI 提示 hints = UsdUI.PropertyHints(attr) # 读取(未授权时返回回退值:空字符串) print(hints.GetDisplayGroup()) # '' print(hints.GetShownIf()) # '' # 写入 hints.SetDisplayGroup("Custom Properties") hints.SetShownIf("myFlag == 1") # 再次读取 print(hints.GetDisplayGroup()) # 'Custom Properties'

UsdUI.PropertyHints接受任意UsdProperty(attribute 或 relationship 均可)。Python 绑定见 wrapPropertyHints.cpp,其构造器签名为PropertyHints(prop),并暴露GetPropertyGetDisplayGroupSetDisplayGroupGetShownIfSetShownIf六个方法。

C++ API

#include "pxr/usd/usdUI/propertyHints.h" #include "pxr/usd/usd/property.h" UsdProperty prop = ...; // 例如 prim.GetProperty(TfToken("myProperty")) UsdUIPropertyHints hints(prop); std::string group = hints.GetDisplayGroup(); // 未授权时回退到旧式字段或空串 std::string shownIf = hints.GetShownIf(); hints.SetDisplayGroup("Custom Properties"); // 始终写入 uiHints 字典 hints.SetShownIf("myFlag == 1");

回退值(Fallback)行为

在 testUsdUIHints.py 的test_Fallbacks中验证了:对未授权任何提示的属性(/HintlessPrim.hintlessAttribute)调用PropertyHintsGetDisplayGroup()返回''GetShownIf()返回'';对默认构造的UsdUI.PropertyHints()(无效对象),get 操作同样返回回退值,而 set 操作会抛出RuntimeError(见test_InvalidHints,testUsdUIHints.py)。

与直接读元数据相比的差异(见 overview.md):API 返回空字符串作为回退,而prim.GetMetadata("uiHints").get("displayName")在未授权时返回None。此外,若需要访问自定义的(非标准)hint 键,才建议直接操作uiHints元数据。

:构建嵌套显示组

显示组可以无限嵌套。实际 DCC 场景中,常把一组相关属性组织成树状结构。以下是与 PrimHints.md 及测试资产相呼应的完整示例——hints.usda 展示了属性与 relationship 混用嵌套分组的真实写法:

#usda 1.0 def "HintsPrim" ( uiHints = { dictionary displayGroupsExpanded = { bool "a group" = 1 bool "a group:a nested group" = 0 } dictionary displayGroupsShownIf = { string "a group" = "x == 1" } string displayName = "a prim" bool hidden = 1 } ) { int attribute = 1 ( uiHints = { string displayGroup = "a group" string displayName = "an attr" bool hidden = 1 string shownIf = "x == 2" } ) rel relationship ( uiHints = { string displayGroup = "a group:a nested group" string displayName = "a rel" bool hidden = 1 string shownIf = "x == 3" } ) }

这里relationship归入 "a group" 下的子组 "a nested group"。测试test_PrimHintsFromAsset验证了 prim 级displayGroupsExpandeddisplayGroupsShownIf的读取(testUsdUIHints.py),test_RelationshipHintsFromAsset验证了 relationship 的displayGroup = 'a group:a nested group'shownIf = 'x == 3'(testUsdUIHints.py)。

嵌套组名中的:同时也是 USD 命名空间的通用分隔符,这意味着显示组天然具备"多级路径"语义,UI 可以据此渲染为折叠树。

用布尔表达式实现条件显隐

shownIf表达式基于SdfBooleanExpression(见 booleanExpression.h)。表达式文本在构造时被解析,解析出错会得到一个空表达式,且可通过GetParseError()获取错误信息。表达式支持的变量即为 prim 上的属性名,求值时取属性的解析值(resolved value)

支持的运算符

overview.md 完整列出:

运算符含义
==等于
!=不等于
<小于
<=小于或等于
>大于
>=大于或等于
&&逻辑与
\|\|逻辑或
!一元逻辑非
( )括号分组与优先级

这些二元/一元操作在SdfBooleanExpression中对应BinaryOperatorEqualToNotEqualToLessThanLessThanOrEqualToGreaterThanGreaterThanOrEqualToAndOr)与UnaryOperator::Not枚举,并可用MakeVariableMakeConstantMakeBinaryOpMakeUnaryOp在代码中程序化构造表达式(见 booleanExpression.h)。

实战示例:组合条件

以下示例(出自 overview.md)演示了属性级shownIf与 prim 级displayGroupsShownIf的联合使用:只有当 prim 的materialHardness解析值<= 2.0时,"Deformation parameters" 显示组整体出现;而组内的fractureAmount属性只有isFractured == true时才显示:

def "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 }

高级表达式:非与括号

利用一元!与括号可以表达更复杂的逻辑。原文档给出的例子:"!(status == "active" || level > 5)"仅在status不等于 "active"level小于等于 5 时显示该属性。

可见性判定规则(易踩坑)

再次强调最终判定:属性在 UI 中可见,当且仅当hidden为 false 且shownIf表达式求值为 true。二者是"或"的隐藏条件——任一命中即隐藏。这在 overview.md 中有明确说明,实现 UI 的消费方必须同时检查这两个 hint。

属性顺序与显示组的关系

PropertyHints 只决定属性归属哪个组,不决定属性在组内/组间的排列顺序。属性默认按字典序返回(Usd.Prim.GetProperties()),要控制 UI 中的顺序需使用 prim 级reorder properties指令或Usd.Prim.SetPropertyOrder()API(见 overview.md):

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" } ) }

DCC 工具在布局时应遵循:显示组按其被属性首次引用的位置放置,组内属性按 prim 的 property order 排序。注意,旧的displayGroupOrderprim 元数据字段已被视为弃用,不应再与 property order / display group 相关 UI hints 混用(见 overview.md)。

测试与验证

仓库在 testUsdUIHints.py 中为 PropertyHints 提供了完整的自动化验证,可作为消费方实现的参考基准:

  • test_RelationshipHintsFromAsset(L228-L243):验证 relationship 的displayGroup(含嵌套)与shownIf读取;
  • test_AttributeHintsFromAsset(L206-L226):验证 attribute 的displayGroup = 'a group'shownIf = 'x == 2'读取;
  • test_Fallbacks(L76-L122):验证未授权时的回退值(空字符串);
  • test_InvalidHints(L124-L156):验证无效 hints 对象上 set 操作抛RuntimeError
  • test_LegacyHintWrites(L346-L369):验证环境变量USDUI_WRITE_LEGACY_UI_HINTS开启时同步写入旧式字段。

配套测试资产 hints.usda 中还包括一个HintlessPrim,用于验证完全无提示属性的回退行为。

与其它 Hints 的关系与建议

  • ObjectHintsdisplayNamehidden)作用于所有对象,PropertyHints 示例中出现的这两个键实际属于 ObjectHints 层级,仅与 PropertyHints 写在同一个uiHints字典中;
  • PrimHintsdisplayGroupsExpandeddisplayGroupsShownIf)控制显示组整体的展开状态与显示条件,是 PropertyHints 的上层控制面;
  • AttributeHintsvalueLabelsvalueLabelsOrder)只适用于 attribute,为属性的取值提供可读标签(如把 1/2/3 显示为 "low"/"med"/"high"),见 AttributeHints.md;
  • relationship 没有专属 hints,直接复用 PropertyHints。

编写建议:优先使用UsdUI.PropertyHintsAPI 而非直接写元数据字典;不要再用已弃用的UsdProperty::SetDisplayGroup/GetDisplayGroup等旧字段(新写入统一进uiHints字典,旧字段仅在回退读取时兼容);将shownIf引用的属性值设计为显式的布尔/数值开关,避免表达式依赖不存在的属性导致求值结果不确定。

通过本文所述的displayGroup嵌套分组、shownIf布尔表达式以及属性顺序控制,你可以在 .usda 资产中为 DCC 工具提供结构清晰、条件自适应的属性面板呈现方案——这正是 PropertyHints 在 OpenUSD 生态中承担的核心职责。

【免费下载链接】OpenUSDUniversal Scene Description项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 3:23:03

vibe coding 实战指南:从工具选型到全局文档的完整工作流

我记得第一次正儿八经用 vibe coding 写完一个小工具&#xff0c;是在一个周六下午。当时想做一个本地书签管理器&#xff0c;需求用中文写了一大段扔给 AI&#xff0c;十分钟后它给我吐出了一整套前后端代码&#xff0c;能跑&#xff0c;界面还挺好看。说实话那个瞬间的体验是…

作者头像 李华
网站建设 2026/9/17 3:22:44

Java17中文文档HTML版制作:离线本地化与IDE接入实践

简介&#xff1a;Java17中文文档HTML版是一份完整的中文API参考手册&#xff0c;面向Java初学者和有经验的开发人员&#xff0c;覆盖Java17语法、标准库、类API及开发工具说明&#xff0c;可帮助读者了解新功能、改进与重要更新&#xff0c;并指导如何构建高效、可靠且安全的应…

作者头像 李华
网站建设 2026/9/17 3:22:23

LeetCode 904水果成篮:滑动窗口与哈希表实现最长子数组

1. 题目解读与本质提炼LeetCode 904这题&#xff0c;乍看是个“往篮子里装水果”的生活场景题&#xff0c;实际上是一个标准的滑动窗口问题。我第一次刷这题的时候&#xff0c;差点被题面绕晕&#xff0c;什么“两棵树”“两个篮子”“必须从左边开始连续采摘”……读完三遍才反…

作者头像 李华
网站建设 2026/9/17 3:21:21

亿级排行榜架构设计:Redis ZSet分片与冷热分离实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华