一、痛点引入
上回我们用 CATIndicationAgent 实现了"两点画直线",但工业场景里很少让你直接点屏幕。更多时候,领导的需求是这样:
"批量选中所有 M8 螺栓,自动统计数量。""点击某个面,自动提取这个面的所在零件和材料。"
手动点选容易漏、容易多选,还分不清你点的是几何元素还是产品节点。CATIA 的 CATPathElementAgent 就是专门解决这个问题的:它让用户在 3D 视图里点,脚本自动判断类型、提取上下文、拿到真正的业务对象。
二、核心思路
本篇文章在上一篇状态机命令的基础上,把鼠标点击代理从 CATIndicationAgent 升级为 CATPathElementAgent,实现"对象选择 类型过滤 路径解析 上下文提取"的完整链路。
用户点击 3D 视图
CATPathElementAgent 捕获选择
AddElementType 过滤允许的类型(Part/产品/几何)
GetValue() 拿到 CATPathElement 路径
解析路径 拿到选中对象 + 父级 Part/Product
输出信息或执行下一步业务你需要准备:
- 已配置好的 CAA RADE 开发环境(参考 08-15 文章)
- 一个继承
CATStateCommand的命令类(参考 08-17 文章) - 熟悉
BuildGraph()四步法
三、环境搭建(3分钟)
与上一篇相同,新建一个 CAA 模块,核心文件结构如下:
MySelectModule.m\
├── IdentityCard.h
├── Imakefile.mk
└── PublicInterfaces\
└── MySelectCmd.h
└── src\
└── MySelectCmd.cppImakefile.mk 关键 LINK_WITH:
LINK_WITH = $(WIZARD_LINK) \
CATApplicationFrame \
CATDialogEngine \
CATVisualization \
CATMathematics \
MecModInterfaces \
ProductStructureInterfaces \
ObjectSpecsModelerCATPathElementAgent 来自 CATDialogEngine 或 CATVisualization;解析出的几何对象需要 MecModInterfaces(Part 上下文)和 ProductStructureInterfaces(Product 上下文)。
四、核心代码
下面是一个完整命令类,实现:用户在 Part 或 Product 中点选一个对象,命令自动判断它是几何元素、零件还是产品节点,并打印名称与父级上下文。
// MySelectCmd.h
#include "CATStateCommand.h"
#include "CATPathElementAgent.h"
class MySelectCmd : public CATStateCommand
{
CATDeclareClass;
public:
MySelectCmd();
virtual ~MySelectCmd();
virtual void BuildGraph();
// 选择完成后的处理动作
void OnElementSelected(CATCommand * iCmd, CATNotification * iNotif,
CATCommandClientData iData);
private:
CATPathElementAgent * _pSelectAgent;
};// MySelectCmd.cpp
#include "MySelectCmd.h"
// 必要的头文件
#include "CATPathElement.h"
#include "CATUnicodeString.h"
#include "CATISpecObject.h" // 通用对象接口
#include "CATIPrtContainer.h" // Part 容器
#include "CATIPrtPart.h" // Part 对象
#include "CATIProduct.h" // Product 对象
#include "CATIMfBRepFeature.h" // BREP 几何特征(面/边/顶点)
#include "CATIContainer.h"
#include "CATDialogAgent.h"
CATImplementClass(MySelectCmd, Implementation, CATStateCommand, CATNull);
MySelectCmd::MySelectCmd()
: CATStateCommand(NULL, "MySelectCmd", CATCommandModeExclusive)
, _pSelectAgent(NULL)
{
}
MySelectCmd::~MySelectCmd()
{
if (_pSelectAgent) {
_pSelectAgent->RequestDelayedDestruction();
_pSelectAgent = NULL;
}
}
void MySelectCmd::BuildGraph()
{
// ========== 第1步:创建 CATPathElementAgent ==========
_pSelectAgent = new CATPathElementAgent("SelectAgent");
// 设置可选择类型:几何元素(BREP)、Part、Product
// 实际过滤行为取决于 AddElementType 的接口标识
_pSelectAgent->AddElementType(CATISpecObject::ClassId());
// 行为:预高亮 + 点击即接受
_pSelectAgent->SetBehavior(CATDlgEngWithPrevaluation
| CATDlgEngAcceptOnPrevaluate);
// ========== 第2步:创建状态并把代理加入状态 ==========
CATDialogState * pSelectState = GetInitialState("SelectState");
pSelectState->AddDialogAgent(_pSelectAgent);
// ========== 第3步:添加结束转移:选择完成后触发动作 ==========
AddTransition(pSelectState, NULL,
IsOutputSetCondition(_pSelectAgent),
Action((ActionMethod)&MySelectCmd::OnElementSelected));
}
void MySelectCmd::OnElementSelected(CATCommand * iCmd,
CATNotification * iNotif,
CATCommandClientData iData)
{
// ========== 第4步:获取选择路径 ==========
CATPathElement * pPath = _pSelectAgent->GetValue();
if (!pPath) return;
CATULONG32 pathSize = pPath->GetSizeOfPath();
if (pathSize == 0) return;
// 路径最后一个元素通常是用户直接点击的对象
CATBaseUnknown * pSelected = pPath->GetElement(pathSize);
if (!pSelected) return;
CATISpecObject_var spSelected = pSelected;
if (!!spSelected) {
CATUnicodeString name;
spSelected->GetName(&name);
printf("选中对象名称:%s\n", name.ConvertToChar());
}
// ========== 第5步:判断类型并提取上下文 ==========
// 尝试解释为 BREP 几何特征
CATIMfBRepFeature_var spBRep = pSelected;
if (!!spBRep) {
printf("类型判断:BREP 几何元素(面/边/顶点)\n");
// 从路径中向上查找所在 Part
for (CATULONG32 i = pathSize; i >= 1; --i) {
CATBaseUnknown * pElem = pPath->GetElement(i);
CATIPrtPart_var spPart = pElem;
if (!!spPart) {
CATUnicodeString partName;
spPart->GetName(&partName);
printf("所属 Part:%s\n", partName.ConvertToChar());
break;
}
}
}
// 尝试解释为 Part 级别对象
CATIPrtPart_var spPrtPart = pSelected;
if (!!spPrtPart) {
printf("类型判断:Part 文档根节点\n");
}
// 尝试解释为 Product 节点
CATIProduct_var spProduct = pSelected;
if (!!spProduct) {
CATUnicodeString pn;
spProduct->GetPartNumber(&pn);
printf("类型判断:Product 节点,件号:%s\n", pn.ConvertToChar());
// 获取父级 Product
CATIProduct_var spParent = spProduct->GetFather();
if (!!spParent) {
CATUnicodeString parentPn;
spParent->GetPartNumber(&parentPn);
printf("父级 Product 件号:%s\n", parentPn.ConvertToChar());
}
}
// 选择完成后,重置代理以便连续选择
_pSelectAgent->InitializeAcquisition();
}代码解析:
CATPathElementAgent:专门的对象选择代理,返回一条"选择路径",路径上的每个节点代表从根到选中对象的层级。AddElementType(CATISpecObject::ClassId()):限定可选对象类型。实际过滤精度与传入的接口 ID 有关,可组合多个AddElementType实现多选。GetValue()/GetSizeOfPath()/GetElement(index):解析路径的核心 API,index 通常从 1 开始。CATIMfBRepFeature:BREP 特征接口,用于判断用户选中的是面、边还是顶点。InitializeAcquisition():重置代理,使命令可以连续接收下一次选择。
五、效果对比
| 对比项 | 传统手动选择 | CAA 对象选择代理 |
|---|---|---|
| 耗时 | 每次人工判断类型、逐层翻结构树 | 点击即识别,毫秒级返回 |
| 出错率 | 高(容易选到引用而非实例) | 极低(路径精确到实例) |
| 可扩展性 | 无法批量处理 | 可作为后续自动化命令的第一步 |
| 上下文获取 | 需手动打开父级查看 | 代码直接解析路径拿到 Part/Product |
六、进阶用法
进阶 1:限定只选特定类型
如果你只想让用户选择平面或产品节点,可以传入更精确的接口 ID:
// 只选平面(CATIPlane 接口,需包含对应头文件)
_pSelectAgent->AddElementType(CATIPlane::ClassId());
// 只选 Product 节点
_pSelectAgent->AddElementType(CATIProduct::ClassId());进阶 2:多选模式 + 结果收集
通过 SetBehavior 开启多选,循环调用 GetValue() 直到用户按 Esc:
_pSelectAgent->SetBehavior(CATDlgEngWithPrevaluation
| CATDlgEngAcceptOnPrevaluate
| CATDlgEngMultiAcquisition);
// 在 OnElementSelected 中收集结果,判断结束条件
if (_pSelectAgent->IsEndPreselected()) {
// 用户结束多选,统一处理已收集的对象
}关注回复「CAA选择」获取完整源码
下一篇:CAA C++ 对话框与状态命令联动,手把手教你做带输入框的交互命令
_栏目:开发实战 | 发布日期:2026-08-19 | 字数:约1180字_